Automate Paperless webhook setup

This commit is contained in:
2026-05-07 23:57:42 +02:00
parent f913bc0ba6
commit 58e737d5cd
8 changed files with 184 additions and 4 deletions

View File

@@ -53,6 +53,7 @@ export interface SettingsResponse {
aiSystemPrompt: string | null;
aiTimeoutSeconds: number;
aiMaxTokens: number;
paperlessWorkflowId: number | null;
};
secrets: {
paperlessTokenSet: boolean;
@@ -121,3 +122,12 @@ export async function triggerNtfyTest(): Promise<void> {
export async function triggerAiTest(): Promise<void> {
await request("/settings/test/ai", { method: "POST" });
}
export async function setupPaperlessWebhook(): Promise<{
status: string;
workflowId: number | null;
webhookUrl: string;
settings: SettingsResponse;
}> {
return request("/settings/paperless-webhook/setup", { method: "POST" });
}

View File

@@ -199,6 +199,12 @@
"webhookSecretRemove": "Webhook-Secret löschen",
"webhookSecretInfo": "Ein Webhook-Secret ist hinterlegt. Lasse das Feld leer, um es unverändert zu lassen.",
"webhookSecretHelp": "Paperless sendet diesen Wert im Header x-contract-companion-secret.",
"webhookSetup": "Paperless automatisch konfigurieren",
"webhookSetupRunning": "Konfiguriere Paperless…",
"webhookSetupSuccess": "Paperless-Workflow konfiguriert (ID {{id}})",
"webhookSetupError": "Paperless-Workflow konnte nicht konfiguriert werden",
"webhookSetupHelp": "Legt ein Secret an und erstellt oder aktualisiert in Paperless den Workflow \"Contract Companion AI Review\".",
"webhookWorkflowInfo": "Paperless-Workflow ID {{id}} ist verknüpft.",
"ical": "iCal-Abo",
"icalFeedUrl": "Feed-URL",
"paperlessApiUrl": "Paperless API URL",

View File

@@ -199,6 +199,12 @@
"webhookSecretRemove": "Remove webhook secret",
"webhookSecretInfo": "A webhook secret is stored. Leave empty to keep it.",
"webhookSecretHelp": "Paperless sends this value in the x-contract-companion-secret header.",
"webhookSetup": "Configure Paperless automatically",
"webhookSetupRunning": "Configuring Paperless…",
"webhookSetupSuccess": "Paperless workflow configured (ID {{id}})",
"webhookSetupError": "Unable to configure Paperless workflow",
"webhookSetupHelp": "Creates a secret and creates or updates the Paperless workflow \"Contract Companion AI Review\".",
"webhookWorkflowInfo": "Paperless workflow ID {{id}} is linked.",
"ical": "iCal subscription",
"icalFeedUrl": "Feed URL",
"paperlessApiUrl": "Paperless API URL",

View File

@@ -35,6 +35,7 @@ import {
fetchServerConfig,
fetchSettings,
resetIcalSecret,
setupPaperlessWebhook,
triggerAiTest,
triggerMailTest,
triggerNtfyTest,
@@ -263,6 +264,15 @@ export default function SettingsPage() {
onError: (error: Error) => showMessage(error.message ?? t("settings.aiTestError"), "error")
});
const paperlessWebhookSetupMutation = useMutation({
mutationFn: () => setupPaperlessWebhook(),
onSuccess: async (result) => {
showMessage(t("settings.webhookSetupSuccess", { id: result.workflowId ?? "-" }), "success");
await Promise.all([refetchSettings(), refetchServerConfig()]);
},
onError: (error: Error) => showMessage(error.message ?? t("settings.webhookSetupError"), "error")
});
const createCategoryMutation = useMutation({
mutationFn: (name: string) => apiCreateCategory(name),
onSuccess: async (category) => {
@@ -858,6 +868,26 @@ export default function SettingsPage() {
{t("settings.webhookSecretInfo")}
</Typography>
)}
{settingsData?.values.paperlessWorkflowId && (
<Typography variant="caption" color="text.secondary">
{t("settings.webhookWorkflowInfo", { id: settingsData.values.paperlessWorkflowId })}
</Typography>
)}
<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
<Button
variant="contained"
onClick={() => paperlessWebhookSetupMutation.mutate()}
disabled={paperlessWebhookSetupMutation.isPending || !serverConfig?.paperlessConfigured}
>
{paperlessWebhookSetupMutation.isPending
? t("settings.webhookSetupRunning")
: t("settings.webhookSetup")}
</Button>
{paperlessWebhookSetupMutation.isPending && <CircularProgress size={24} />}
</Stack>
<Typography variant="caption" color="text.secondary">
{t("settings.webhookSetupHelp")}
</Typography>
<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
<Button
variant="outlined"