zerobyte/app/schemas/config-import.ts
Jakub Trávník 8591432c59 refactor: add ArkType schema validation for config import
- Add app/schemas/config-import.ts with ArkType schemas for upfront validation
- Replace runtime type checks with schema-based validation in config-import.ts
- Return validation errors early with detailed path and message info
- Export retentionPolicySchema from backups.dto.ts for reuse
- Update CLI to handle validation errors in both dry-run and import modes
- Fix README mirrors examples to use existing repository names
2026-01-02 14:02:06 +01:00

114 lines
4.2 KiB
TypeScript

import { type } from "arktype";
import { volumeConfigSchema } from "./volumes";
import { repositoryConfigSchema } from "./restic";
import { notificationConfigSchema } from "./notifications";
import { retentionPolicySchema } from "../server/modules/backups/backups.dto";
/**
* ArkType schemas for validating config import JSON files.
* These provide runtime validation with detailed error messages.
*/
// Short ID format: 8 character base64url string
const shortIdSchema = type(/^[A-Za-z0-9_-]{8}$/);
// Volume entry schema for import
export const volumeImportSchema = type({
name: "string>=1",
shortId: shortIdSchema.optional(),
autoRemount: "boolean?",
config: volumeConfigSchema,
}).onUndeclaredKey("delete");
// Repository entry schema for import
export const repositoryImportSchema = type({
name: "string>=1",
shortId: shortIdSchema.optional(),
compressionMode: type("'auto' | 'off' | 'max'").optional(),
config: repositoryConfigSchema,
}).onUndeclaredKey("delete");
// Notification destination entry schema for import
export const notificationDestinationImportSchema = type({
name: "string>=1",
enabled: "boolean?",
config: notificationConfigSchema,
}).onUndeclaredKey("delete");
// Schedule notification assignment (either string name or object with settings)
const scheduleNotificationObjectSchema = type({
name: "string>=1",
notifyOnStart: "boolean?",
notifyOnSuccess: "boolean?",
notifyOnWarning: "boolean?",
notifyOnFailure: "boolean?",
}).onUndeclaredKey("delete");
export const scheduleNotificationAssignmentSchema = type("string>=1").or(scheduleNotificationObjectSchema);
// Schedule mirror assignment
export const scheduleMirrorSchema = type({
repository: "string>=1",
enabled: "boolean?",
}).onUndeclaredKey("delete");
// Array types for complex schemas
const scheduleNotificationsArray = scheduleNotificationAssignmentSchema.array();
const scheduleMirrorsArray = scheduleMirrorSchema.array();
// Backup schedule entry schema for import
export const backupScheduleImportSchema = type({
name: "string?",
shortId: shortIdSchema.optional(),
volume: "string>=1",
repository: "string>=1",
cronExpression: "string",
enabled: "boolean?",
retentionPolicy: retentionPolicySchema.or("null").optional(),
excludePatterns: "string[]?",
excludeIfPresent: "string[]?",
includePatterns: "string[]?",
oneFileSystem: "boolean?",
notifications: scheduleNotificationsArray.optional(),
mirrors: scheduleMirrorsArray.optional(),
}).onUndeclaredKey("delete");
// User entry schema for import
export const userImportSchema = type({
username: "string>=1",
password: "(string>=1)?",
passwordHash: "(string>=1)?",
hasDownloadedResticPassword: "boolean?",
}).onUndeclaredKey("delete");
// Recovery key format: 64-character hex string
const recoveryKeySchema = type(/^[a-fA-F0-9]{64}$/);
// Array types for root config
const volumesArray = volumeImportSchema.array();
const repositoriesArray = repositoryImportSchema.array();
const backupSchedulesArray = backupScheduleImportSchema.array();
const notificationDestinationsArray = notificationDestinationImportSchema.array();
const usersArray = userImportSchema.array();
// Root config schema
export const importConfigSchema = type({
volumes: volumesArray.optional(),
repositories: repositoriesArray.optional(),
backupSchedules: backupSchedulesArray.optional(),
notificationDestinations: notificationDestinationsArray.optional(),
users: usersArray.optional(),
recoveryKey: recoveryKeySchema.optional(),
}).onUndeclaredKey("delete");
// Type exports
export type VolumeImport = typeof volumeImportSchema.infer;
export type RepositoryImport = typeof repositoryImportSchema.infer;
export type NotificationDestinationImport = typeof notificationDestinationImportSchema.infer;
export type BackupScheduleImport = typeof backupScheduleImportSchema.infer;
export type UserImport = typeof userImportSchema.infer;
export type ImportConfig = typeof importConfigSchema.infer;
export type ScheduleNotificationAssignment = typeof scheduleNotificationAssignmentSchema.infer;
export type ScheduleMirror = typeof scheduleMirrorSchema.infer;
// RetentionPolicy type is re-exported from backups.dto.ts
export type { RetentionPolicy } from "../server/modules/backups/backups.dto";