From a8682807660ae1b3e5f86bcfd23f5b54653c5834 Mon Sep 17 00:00:00 2001 From: faligam Date: Mon, 7 Sep 2026 12:34:47 +0200 Subject: [PATCH] Add research deletion protection interface --- src/lib/api.ts | 6 ++- src/lib/components/ResearchListItem.svelte | 1 + src/lib/components/Sidebar.svelte | 4 ++ src/lib/research-api.ts | 34 ++++++++++-- src/lib/types.ts | 1 + src/routes/admin/deleted/+page.svelte | 57 ++++++++++++++++++++ src/routes/research/[id]/+page.svelte | 62 ++++++++++++++++------ 7 files changed, 145 insertions(+), 20 deletions(-) create mode 100644 src/routes/admin/deleted/+page.svelte diff --git a/src/lib/api.ts b/src/lib/api.ts index 2fd4f56..4cee574 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -45,11 +45,13 @@ export async function apiGet( export async function apiDelete( path: string, - config?: ApiConfig + config?: ApiConfig, + body?: unknown ): Promise { const res = await fetch(`${API_BASE_URL}${path}`, { method: 'DELETE', - headers: headers(config) + headers: headers(config), + body: body ? JSON.stringify(body) : undefined }); if (!res.ok) { throw new Error(`API Error ${res.status}: ${res.statusText}`); diff --git a/src/lib/components/ResearchListItem.svelte b/src/lib/components/ResearchListItem.svelte index 22bb039..b25337b 100644 --- a/src/lib/components/ResearchListItem.svelte +++ b/src/lib/components/ResearchListItem.svelte @@ -26,6 +26,7 @@ {formatDate(item.created_at)}

+ {#if item.is_deletion_protected}🔒 {/if} {item.query}

diff --git a/src/lib/components/Sidebar.svelte b/src/lib/components/Sidebar.svelte index fc13e83..585df21 100644 --- a/src/lib/components/Sidebar.svelte +++ b/src/lib/components/Sidebar.svelte @@ -121,6 +121,10 @@
+ +
{#if loading} diff --git a/src/lib/research-api.ts b/src/lib/research-api.ts index a411bd9..9df061b 100644 --- a/src/lib/research-api.ts +++ b/src/lib/research-api.ts @@ -10,6 +10,7 @@ interface ApiResearchStatus { updated_at?: string; source_count?: number; claim_count?: number; + is_deletion_protected?: boolean; } interface ApiSourcesResponse { @@ -70,6 +71,7 @@ function normalizeDetail(status: ApiResearchStatus): ResearchDetail { status: status.state.toUpperCase(), created_at: status.created_at, completed_at: status.updated_at, + is_deletion_protected: status.is_deletion_protected ?? false, }; } @@ -159,6 +161,7 @@ export async function listResearch( query: item.query, status: item.state.toUpperCase(), created_at: item.created_at, + is_deletion_protected: item.is_deletion_protected ?? false, })); } @@ -173,13 +176,37 @@ export async function getResearchReport( } /** - * Löscht eine Research-Abfrage permanent. + * Blendet eine Research-Abfrage aus. Sie bleibt für Administratoren im Papierkorb. */ export async function deleteResearch( apiKey: string, - researchId: string + researchId: string, + password?: string ): Promise { - await apiDelete(`/v1/research/${researchId}`, { apiKey }); + await apiDelete(`/v1/research/${researchId}`, { apiKey }, password ? { password } : undefined); +} + +export async function setDeletionProtection( + apiKey: string, researchId: string, password: string, currentPassword?: string +): Promise { + await apiPost(`/v1/research/${researchId}/deletion-protection`, { + password, current_password: currentPassword, + }, { apiKey }); +} + +export interface DeletedResearch { + research_id: string; + query: string; + hidden_at: string; + is_deletion_protected: boolean; +} + +export async function listDeletedResearch(apiKey: string): Promise { + return await apiGet('/v1/admin/research/deleted', { apiKey }); +} + +export async function purgeDeletedResearch(apiKey: string, researchId: string): Promise { + await apiDelete(`/v1/admin/research/${researchId}`, { apiKey }); } /** @@ -255,6 +282,7 @@ export interface ResearchDetail { methodology?: string; plan?: ResearchPlan; username?: string; + is_deletion_protected?: boolean; } export interface ResearchPlan { diff --git a/src/lib/types.ts b/src/lib/types.ts index aa91ea4..48849fe 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -11,6 +11,7 @@ export interface ResearchSummary { status: string; created_at: string; completed_at?: string; + is_deletion_protected?: boolean; } export interface SourceInfo { diff --git a/src/routes/admin/deleted/+page.svelte b/src/routes/admin/deleted/+page.svelte new file mode 100644 index 0000000..f4c427f --- /dev/null +++ b/src/routes/admin/deleted/+page.svelte @@ -0,0 +1,57 @@ + + +
+

Papierkorb (Administration)

+

Soft-gelöschte Recherchen. Eine endgültige Löschung wird protokolliert.

+ {#if loading}

Lade Papierkorb …

+ {:else if error}

{error}

+ {:else if items.length === 0}

Der Papierkorb ist leer.

+ {:else} +
+ {#each items as item (item.research_id)} +
+

{item.is_deletion_protected ? '🔒 ' : ''}{item.query}

Gelöscht: {new Date(item.hidden_at).toLocaleString('de-DE')}

+ +
+ {/each} +
+ {/if} +
diff --git a/src/routes/research/[id]/+page.svelte b/src/routes/research/[id]/+page.svelte index 9963a58..6aecc60 100644 --- a/src/routes/research/[id]/+page.svelte +++ b/src/routes/research/[id]/+page.svelte @@ -1,7 +1,7 @@