Add generic plugin architecture and watch-directory feature
Introduces a manifest contract so feature containers self-describe their settings (JSON Schema + access rules). Backend and frontend gain generic plugin proxy and dynamic Extensions UI with zero feature-specific code. Doc-service is the first plugin consumer: exposes /plugin/manifest and /plugin/settings, adds a watchdog-based file watcher that auto-ingests PDFs from a mounted directory, maps subfolders to categories, supports AI-suggested folder/filename (user-confirmed), and enforces a no-remove policy. Access is gated by is_superuser or doc-service-admin group. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -107,6 +107,10 @@ export interface DocumentOut {
|
||||
created_at: string;
|
||||
processed_at: string | null;
|
||||
categories: CategoryOut[];
|
||||
source: string;
|
||||
watch_path: string | null;
|
||||
suggested_folder: string | null;
|
||||
suggested_filename: string | null;
|
||||
}
|
||||
|
||||
export interface DocumentPage {
|
||||
@@ -371,3 +375,59 @@ export const updateSystemPrompt = (
|
||||
api
|
||||
.patch<SystemPromptsData>(`/settings/system-prompts/${serviceId}`, data)
|
||||
.then((r) => r.data);
|
||||
|
||||
// --- Document suggestions (watch-ingested documents) ---
|
||||
export const confirmFolderSuggestion = (docId: string) =>
|
||||
api.post(`/documents/${docId}/suggestions/folder/confirm`);
|
||||
|
||||
export const rejectFolderSuggestion = (docId: string) =>
|
||||
api.post(`/documents/${docId}/suggestions/folder/reject`);
|
||||
|
||||
export const confirmFilenameSuggestion = (docId: string) =>
|
||||
api.post(`/documents/${docId}/suggestions/filename/confirm`);
|
||||
|
||||
export const rejectFilenameSuggestion = (docId: string) =>
|
||||
api.post(`/documents/${docId}/suggestions/filename/reject`);
|
||||
|
||||
// --- Plugins ---
|
||||
export interface PluginOut {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface PluginSchemaProperty {
|
||||
type: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface PluginManifest {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
version: string;
|
||||
access: {
|
||||
allow_superuser: boolean;
|
||||
required_groups: string[];
|
||||
};
|
||||
settings_schema: {
|
||||
type: string;
|
||||
title?: string;
|
||||
properties: Record<string, PluginSchemaProperty>;
|
||||
};
|
||||
}
|
||||
|
||||
export const getPlugins = () =>
|
||||
api.get<PluginOut[]>("/plugins").then((r) => r.data);
|
||||
|
||||
export const getPluginManifest = (id: string) =>
|
||||
api.get<PluginManifest>(`/plugins/${id}/manifest`).then((r) => r.data);
|
||||
|
||||
export const getPluginSettings = (id: string) =>
|
||||
api.get<Record<string, unknown>>(`/plugins/${id}/settings`).then((r) => r.data);
|
||||
|
||||
export const updatePluginSettings = (id: string, data: Record<string, unknown>) =>
|
||||
api.patch<Record<string, unknown>>(`/plugins/${id}/settings`, data).then((r) => r.data);
|
||||
|
||||
Reference in New Issue
Block a user