From 54cafaa1da0a93fe12f6c5863725abe3ce90934f Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Thu, 23 Oct 2025 03:09:31 +0200 Subject: [PATCH] fix csrf_token and GuildSelect --- src/lib/AsideCard.svelte | 2 +- src/lib/GuildSelect.svelte | 31 ++++++++++++++++----- src/lib/backend.ts | 57 ++++++++++++++++++++++++++++++-------- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/lib/AsideCard.svelte b/src/lib/AsideCard.svelte index 4e7fb2e..4e6029c 100644 --- a/src/lib/AsideCard.svelte +++ b/src/lib/AsideCard.svelte @@ -25,7 +25,7 @@ let { children, title = null } = $props(); position: relative; font: inherit; - a { + :global(a) { color: inherit; text-decoration: underline; } diff --git a/src/lib/GuildSelect.svelte b/src/lib/GuildSelect.svelte index 12c07c4..ddf41ae 100644 --- a/src/lib/GuildSelect.svelte +++ b/src/lib/GuildSelect.svelte @@ -1,29 +1,46 @@
@@ -46,7 +63,7 @@ let suggestions: GuildEntry[] = $state([]); border-bottom: 1px solid var(--border); } - :not(:focus) + ul { + :not(:focus) + ul:not(:active) { display: none; } diff --git a/src/lib/backend.ts b/src/lib/backend.ts index e36d983..92cbfdb 100644 --- a/src/lib/backend.ts +++ b/src/lib/backend.ts @@ -44,6 +44,32 @@ export type ServerHealth = { }; +// caches an oath (CSRF token) +class OathKeeper { + lastOath : number; + csrfToken: string | null; + + constructor () { + this.lastOath = 0; + this.csrfToken = null; + } + + set (tok: string) { + this.lastOath = +new Date; + this.csrfToken = tok; + } + + get () { + if (this.lastOath === 0 || +new Date() - this.lastOath > 1800000) { + // 30 minutes decay + return null; + } + return this.csrfToken; + } +} + +const oathKeeper = new OathKeeper(); + export class Backend { static ENDPOINT_BASE = '/v1'; @@ -60,33 +86,42 @@ export class Backend { } class EventBackend extends Backend { - event: any; + event: any | null; - constructor (event: any) { + constructor (event: any | null) { super(); this.event = event; } async oath (): Promise { - const resp = await this.fetch("oath"); - if (resp.status !== 200) { - throw new Error(); + let csrfToken = oathKeeper.get(); + if (!csrfToken) { + const resp = await this.fetch("oath"); + if (resp.status !== 200) { + throw new Error(); + } + const respJ = await resp.json(); + csrfToken = respJ.csrf_token; + } + if (!csrfToken) { + console.warn("csrf_token is null"); + } + else { + oathKeeper.set(csrfToken); } - const respJ = await resp.json(); - const { csrfToken } = respJ; return new CsrfEventBackend(this.event, csrfToken); } async fetch(url: string, options?: RequestInit): Promise { console.debug(`fetch ${Backend.ENDPOINT_BASE}/${url.replace(/^\/+/, '')}`); - return await this.event.fetch(`${Backend.ENDPOINT_BASE}/${url.replace(/^\/+/, '')}`, options); + return await (this.event?.fetch||fetch)(`${Backend.ENDPOINT_BASE}/${url.replace(/^\/+/, '')}`, options); } } class CsrfEventBackend extends EventBackend { - csrfToken: string; + csrfToken: string | null; - constructor(event:EventBackend, csrfToken: string) { + constructor(event: EventBackend | null, csrfToken: string | null) { super(event); this.csrfToken = csrfToken; } @@ -97,7 +132,7 @@ class CsrfEventBackend extends EventBackend { body: JSON.stringify(data), headers: { 'content-type': 'application/json', - 'x-csrftoken': this.csrfToken + 'x-csrftoken': this.csrfToken || "" } }); }