use actix_session::CookieSession; use actix_web::{http::header::LOCATION, HttpResponse}; use sled::Db; use std::sync::Arc; use uuid::Uuid; mod extractors; mod forms; mod store; pub use { extractors::{AcceptedCookies, Auth, Authenticated}, forms::{ cookies::{cookies, cookies_page, CookiesArgs, CookiesPageArgs, CookiesState}, delete_user::{ delete_user, delete_user_page, DeleteUserArgs, DeleteUserPageArgs, DeleteUserState, }, login::{login, login_page, LoginArgs, LoginPageArgs, LoginState}, logout::{logout, logout_page, LogoutArgs, LogoutPageArgs, LogoutState}, register::{register, register_page, RegisterArgs, RegisterPageArgs, RegisterState}, update_password::{ update_password, update_password_page, UpdatePasswordArgs, UpdatePasswordPageArgs, UpdatePasswordState, }, update_username::{ update_username, update_username_page, UpdateUsernameArgs, UpdateUsernamePageArgs, UpdateUsernameState, }, }, store::User, }; pub trait Pages { fn home_path(&self) -> String; fn not_found_path(&self) -> String; fn internal_server_error_path(&self) -> String; fn cookies_path(&self) -> String; fn login_path(&self) -> String; fn register_path(&self) -> String; fn logout_path(&self) -> String; fn accounts_path(&self) -> String; fn update_username_path(&self) -> String; fn update_password_path(&self) -> String; fn delete_user_path(&self) -> String; } #[derive(Clone)] pub struct Config { pub key: Vec, pub https: bool, pub pages: Arc, } #[derive(Clone)] pub struct State { user_store: store::UserStore, db: Db, pages: Arc, } impl State { pub async fn by_username(&self, username: String) -> Result, Error> { Ok(self.user_store.user_by_username(username).await?) } pub async fn suspend(&self, user_id: Uuid) -> Result<(), Error> { Ok(self.user_store.suspend(user_id).await?) } } pub fn state(config: &Config, db: Db) -> Result { Ok(State { user_store: store::UserStore::build(&db)?, db, pages: Arc::clone(&config.pages), }) } pub fn cookie_middleware(config: &Config) -> CookieSession { CookieSession::private(&config.key) .name("accounts_scope") .secure(config.https) } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("{0}")] Session(#[from] extractors::SessionError), #[error("{0}")] Store(#[from] store::StoreError), #[error("Panic in blocking operation")] Blocking, } impl std::fmt::Debug for Config { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("Config") .field("key", &"Key") .field("https", &self.https) .field("pages", &"Box") .finish() } } impl std::fmt::Debug for State { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("State") .field("user_store", &"UserStore") .field("db", &"Db") .finish() } } fn to_cookie_page(state: &State) -> HttpResponse { HttpResponse::SeeOther() .header(LOCATION, state.pages.cookies_path()) .finish() } fn to_register(state: &State) -> HttpResponse { HttpResponse::SeeOther() .header(LOCATION, state.pages.register_path()) .finish() } fn to_account_page(state: &State) -> HttpResponse { HttpResponse::SeeOther() .header(LOCATION, state.pages.accounts_path()) .finish() } fn to_home(state: &State) -> HttpResponse { HttpResponse::SeeOther() .header(LOCATION, state.pages.home_path()) .finish() } impl From> for Error where Error: From, E: std::fmt::Debug, { fn from(e: actix_web::error::BlockingError) -> Self { match e { actix_web::error::BlockingError::Error(e) => e.into(), _ => Error::Blocking, } } }