From 15fb9cc0aa7ddd6a1c7f76cf2bac6addfb8f91fb Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Sat, 25 Apr 2026 10:10:24 +0200 Subject: [PATCH] feat: pre/post backup webhooks --- app/client/api-client/types.gen.ts | 70 + .../create-schedule-form/advanced-section.tsx | 96 + .../components/create-schedule-form/index.tsx | 26 +- .../components/create-schedule-form/types.ts | 44 +- .../components/create-schedule-form/utils.ts | 23 + .../routes/__tests__/backup-details.test.tsx | 2 + .../routes/__tests__/edit-backup.test.tsx | 24 + .../modules/backups/routes/backup-details.tsx | 2 + .../modules/backups/routes/create-backup.tsx | 2 + .../migration.sql | 2 + .../snapshot.json | 2482 +++++++++++++++++ app/server/db/schema.ts | 3 + .../modules/agents/__tests__/session.test.ts | 1 + .../backups.service.execution.test.ts | 42 + .../backups/__tests__/backups.service.test.ts | 61 + app/server/modules/backups/backup-executor.ts | 57 +- app/server/modules/backups/backups.dto.ts | 7 + app/server/modules/backups/backups.service.ts | 11 +- .../backups/helpers/backup-lifecycle.ts | 2 +- .../src/__tests__/controller-session.test.ts | 1 + apps/agent/src/commands/backup-run.test.ts | 192 ++ apps/agent/src/commands/backup-run.ts | 117 +- packages/contracts/src/agent-protocol.ts | 2 + packages/core/package.json | 5 + packages/core/src/backup-hooks/index.ts | 291 ++ 25 files changed, 3477 insertions(+), 88 deletions(-) create mode 100644 app/drizzle/20260423190302_cute_pete_wisdom/migration.sql create mode 100644 app/drizzle/20260423190302_cute_pete_wisdom/snapshot.json create mode 100644 packages/core/src/backup-hooks/index.ts diff --git a/app/client/api-client/types.gen.ts b/app/client/api-client/types.gen.ts index ddf355f3..d63fc595 100644 --- a/app/client/api-client/types.gen.ts +++ b/app/client/api-client/types.gen.ts @@ -2434,6 +2434,16 @@ export type ListBackupSchedulesResponses = { includePatterns: Array | null; oneFileSystem: boolean; customResticParams: Array | null; + preBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries: number; retryDelay: number; lastBackupAt: number | null; @@ -2712,6 +2722,16 @@ export type CreateBackupScheduleData = { oneFileSystem?: boolean; tags?: Array; customResticParams?: Array; + preBackupWebhook?: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook?: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries?: number; retryDelay?: number; }; @@ -2747,6 +2767,16 @@ export type CreateBackupScheduleResponses = { includePatterns: Array | null; oneFileSystem: boolean; customResticParams: Array | null; + preBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries: number; retryDelay: number; lastBackupAt: number | null; @@ -2816,6 +2846,16 @@ export type GetBackupScheduleResponses = { includePatterns: Array | null; oneFileSystem: boolean; customResticParams: Array | null; + preBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries: number; retryDelay: number; lastBackupAt: number | null; @@ -3093,6 +3133,16 @@ export type UpdateBackupScheduleData = { oneFileSystem?: boolean; tags?: Array; customResticParams?: Array; + preBackupWebhook?: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook?: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries?: number; retryDelay?: number; }; @@ -3130,6 +3180,16 @@ export type UpdateBackupScheduleResponses = { includePatterns: Array | null; oneFileSystem: boolean; customResticParams: Array | null; + preBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries: number; retryDelay: number; lastBackupAt: number | null; @@ -3179,6 +3239,16 @@ export type GetBackupScheduleForVolumeResponses = { includePatterns: Array | null; oneFileSystem: boolean; customResticParams: Array | null; + preBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; + postBackupWebhook: { + url: string; + headers?: Record; + body?: string; + } | null; maxRetries: number; retryDelay: number; lastBackupAt: number | null; diff --git a/app/client/modules/backups/components/create-schedule-form/advanced-section.tsx b/app/client/modules/backups/components/create-schedule-form/advanced-section.tsx index a6d865a3..aa5aae3f 100644 --- a/app/client/modules/backups/components/create-schedule-form/advanced-section.tsx +++ b/app/client/modules/backups/components/create-schedule-form/advanced-section.tsx @@ -56,6 +56,102 @@ export const AdvancedSection = ({ form }: AdvancedSectionProps) => { )} /> + ( + + Pre-backup webhook + + + + + Called with POST before restic starts. A non-2xx response stops the backup. + + + + )} + /> + ( + + Pre-backup webhook headers + +