#[cfg(feature = "server")] use crate::util::surrealdb::{thing_to_string, DB}; use dioxus::prelude::*; use serde::{Deserialize, Serialize}; use super::user::User; #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] pub struct Session { #[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))] id: String, #[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))] user_id: String, expires: i64, token: String, } #[cfg(feature = "server")] impl Session { pub async fn new(user_id: String) -> Result { let mut res = DB .query("CREATE ONLY session SET user = type::thing('user', $user_id) RETURN id, user as user_id, time::unix(expires) as expires, token;") .bind(("user_id", user_id)) .await?; res = res.check()?; let session: Option = res.take(0)?; match session { Some(s) => Ok(s), None => Err(crate::Error::NoDocument), } } pub fn set_cookie(&self) -> Result<(), crate::Error> { use axum::http::{header, HeaderValue}; use axum_extra::extract::cookie::{Cookie, Expiration, SameSite}; use dioxus::prelude::server_context; use time::OffsetDateTime; let timestamp = OffsetDateTime::from_unix_timestamp(self.expires)?; let expiration = Expiration::from(timestamp); let mut cookie = Cookie::build(("session_token", &self.token)) .expires(expiration) .build(); cookie.set_same_site(SameSite::Strict); server_context() .response_parts_mut() .unwrap() .headers .insert( header::SET_COOKIE, HeaderValue::from_str(&cookie.to_string())?, ); Ok(()) } pub async fn fetch_user_from_token(session_token: String) -> Result { let mut res = DB .query( "SELECT user as id, user.email as email, user.admin as admin FROM session WHERE token = $session_token", ) .bind(("session_token", session_token)) .await?; res = res.check()?; let user: Option = res.take(0)?; match user { Some(u) => Ok(u), None => Err(crate::Error::NoDocument), } } pub async fn get_token_from_cookie() -> Result { use axum_extra::extract::CookieJar; let jar: CookieJar = extract().await.unwrap_or_default(); match jar.get("session_token") { Some(s) => Ok(s.value().to_string()), None => Err(crate::Error::NoSessionCookie), } } pub async fn delete_cookie() -> Result<(), crate::Error> { use axum::http::{header, HeaderValue}; use axum_extra::extract::cookie::{Cookie, SameSite}; use dioxus::prelude::server_context; use time::Duration; let mut cookie = Cookie::build(("session_token", "")) .max_age(Duration::seconds(1)) .build(); cookie.set_same_site(SameSite::Strict); server_context() .response_parts_mut() .unwrap() .headers .insert( header::SET_COOKIE, HeaderValue::from_str(&cookie.to_string())?, ); Ok(()) } pub async fn delete_session(user_id: String) -> Result<(), crate::Error> { let res = DB .query("DELETE ONLY session WHERE user = type::thing('user', $user_id)") .bind(("user_id", user_id)) .await?; res.check()?; Ok(()) } // Fetches the current user from cookie headers pub async fn fetch_current_user() -> Result { let session_token = Self::get_token_from_cookie().await?; let user = Self::fetch_user_from_token(session_token).await?; Ok(user) } }