use actix_session::{ config::CookieContentSecurity, storage::CookieSessionStore, SessionMiddleware, }; use actix_web::{ cookie::{Key, SameSite}, dev::ServiceResponse, http::{Method, StatusCode}, middleware::{ErrorHandlerResponse, ErrorHandlers}, web::{self, Data, Path}, HttpResponse, Responder, }; use askama::Template; use assets::{Assets, EmbedService}; use routes::login::{route_get_login, route_post_login}; use rustical_store::{ auth::{AuthenticationMiddleware, AuthenticationProvider, User}, Calendar, CalendarStore, }; use std::sync::Arc; mod assets; mod config; mod routes; pub use config::FrontendConfig; #[derive(Template)] #[template(path = "pages/user.html")] struct UserPage { pub user_id: String, pub calendars: Vec, } async fn route_user( path: Path, store: Data, user: User, ) -> impl Responder { let user_id = path.into_inner(); UserPage { calendars: store.get_calendars(&user.id).await.unwrap(), user_id: user.id, } } #[derive(Template)] #[template(path = "pages/calendar.html")] struct CalendarPage { owner: String, calendar: Calendar, } async fn route_calendar( path: Path<(String, String)>, store: Data, _user: User, ) -> impl Responder { let (owner, cal_id) = path.into_inner(); CalendarPage { owner: owner.to_owned(), calendar: store.get_calendar(&owner, &cal_id).await.unwrap(), } } fn unauthorized_handler(res: ServiceResponse) -> actix_web::Result> { let (req, _) = res.into_parts(); let login_url = req.url_for_static("frontend_login").unwrap().to_string(); // let response = Redirect::to(login_url).respond_to(&req); let response = HttpResponse::Unauthorized().body(format!( r#" Unauthorized, redirecting to login page "# )); let res = ServiceResponse::new(req, response) .map_into_boxed_body() .map_into_right_body(); Ok(ErrorHandlerResponse::Response(res)) } pub fn configure_frontend( cfg: &mut web::ServiceConfig, auth_provider: Arc, store: Arc, frontend_config: FrontendConfig, ) { cfg.service( web::scope("") .wrap(ErrorHandlers::new().handler(StatusCode::UNAUTHORIZED, unauthorized_handler)) .wrap(AuthenticationMiddleware::new(auth_provider.clone())) .wrap( SessionMiddleware::builder( CookieSessionStore::default(), Key::from(&frontend_config.secret_key), ) .cookie_secure(true) .cookie_same_site(SameSite::Strict) .cookie_content_security(CookieContentSecurity::Private) .build(), ) .app_data(Data::from(auth_provider)) .app_data(Data::from(store.clone())) .service(EmbedService::::new("/assets".to_owned())) .service( web::resource("/user/{user}").route(web::method(Method::GET).to(route_user::)), ) .service( web::resource("/user/{user}/{calendar}") .route(web::method(Method::GET).to(route_calendar::)), ) .service( web::resource("/login") .name("frontend_login") .route(web::method(Method::GET).to(route_get_login)) .route(web::method(Method::POST).to(route_post_login::)), ), ); }