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 }), }); export const traefikWidgetSchema = z.object({ type: z.literal("traefik"), name: z.string().default("Traefik"), apiUrl: z.string().url(), interval: durationSchema.default("15s"), }); export const widgetSchema = z.discriminatedUnion("type", [ bookmarkWidgetSchema, searchWidgetSchema, dockerWidgetSchema, databaseWidgetSchema, systemWidgetSchema, httpWidgetSchema, traefikWidgetSchema, ]); 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;