import { z } from "zod"; /** * Base fields required regardless of Gitea integration mode. */ const BaseSchema = z.object({ netbirdApiUrl: z.string().url(), netbirdApiToken: z.string().min(1), giteaEnabled: z.string().default("true").transform((v) => v === "true"), reconcilerToken: z.string().min(1), pollIntervalSeconds: z.coerce.number().int().positive().default(30), port: z.coerce.number().int().positive().default(8080), dataDir: z.string().default("/data"), stateFile: z.string().default("netbird.json"), }); const GiteaFieldsSchema = z.object({ giteaUrl: z.string().url(), giteaToken: z.string().min(1), giteaRepo: z.string().regex(/^[^/]+\/[^/]+$/), // owner/repo }); /** * When giteaEnabled=true, Gitea fields are validated and required. * When giteaEnabled=false, Gitea fields are ignored. * * The output type always includes the optional Gitea fields so * downstream code can check `config.giteaEnabled` and narrow. */ const ConfigSchema = BaseSchema.merge(GiteaFieldsSchema.partial()) .superRefine((data, ctx) => { if (!data.giteaEnabled) return; if (!data.giteaUrl) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "GITEA_URL is required when GITEA_ENABLED=true", path: ["giteaUrl"], }); } if (!data.giteaToken) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "GITEA_TOKEN is required when GITEA_ENABLED=true", path: ["giteaToken"], }); } if (!data.giteaRepo) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "GITEA_REPO is required when GITEA_ENABLED=true", path: ["giteaRepo"], }); } }); export type Config = z.infer; export function loadConfig(): Config { return ConfigSchema.parse({ netbirdApiUrl: Deno.env.get("NETBIRD_API_URL"), netbirdApiToken: Deno.env.get("NETBIRD_API_TOKEN"), giteaEnabled: Deno.env.get("GITEA_ENABLED"), giteaUrl: Deno.env.get("GITEA_URL"), giteaToken: Deno.env.get("GITEA_TOKEN"), giteaRepo: Deno.env.get("GITEA_REPO"), reconcilerToken: Deno.env.get("RECONCILER_TOKEN"), pollIntervalSeconds: Deno.env.get("POLL_INTERVAL_SECONDS"), port: Deno.env.get("PORT"), dataDir: Deno.env.get("DATA_DIR"), stateFile: Deno.env.get("STATE_FILE"), }); }