feat: Add Compliance Wiki as internal admin knowledge base

Migration 040 with wiki_categories + wiki_articles tables, 10 seed
articles across 8 categories (DSGVO, Art. 9, AVV, HinSchG etc.).
Read-only FastAPI API, Next.js proxy, and two-column frontend with
full-text search.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-09 20:01:27 +01:00
parent 3d22935065
commit 11d4c2fd36
8 changed files with 1379 additions and 1 deletions

View File

@@ -5,7 +5,7 @@
* retry logic, and optimistic locking support.
*/
import { SDKState, CheckpointStatus, ProjectInfo } from './types'
import { SDKState, CheckpointStatus, ProjectInfo, WikiCategory, WikiArticle, WikiSearchResult } from './types'
// =============================================================================
// TYPES
@@ -730,6 +730,110 @@ export class SDKApiClient {
)
}
// ===========================================================================
// WIKI (read-only knowledge base)
// ===========================================================================
/**
* List all wiki categories with article counts
*/
async listWikiCategories(): Promise<WikiCategory[]> {
const data = await this.fetchWithRetry<{ categories: Array<{
id: string; name: string; description: string; icon: string;
sort_order: number; article_count: number
}> }>(
`${this.baseUrl}/wiki?endpoint=categories`,
{ method: 'GET' }
)
return (data.categories || []).map(c => ({
id: c.id,
name: c.name,
description: c.description,
icon: c.icon,
sortOrder: c.sort_order,
articleCount: c.article_count,
}))
}
/**
* List wiki articles, optionally filtered by category
*/
async listWikiArticles(categoryId?: string): Promise<WikiArticle[]> {
const params = new URLSearchParams({ endpoint: 'articles' })
if (categoryId) params.set('category_id', categoryId)
const data = await this.fetchWithRetry<{ articles: Array<{
id: string; category_id: string; category_name: string; title: string;
summary: string; content: string; legal_refs: string[]; tags: string[];
relevance: string; source_urls: string[]; version: number; updated_at: string
}> }>(
`${this.baseUrl}/wiki?${params.toString()}`,
{ method: 'GET' }
)
return (data.articles || []).map(a => ({
id: a.id,
categoryId: a.category_id,
categoryName: a.category_name,
title: a.title,
summary: a.summary,
content: a.content,
legalRefs: a.legal_refs || [],
tags: a.tags || [],
relevance: a.relevance as WikiArticle['relevance'],
sourceUrls: a.source_urls || [],
version: a.version,
updatedAt: a.updated_at,
}))
}
/**
* Get a single wiki article by ID
*/
async getWikiArticle(id: string): Promise<WikiArticle> {
const data = await this.fetchWithRetry<{
id: string; category_id: string; category_name: string; title: string;
summary: string; content: string; legal_refs: string[]; tags: string[];
relevance: string; source_urls: string[]; version: number; updated_at: string
}>(
`${this.baseUrl}/wiki?endpoint=article&id=${encodeURIComponent(id)}`,
{ method: 'GET' }
)
return {
id: data.id,
categoryId: data.category_id,
categoryName: data.category_name,
title: data.title,
summary: data.summary,
content: data.content,
legalRefs: data.legal_refs || [],
tags: data.tags || [],
relevance: data.relevance as WikiArticle['relevance'],
sourceUrls: data.source_urls || [],
version: data.version,
updatedAt: data.updated_at,
}
}
/**
* Full-text search across wiki articles
*/
async searchWiki(query: string): Promise<WikiSearchResult[]> {
const data = await this.fetchWithRetry<{ results: Array<{
id: string; title: string; summary: string; category_name: string;
relevance: string; highlight: string
}> }>(
`${this.baseUrl}/wiki?endpoint=search&q=${encodeURIComponent(query)}`,
{ method: 'GET' }
)
return (data.results || []).map(r => ({
id: r.id,
title: r.title,
summary: r.summary,
categoryName: r.category_name,
relevance: r.relevance,
highlight: r.highlight,
}))
}
/**
* Health check
*/