From fc46c4874a394a30504aa100b88a7d3ac6eb8b01 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 8 Jun 2025 12:49:50 +0200 Subject: [PATCH] fix(web): refresh navbar on login and logout --- CHANGELOG.md | 5 ++++ otter/src/server/web/mod.rs | 38 ++++++++++++++---------- otter/src/web/page.rs | 9 ++++++ otter/src/web/templates/base.html | 9 +++++- otter/src/web/templates/views/index.html | 2 -- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9129687..d61481f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/otter) +### Added + +* Web UI + * Login/logout button + ## [0.1.0](https://git.rustybever.be/Chewing_Bever/otter/src/tag/0.1.0) ### Added diff --git a/otter/src/server/web/mod.rs b/otter/src/server/web/mod.rs index ca6ce6f..2099ab4 100644 --- a/otter/src/server/web/mod.rs +++ b/otter/src/server/web/mod.rs @@ -1,7 +1,7 @@ use axum::{ Form, RequestExt, Router, extract::{Request, State}, - http::HeaderMap, + http::{HeaderMap, HeaderName, HeaderValue, header}, middleware::{self, Next}, response::{IntoResponse, Redirect, Response}, routing::{get, post}, @@ -23,18 +23,27 @@ const SESSION_ID_COOKIE: &str = "sessionid"; pub fn router(ctx: Context) -> Router { Router::new() .route("/", get(get_index)) - .layer(middleware::from_fn_with_state( - ctx.clone(), - auth_web_middleware, - )) + // .layer(middleware::from_fn_with_state( + // ctx.clone(), + // auth_web_middleware, + // )) // Login route needs to be handled differently, as the middleware turns it into a redirect // loop .route("/login", get(get_login).post(post_login)) .route("/logout", post(post_logout)) } -async fn get_index(State(ctx): State, headers: HeaderMap) -> TemplateResponse> { - View::Index.page(&headers).response(&ctx.tera) +async fn get_index( + State(ctx): State, + headers: HeaderMap, + jar: CookieJar, +) -> AppResult>> { + let authenticated = extract_session(ctx.clone(), &jar).await?.is_some(); + + Ok(View::Index + .page(&headers) + .authenticated(authenticated) + .response(&ctx.tera)) } async fn get_login(State(ctx): State, headers: HeaderMap, jar: CookieJar) -> Response { @@ -79,15 +88,16 @@ async fn post_login( .unwrap() { Ok(session) => Ok(( - jar.add( + // Redirect forces htmx to reload the full page, refreshing the navbar + [("HX-Redirect", "/")], + (jar.add( Cookie::build((SESSION_ID_COOKIE, session.id.to_string())) .secure(true) .same_site(cookie::SameSite::Lax) .http_only(true) .path("/") .max_age(Duration::days(365)), - ), - Redirect::to("/"), + )), ) .into_response()), Err(AuthErr::UnknownUser | AuthErr::InvalidPassword) => { @@ -98,15 +108,13 @@ async fn post_login( } /// Log out the user by simply removing the session -async fn post_logout( - State(ctx): State, - jar: CookieJar, -) -> AppResult<(CookieJar, Redirect)> { +async fn post_logout(State(ctx): State, jar: CookieJar) -> AppResult { if let Some(session) = extract_session(ctx.clone(), &jar).await? { ctx.store.remove_session(session.id)?; } - Ok((jar.remove(SESSION_ID_COOKIE), Redirect::to("/"))) + // Redirect forces htmx to reload the full page, refreshing the navbar + Ok(([("HX-Redirect", "/")], jar.remove(SESSION_ID_COOKIE))) } async fn extract_session(ctx: Context, jar: &CookieJar) -> AppResult> { diff --git a/otter/src/web/page.rs b/otter/src/web/page.rs index 0ba40c5..d7c0b4c 100644 --- a/otter/src/web/page.rs +++ b/otter/src/web/page.rs @@ -10,6 +10,7 @@ const HX_HISTORY_RESTORE_HEADER: &str = "HX-History-Restore-Request"; pub struct Page { template: T, wrap_with_base: bool, + authenticated: bool, } impl Template for Page { @@ -23,6 +24,7 @@ impl Template for Page { if self.wrap_with_base { let mut ctx = tera::Context::new(); ctx.insert("inner", &inner); + ctx.insert("authenticated", &self.authenticated); tera.render(super::BASE_TEMPLATE, &ctx) } else { @@ -36,6 +38,7 @@ impl Page { Self { template, wrap_with_base: false, + authenticated: false, } } @@ -50,4 +53,10 @@ impl Page { self } + + pub fn authenticated(mut self, authenticated: bool) -> Self { + self.authenticated = authenticated; + + self + } } diff --git a/otter/src/web/templates/base.html b/otter/src/web/templates/base.html index 68e958f..bf13d16 100644 --- a/otter/src/web/templates/base.html +++ b/otter/src/web/templates/base.html @@ -16,10 +16,17 @@ a:hover {
diff --git a/otter/src/web/templates/views/index.html b/otter/src/web/templates/views/index.html index 7116f34..6fc5b55 100644 --- a/otter/src/web/templates/views/index.html +++ b/otter/src/web/templates/views/index.html @@ -1,5 +1,3 @@

Otter

Otter is a self-hostable Gpodder implementation. - -If you're seeing this, you're logged in.