import { z } from "zod"; const searchEngineSchema = z.enum(["duckduckgo", "google", "bing"]); const durationSchema = z .string() .regex(/^\d+(ms|s|m|h)$/, 'Expected a duration like "500ms", "5s", "1m", or "1h"'); export const bookmarkWidgetSchema = z.object({ type: z.literal("bookmark"), name: z.string(), href: z.string().url(), description: z.string().optional(), icon: z.string().optional(), }); export const searchWidgetSchema = z.object({ type: z.literal("search"), engines: z.array(searchEngineSchema).min(1).default(["duckduckgo"]), defaultEngine: searchEngineSchema.optional(), }); export const dockerWidgetSchema = z.object({ type: z.literal("docker"), name: z.string(), containerName: z.string(), href: z.string().url().optional(), showStats: z.boolean().default(true), interval: durationSchema.default("5s"), icon: z.string().optional(), }); export const databaseWidgetSchema = z.object({ type: z.literal("database"), name: z.string(), containerName: z.string(), engine: z.enum(["postgres", "redis"]), interval: durationSchema.default("10s"), }); export const systemWidgetSchema = z.object({ type: z.literal("system"), name: z.string().default("System"), interval: durationSchema.default("5s"), }); export const httpWidgetSchema = z.object({ type: z.literal("http"), name: z.string(), url: z.string().url(), method: z.enum(["GET", "HEAD", "POST"]).default("GET"), timeout: durationSchema.default("5s"), interval: durationSchema.default("30s"), expect: z .object({ status: z.number().int().min(100).max(599).default(200), }) .default({ status: 200 }), }); const serviceCommonFields = { type: z.literal("service") as z.ZodLiteral<"service">, name: z.string(), containerName: z.string(), href: z.string().url().optional(), showStats: z.boolean().default(true), interval: durationSchema.default("15s"), icon: z.string().optional(), }; export const giteaServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("gitea"), username: z.string().default("Valknar"), apiToken: z.string(), }); export const coolifyServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("coolify"), apiToken: z.string(), }); export const immichServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("immich"), apiKey: z.string(), }); export const n8nServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("n8n"), apiKey: z.string(), }); export const umamiServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("umami"), username: z.string(), password: z.string(), websiteId: z.string(), }); export const headscaleServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("headscale"), apiToken: z.string(), }); export const traefikServiceWidgetSchema = z.object({ ...serviceCommonFields, service: z.literal("traefik"), }); export const serviceWidgetSchema = z.discriminatedUnion("service", [ giteaServiceWidgetSchema, coolifyServiceWidgetSchema, immichServiceWidgetSchema, n8nServiceWidgetSchema, umamiServiceWidgetSchema, headscaleServiceWidgetSchema, traefikServiceWidgetSchema, ]); export const widgetSchema = z.discriminatedUnion("type", [ bookmarkWidgetSchema, searchWidgetSchema, dockerWidgetSchema, databaseWidgetSchema, systemWidgetSchema, httpWidgetSchema, serviceWidgetSchema, ]); export const groupSchema = z.object({ name: z.string(), widgets: z.array(widgetSchema), }); export const themeSchema = z .object({ mode: z.enum(["dark", "light", "auto"]).default("dark"), variables: z.record(z.string(), z.string()).default({}), customCssPath: z.string().optional(), }) .default({ mode: "dark", variables: {} }); export const settingsSchema = z .object({ title: z.string().default("PulseNode"), }) .default({ title: "PulseNode" }); const dockerDiscoveryDefaults = { enabled: false, socketPath: "/var/run/docker.sock", labelPrefix: "traefik", excludeLabels: [] as string[], }; const dockerDiscoverySchema = z.object({ enabled: z.boolean().default(false), socketPath: z.string().default("/var/run/docker.sock"), labelPrefix: z.string().default("traefik"), network: z.string().optional(), excludeLabels: z.array(z.string()).default([]), }); export const discoverySchema = z .object({ docker: dockerDiscoverySchema.default(dockerDiscoveryDefaults), }) .default({ docker: dockerDiscoveryDefaults }); export const configSchema = z.object({ theme: themeSchema, settings: settingsSchema, discovery: discoverySchema, groups: z.array(groupSchema).default([]), }); export type Config = z.infer; export type Widget = z.infer; export type Group = z.infer; export type SearchEngine = z.infer; export type ServiceWidget = z.infer; export type GiteaServiceWidget = z.infer; export type CoolifyServiceWidget = z.infer; export type ImmichServiceWidget = z.infer; export type N8nServiceWidget = z.infer; export type UmamiServiceWidget = z.infer; export type HeadscaleServiceWidget = z.infer; export type TraefikServiceWidget = z.infer;