From f89b5b4437c12a8821a67597837791c638b65e7b Mon Sep 17 00:00:00 2001 From: ramvignesh-b Date: Tue, 12 May 2026 04:40:55 +0530 Subject: [PATCH 01/11] feat: add functionality to delete provider configurations and associated tokens via UI and API --- src/core/ConfigManager.ts | 9 +++++++++ src/routes/config.ts | 25 +++++++++++++++++++++++++ src/routes/dashboard.tsx | 18 ++++++++++++++---- src/views/dashboard.js | 32 ++++++++++++++++++++++++++++++-- tests/integration/config.test.ts | 14 ++++++++++++++ 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/core/ConfigManager.ts b/src/core/ConfigManager.ts index 2d9a350..e3a7cbd 100644 --- a/src/core/ConfigManager.ts +++ b/src/core/ConfigManager.ts @@ -40,4 +40,13 @@ export class ConfigManager { return result; } + + async deleteProviderConfig(provider: string): Promise { + await this.redis.del(`config:${provider}`); + // Also clean up tokens + const tokenKeys = await this.redis.keys(`provider:${provider}:*`); + if (tokenKeys.length > 0) { + await this.redis.del(...tokenKeys); + } + } } diff --git a/src/routes/config.ts b/src/routes/config.ts index d8d3b6d..f77066d 100644 --- a/src/routes/config.ts +++ b/src/routes/config.ts @@ -70,6 +70,23 @@ const setConfigRoute = createRoute({ }, }); +const deleteConfigRoute = createRoute({ + method: "delete", + path: "/{provider}", + security: [{ API_KEY: [] }], + request: { + params: z.object({ + provider: z.string().openapi({ example: "trakt" }), + }), + }, + responses: { + 200: { + content: { "application/json": { schema: SuccessMessage } }, + description: "Delete a provider configuration and its tokens", + }, + }, +}); + // Implementations configRoutes.use("*", authMiddleware); @@ -88,4 +105,12 @@ configRoutes.openapi(setConfigRoute, async (c) => { return c.json({ message: `Config for ${provider} saved successfully` }, 200); }); +configRoutes.openapi(deleteConfigRoute, async (c) => { + const provider = c.req.valid("param").provider; + const configManager = new ConfigManager(redis); + + await configManager.deleteProviderConfig(provider); + return c.json({ message: `Config for ${provider} deleted successfully` }, 200); +}); + export { configRoutes }; diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx index aa5bca3..9b78073 100644 --- a/src/routes/dashboard.tsx +++ b/src/routes/dashboard.tsx @@ -308,10 +308,20 @@ export const Dashboard = (props: { isUnlocked: boolean }) => (
- +
+ + +
{ }); if (res.status === 401) { - this.isUnlocked = false; - throw new Error("Session expired"); + return this.handleSessionExpired(); } if (!res.ok) throw new Error("Refresh failed"); @@ -166,6 +165,35 @@ document.addEventListener("alpine:init", () => { } }, + async deleteProvider(name) { + if ( + !confirm( + `Are you sure you want to delete ${name}? This will also remove all associated tokens.`, + ) + ) + return; + + this.loading = true; + try { + const res = await fetch(`/api/config/${name}`, { + method: "DELETE", + }); + + if (res.status === 401) { + return this.handleSessionExpired(); + } + + if (!res.ok) throw new Error("Delete failed"); + + this.showNotification(`Deleted ${name}`); + await this.fetchProviders(); + } catch (err) { + this.showNotification(err.message, "error"); + } finally { + this.loading = false; + } + }, + editProvider(provider) { this.form = { providerName: provider.name, diff --git a/tests/integration/config.test.ts b/tests/integration/config.test.ts index 68fcbcd..50aec70 100644 --- a/tests/integration/config.test.ts +++ b/tests/integration/config.test.ts @@ -78,4 +78,18 @@ describe("Config Integration", () => { expect(res.status).toBe(400); }); + + it("should delete a provider configuration", async () => { + redis.del.mockImplementation(() => Promise.resolve(1)); + + const res = await app.request("/api/config/trakt", { + method: "DELETE", + headers: { + Authorization: "Bearer test-api-key", + }, + }); + + expect(res.status).toBe(200); + expect(redis.del).toHaveBeenCalled(); + }); }); -- 2.52.0 From 106edb8bb71c7ca40891571c04d29a787341f9fc Mon Sep 17 00:00:00 2001 From: ramvignesh-b Date: Tue, 12 May 2026 04:42:33 +0530 Subject: [PATCH 02/11] feat: hide api key input when authenticated --- src/routes/dashboard.tsx | 68 ++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx index 9b78073..2db29fc 100644 --- a/src/routes/dashboard.tsx +++ b/src/routes/dashboard.tsx @@ -63,47 +63,53 @@ export const Dashboard = (props: { isUnlocked: boolean }) => (