From edfc91468b195b045f41ef80011947cd11ef28f5 Mon Sep 17 00:00:00 2001 From: Sanju Sivalingam Date: Tue, 17 Feb 2026 14:45:44 +0530 Subject: [PATCH] feat: add LLM provider settings page Co-Authored-By: Claude Opus 4.6 --- web/src/lib/api/settings.remote.ts | 54 ++++++++++++++++ web/src/lib/schema/settings.ts | 7 +++ .../routes/dashboard/settings/+page.svelte | 62 +++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 web/src/lib/api/settings.remote.ts create mode 100644 web/src/lib/schema/settings.ts create mode 100644 web/src/routes/dashboard/settings/+page.svelte diff --git a/web/src/lib/api/settings.remote.ts b/web/src/lib/api/settings.remote.ts new file mode 100644 index 0000000..cc02b81 --- /dev/null +++ b/web/src/lib/api/settings.remote.ts @@ -0,0 +1,54 @@ +import { form, query, getRequestEvent } from '$app/server'; +import { db } from '$lib/server/db'; +import { llmConfig } from '$lib/server/db/schema'; +import { eq } from 'drizzle-orm'; +import { llmConfigSchema } from '$lib/schema/settings'; + +export const getConfig = query(async () => { + const { locals } = getRequestEvent(); + if (!locals.user) return null; + + const config = await db + .select() + .from(llmConfig) + .where(eq(llmConfig.userId, locals.user.id)) + .limit(1); + + if (config.length === 0) return null; + + // mask the API key for display + return { + ...config[0], + apiKey: config[0].apiKey.slice(0, 8) + '...' + config[0].apiKey.slice(-4) + }; +}); + +export const updateConfig = form(llmConfigSchema, async (data) => { + const { locals } = getRequestEvent(); + if (!locals.user) return; + + const existing = await db + .select() + .from(llmConfig) + .where(eq(llmConfig.userId, locals.user.id)) + .limit(1); + + if (existing.length > 0) { + await db + .update(llmConfig) + .set({ + provider: data.provider, + apiKey: data.apiKey, + model: data.model ?? null + }) + .where(eq(llmConfig.userId, locals.user.id)); + } else { + await db.insert(llmConfig).values({ + id: crypto.randomUUID(), + userId: locals.user.id, + provider: data.provider, + apiKey: data.apiKey, + model: data.model ?? null + }); + } +}); diff --git a/web/src/lib/schema/settings.ts b/web/src/lib/schema/settings.ts new file mode 100644 index 0000000..264ad66 --- /dev/null +++ b/web/src/lib/schema/settings.ts @@ -0,0 +1,7 @@ +import { object, string, pipe, minLength, optional } from 'valibot'; + +export const llmConfigSchema = object({ + provider: pipe(string(), minLength(1)), + apiKey: pipe(string(), minLength(1)), + model: optional(string()) +}); diff --git a/web/src/routes/dashboard/settings/+page.svelte b/web/src/routes/dashboard/settings/+page.svelte new file mode 100644 index 0000000..fa3b763 --- /dev/null +++ b/web/src/routes/dashboard/settings/+page.svelte @@ -0,0 +1,62 @@ + + +

Settings

+ +
+

LLM Provider

+ +
+ + + + + + + +
+ + {#if config} +

+ Current: {config.provider} · Key: {config.apiKey} + {#if config.model} · Model: {config.model}{/if} +

+ {/if} +