From 493746b14c1251a45b061d2e3edd9160c929d2b9 Mon Sep 17 00:00:00 2001 From: benj Date: Fri, 10 Apr 2026 11:13:34 +0800 Subject: a basic ui and landing web interface for tidyindex.com --- web/ui/src/routes/+layout.server.ts | 7 + web/ui/src/routes/+layout.svelte | 9 + web/ui/src/routes/+page.server.ts | 20 ++ web/ui/src/routes/auth/callback/+server.ts | 14 ++ web/ui/src/routes/auth/logout/+server.ts | 13 ++ web/ui/src/routes/dashboard/+layout.server.ts | 12 ++ web/ui/src/routes/dashboard/+layout.svelte | 90 ++++++++ web/ui/src/routes/dashboard/+page.server.ts | 7 + .../src/routes/dashboard/account/+page.server.ts | 107 +++++++++ web/ui/src/routes/dashboard/account/+page.svelte | 238 +++++++++++++++++++++ web/ui/src/routes/dashboard/keys/+page.server.ts | 68 ++++++ web/ui/src/routes/dashboard/keys/+page.svelte | 220 +++++++++++++++++++ web/ui/src/routes/dashboard/usage/+page.server.ts | 13 ++ web/ui/src/routes/dashboard/usage/+page.svelte | 152 +++++++++++++ 14 files changed, 970 insertions(+) create mode 100644 web/ui/src/routes/+layout.server.ts create mode 100644 web/ui/src/routes/+layout.svelte create mode 100644 web/ui/src/routes/+page.server.ts create mode 100644 web/ui/src/routes/auth/callback/+server.ts create mode 100644 web/ui/src/routes/auth/logout/+server.ts create mode 100644 web/ui/src/routes/dashboard/+layout.server.ts create mode 100644 web/ui/src/routes/dashboard/+layout.svelte create mode 100644 web/ui/src/routes/dashboard/+page.server.ts create mode 100644 web/ui/src/routes/dashboard/account/+page.server.ts create mode 100644 web/ui/src/routes/dashboard/account/+page.svelte create mode 100644 web/ui/src/routes/dashboard/keys/+page.server.ts create mode 100644 web/ui/src/routes/dashboard/keys/+page.svelte create mode 100644 web/ui/src/routes/dashboard/usage/+page.server.ts create mode 100644 web/ui/src/routes/dashboard/usage/+page.svelte (limited to 'web/ui/src/routes') diff --git a/web/ui/src/routes/+layout.server.ts b/web/ui/src/routes/+layout.server.ts new file mode 100644 index 0000000..37c08a0 --- /dev/null +++ b/web/ui/src/routes/+layout.server.ts @@ -0,0 +1,7 @@ +import type { LayoutServerLoad } from './$types'; + +export const load: LayoutServerLoad = async ({ locals }) => { + return { + account: locals.account + }; +}; diff --git a/web/ui/src/routes/+layout.svelte b/web/ui/src/routes/+layout.svelte new file mode 100644 index 0000000..ddd7c4c --- /dev/null +++ b/web/ui/src/routes/+layout.svelte @@ -0,0 +1,9 @@ + + + +{@render children()} diff --git a/web/ui/src/routes/+page.server.ts b/web/ui/src/routes/+page.server.ts new file mode 100644 index 0000000..2daf03d --- /dev/null +++ b/web/ui/src/routes/+page.server.ts @@ -0,0 +1,20 @@ +import { redirect } from '@sveltejs/kit'; + +import { createAnonymousAccount, createSession } from '$lib/server/auth'; +import type { PageServerLoad } from './$types'; + +/** + * The root route is just a bouncer: if you already have a session, + * go to the dashboard; if not, we mint an anonymous account on the + * spot, set a session cookie, and go to the dashboard. + * + * There is intentionally no sign-in page. Users can add an email on + * the Account tab later if they want a way to recover their keys. + */ +export const load: PageServerLoad = async ({ locals, cookies }) => { + if (!locals.account) { + const account = createAnonymousAccount(); + createSession(cookies, account.id); + } + throw redirect(303, '/dashboard'); +}; diff --git a/web/ui/src/routes/auth/callback/+server.ts b/web/ui/src/routes/auth/callback/+server.ts new file mode 100644 index 0000000..da8181b --- /dev/null +++ b/web/ui/src/routes/auth/callback/+server.ts @@ -0,0 +1,14 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit'; + +import { consumeMagicLink, createSession } from '$lib/server/auth'; + +export const GET: RequestHandler = async ({ url, cookies }) => { + const token = url.searchParams.get('token'); + if (!token) throw redirect(303, '/?error=missing-token'); + + const account = consumeMagicLink(token); + if (!account) throw redirect(303, '/?error=invalid-token'); + + createSession(cookies, account.id); + throw redirect(303, '/dashboard'); +}; diff --git a/web/ui/src/routes/auth/logout/+server.ts b/web/ui/src/routes/auth/logout/+server.ts new file mode 100644 index 0000000..b17d1d5 --- /dev/null +++ b/web/ui/src/routes/auth/logout/+server.ts @@ -0,0 +1,13 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit'; + +import { clearSession } from '$lib/server/auth'; + +export const POST: RequestHandler = async ({ cookies }) => { + clearSession(cookies); + throw redirect(303, '/'); +}; + +export const GET: RequestHandler = async ({ cookies }) => { + clearSession(cookies); + throw redirect(303, '/'); +}; diff --git a/web/ui/src/routes/dashboard/+layout.server.ts b/web/ui/src/routes/dashboard/+layout.server.ts new file mode 100644 index 0000000..8eebc76 --- /dev/null +++ b/web/ui/src/routes/dashboard/+layout.server.ts @@ -0,0 +1,12 @@ +import { redirect } from '@sveltejs/kit'; + +import type { LayoutServerLoad } from './$types'; + +export const load: LayoutServerLoad = async ({ locals }) => { + if (!locals.account) { + throw redirect(303, '/'); + } + return { + account: locals.account + }; +}; diff --git a/web/ui/src/routes/dashboard/+layout.svelte b/web/ui/src/routes/dashboard/+layout.svelte new file mode 100644 index 0000000..9137c75 --- /dev/null +++ b/web/ui/src/routes/dashboard/+layout.svelte @@ -0,0 +1,90 @@ + + + + Tidy Index — Dashboard + + +
+
+ + + Tidy Index + + +
+ + plan: {planInfo.name.toLowerCase()} + + {#if account?.email} + {account.email} + {:else if account?.pending_email} + + pending · {account.pending_email} + + {:else} + anonymous + {/if} +
+
+
+ +
+ {#if !account?.email && !account?.pending_email} + + {:else if account?.pending_email && !account?.email} + + {/if} + + +
+ +
+
+ {@render children()} +
+
+ +