use axum::{ extract::{Form, Path, State}, http::{header, StatusCode}, response::{Html, IntoResponse, Redirect, Response}, }; use serde::Deserialize; use tera::Context; use super::handlers::{render, UiError}; use crate::api::AppState; use crate::session::{extract_cookie, COOKIE_NAME}; pub async fn login_page( State(state): State, Path(slug): Path, ) -> Result, UiError> { let tera = state.tera.as_ref().ok_or(UiError::NotConfigured)?; let mut ctx = Context::new(); ctx.insert("slug", &slug); ctx.insert("error", &false); ctx.insert("base_url", &format!("/ui/{slug}")); render(tera, "pages/login.html", &ctx).await } #[derive(Deserialize)] pub struct LoginForm { pub key: String, } pub async fn login_submit( State(state): State, Path(slug): Path, Form(form): Form, ) -> Response { let Some(ref registry) = state.registry else { return Redirect::to("/ui/").into_response(); }; let Some(ctx) = registry.get(&slug) else { return StatusCode::NOT_FOUND.into_response(); }; match ctx.verify_key(&form.key) { Some(role) => { let token = state.sessions.create(slug.clone(), role); let cookie = format!( "{}={}; Path=/ui/; HttpOnly; SameSite=Strict; Max-Age={}", COOKIE_NAME, token, 30 * 24 * 3600, ); ( [(header::SET_COOKIE, cookie)], Redirect::to(&format!("/ui/{slug}/")), ) .into_response() } None => { let tera = match state.tera.as_ref() { Some(t) => t, None => return StatusCode::INTERNAL_SERVER_ERROR.into_response(), }; let mut tctx = Context::new(); tctx.insert("slug", &slug); tctx.insert("error", &true); tctx.insert("base_url", &format!("/ui/{slug}")); match render(tera, "pages/login.html", &tctx).await { Ok(html) => html.into_response(), Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(), } } } } pub async fn logout( State(state): State, Path(slug): Path, request: axum::extract::Request, ) -> Response { let cookie_str = request .headers() .get(header::COOKIE) .and_then(|v| v.to_str().ok()) .unwrap_or(""); if let Some(token) = extract_cookie(cookie_str, COOKIE_NAME) { state.sessions.revoke(&token); } let clear = format!( "{}=; Path=/ui/; HttpOnly; SameSite=Strict; Max-Age=0", COOKIE_NAME ); ( [(header::SET_COOKIE, clear)], Redirect::to(&format!("/ui/{slug}/login")), ) .into_response() }