diff options
| author | Adam Malczewski <[email protected]> | 2026-05-22 17:07:31 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-22 17:07:31 +0900 |
| commit | 288b21cec98421fda57028a0c8c9d835cfbb14b0 (patch) | |
| tree | 9ce3ec38acdcf7be96e28f1e0d5deccf6b46c917 /packages/frontend/src/lib/components/ModelStatus.svelte | |
| parent | 45a4890031192f4e7409443f98e824dad17ba175 (diff) | |
| download | dispatch-288b21cec98421fda57028a0c8c9d835cfbb14b0.tar.gz dispatch-288b21cec98421fda57028a0c8c9d835cfbb14b0.zip | |
feat: add/remove keys from UI, backend URL setting, user service, Docker fix
- Add POST /models/add-key and POST /models/remove-key API endpoints
- Add 'Add New Key' modal (page-level) with provider selection
- Add remove button per key in Model Status view
- Add configurable backend URL setting in Settings panel with localStorage persistence
- Convert systemd service from system to user service (systemctl --user)
- Fix Docker entrypoint to chown all nested node_modules dirs
- Update dispatch.toml credential paths to use -pro/-max naming
- Make API port configurable via PORT env var (default 3000, prod 18390)
Diffstat (limited to 'packages/frontend/src/lib/components/ModelStatus.svelte')
| -rw-r--r-- | packages/frontend/src/lib/components/ModelStatus.svelte | 49 |
1 files changed, 48 insertions, 1 deletions
diff --git a/packages/frontend/src/lib/components/ModelStatus.svelte b/packages/frontend/src/lib/components/ModelStatus.svelte index 1270fcc..d6ff0f5 100644 --- a/packages/frontend/src/lib/components/ModelStatus.svelte +++ b/packages/frontend/src/lib/components/ModelStatus.svelte @@ -20,10 +20,12 @@ const { keys = [], currentModel, apiBase = "", + onAddKey = () => {}, }: { keys?: KeyInfo[]; currentModel?: string; apiBase?: string; + onAddKey?: () => void; } = $props(); const activeKeys = $derived(keys.filter((k) => k.status === "active").length); @@ -44,6 +46,9 @@ let showKeyModal = $state<string | null>(null); // keyId or null let keyModalValue = $state(""); let keyModalError = $state<string | null>(null); let keyModalSaving = $state(false); +let removingKey = $state<string | null>(null); + + async function loadCredentialStatus(): Promise<void> { try { @@ -129,6 +134,27 @@ async function saveApiKey(): Promise<void> { } } +async function removeKey(keyId: string): Promise<void> { + if (!confirm(`Remove key "${keyId}" from config?`)) return; + removingKey = keyId; + try { + const res = await fetch(`${apiBase}/models/remove-key`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ id: keyId }), + }); + const data = (await res.json()) as { success?: boolean; error?: string }; + if (res.ok && data.success) { + await new Promise((r) => setTimeout(r, 500)); + window.location.reload(); + } + } catch { + // ignore + } finally { + removingKey = null; + } +} + $effect(() => { void loadCredentialStatus(); void loadApiKeyStatus(); @@ -209,8 +235,21 @@ function truncate(str: string | null, max: number): string { > {key.status} </span> - <span class="text-xs font-mono">{key.id}</span> + <span class="text-xs font-mono flex-1">{key.id}</span> <span class="text-xs text-base-content/40">{key.provider}</span> + <button + type="button" + class="btn btn-xs btn-ghost btn-square text-base-content/30 hover:text-error" + disabled={removingKey === key.id} + onclick={() => removeKey(key.id)} + title="Remove key" + > + {#if removingKey === key.id} + <span class="loading loading-spinner loading-xs"></span> + {:else} + ✕ + {/if} + </button> </div> {#if key.status === "exhausted"} <div class="pl-2 flex flex-col gap-0.5"> @@ -270,6 +309,13 @@ function truncate(str: string | null, max: number): string { </div> {/if} {/if} + <button + type="button" + class="btn btn-sm btn-primary btn-outline w-full mt-2" + onclick={onAddKey} + > + + Add New Key + </button> <!-- Import Key Modal --> {#if showKeyModal} <div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" role="dialog"> @@ -308,4 +354,5 @@ function truncate(str: string | null, max: number): string { </div> </div> {/if} + </div> |
