A focused Docker Compose management web application.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: logout page

Brooke 464aabd4 806eb19d

+66 -9
+14 -7
packages/node/src/api/auth.rs
··· 18 18 use sqlx::{SqlitePool, prelude::FromRow}; 19 19 use uuid::Uuid; 20 20 21 - use crate::{api::response::LuminaryResponse, eyre_fmt, obtain}; 21 + use crate::{api::response::LuminaryResponse, eyre_fmt, get_user, obtain}; 22 22 23 23 /// Returns a router containing all authentication-related endpoints. 24 24 pub fn router() -> Router { ··· 31 31 .post(reset_password), 32 32 ) 33 33 .push( 34 - Router::with_hoop(protected).push( 35 - Router::with_path("users") 36 - .get(get_users) 37 - .post(create_user) 38 - .push(Router::with_path("{user}").delete(delete_user)), 39 - ), 34 + Router::with_hoop(protected) 35 + .push(Router::with_path("self").get(get_current_user)) 36 + .push( 37 + Router::with_path("users") 38 + .get(get_users) 39 + .post(create_user) 40 + .push(Router::with_path("{user}").delete(delete_user)), 41 + ), 40 42 ); 43 + } 44 + 45 + #[endpoint] 46 + async fn get_current_user(depot: &mut Depot) -> LuminaryResponse<LuminaryUser> { 47 + return Ok(get_user!(depot).clone().into()); 41 48 } 42 49 43 50 /// Reads username and password from the request body, and returns an authentication token if the credentials are valid.
+1 -1
packages/node/src/util.rs
··· 14 14 macro_rules! get_user { 15 15 ($depot:expr) => { 16 16 $depot 17 - .get::<crate::auth::LuminaryUser>("user") 17 + .get::<crate::api::auth::LuminaryUser>("user") 18 18 .ok() 19 19 .and_then(|v| Some(v)) 20 20 .expect("User can not be obtained from a unprotected endpoint.")
+10
packages/panel/src/lib/api/index.ts
··· 120 120 const response = await client.POST("/api/auth/login", { body: credentials }); 121 121 putToken(response.data!); 122 122 } 123 + 124 + /** 125 + * Logs the user out by invalidating the token on the backend and removing it from localStorage. 126 + */ 127 + export async function logout() { 128 + await client.POST("/api/auth/logout"); 129 + putToken(null); 130 + 131 + await goto("/login"); 132 + }
+31 -1
packages/panel/src/routes/(authenticated)/settings/+page.svelte
··· 1 - <h1>Hello World</h1> 1 + <script lang="ts"> 2 + import { faArrowRightFromBracket, faGears, faUser } from "@fortawesome/free-solid-svg-icons"; 3 + import PromiseButton from "$lib/component/PromiseButton.svelte"; 4 + import Tabs from "$lib/component/Tabs.svelte"; 5 + import Fa from "svelte-fa"; 6 + import { api } from "$lib"; 7 + 8 + let { data } = $props(); 9 + </script> 10 + 11 + <div class="flexc gap-10"> 12 + <!-- Title Bar --> 13 + <h1 class="flexr gap-10 center fit"> 14 + <Fa icon={faUser} size="lg" /> 15 + <div style="display: inline-block;"> 16 + <div style="font-size: 22px;">{data.user.username}</div> 17 + <PromiseButton style="a" fit onclick={api.logout}> 18 + <div class="a flexr gap-5" style="color: var(--red)"> 19 + <Fa icon={faArrowRightFromBracket} /> 20 + Log out 21 + </div> 22 + </PromiseButton> 23 + </div> 24 + </h1> 25 + 26 + <Tabs tabs={[{ icon: faGears, label: "application", content: appearance }]} /> 27 + </div> 28 + 29 + {#snippet appearance()} 30 + <div class="flexc gap-10">COMING SOON</div> 31 + {/snippet}
+10
packages/panel/src/routes/(authenticated)/settings/+page.ts
··· 1 + import type { PageLoad } from "../$types"; 2 + import { api } from "$lib"; 3 + 4 + export const load: PageLoad = async () => { 5 + const response = await api.client.GET("/api/auth/self"); 6 + 7 + return { 8 + user: response.data!, 9 + }; 10 + };