import { z } from "zod"; export const nodeRoleSchema = z.enum(["hub", "gw", "edge", "ix"]); export type NodeRole = z.infer; export const aliasPurposeSchema = z.enum([ "geo", "ix", "backup", "admin", "custom", ]); export type AliasPurpose = z.infer; export const aliasModeSchema = z.enum(["primary", "pair"]); export type AliasMode = z.infer; export const syncStatusSchema = z.enum([ "pending", "ok", "drift", "missing", "error", ]); export type SyncStatus = z.infer; export const addressFamilySchema = z.enum(["v4", "v6"]); export const cfZoneSchema = z.object({ id: z.string(), name: z.string(), status: z.string().optional(), }); export type CfZone = z.infer; export const cfDnsRecordSchema = z.object({ id: z.string().optional(), type: z.string(), name: z.string(), content: z.string(), ttl: z.number(), proxied: z.boolean().optional(), priority: z.number().optional(), }); export type CfDnsRecord = z.infer; export const createDnsRecordPayloadSchema = z.object({ type: z.string(), name: z.string(), content: z.string(), ttl: z.number(), proxied: z.boolean().optional(), priority: z.number().optional(), }); export type CreateDnsRecordPayload = z.infer; export const patchDnsRecordPayloadSchema = createDnsRecordPayloadSchema.partial(); export type PatchDnsRecordPayload = z.infer; export const locationSchema = z.object({ id: z.string(), code: z.string(), name: z.string(), country: z.string().nullable(), sortOrder: z.number(), }); export type Location = z.infer; export const zoneSchema = z.object({ id: z.string(), cfZoneId: z.string().nullable(), name: z.string(), role: z.string(), namingTemplate: z.string(), defaultTtl: z.number(), lastSyncAt: z.string().nullable(), createdAt: z.string(), updatedAt: z.string(), }); export type Zone = z.infer; export const zoneCreateSchema = z.object({ name: z.string().min(1), cfZoneId: z.string().optional().nullable(), role: z.string().default("routing"), namingTemplate: z.string().optional(), defaultTtl: z.number().int().min(60).max(86400).optional(), }); export type ZoneCreate = z.infer; export const zonePatchSchema = zoneCreateSchema.partial(); export type ZonePatch = z.infer; export const nodeAddressSchema = z.object({ id: z.string(), family: addressFamilySchema, ip: z.string(), }); export type NodeAddress = z.infer; export const nodeSchema = z.object({ id: z.string(), zoneId: z.string(), locationId: z.string(), locationCode: z.string().optional(), locationName: z.string().optional(), hostname: z.string(), role: nodeRoleSchema, indexNum: z.number(), providerTag: z.string().nullable(), notes: z.string().nullable(), syncStatus: syncStatusSchema, cfARecordId: z.string().nullable(), cfAaaaRecordId: z.string().nullable(), lastError: z.string().nullable(), addresses: z.array(nodeAddressSchema).default([]), aliasCount: z.number().optional(), createdAt: z.string(), updatedAt: z.string(), }); export type Node = z.infer; export const nodeCreateSchema = z.object({ zoneId: z.string().min(1), locationId: z.string().min(1), role: nodeRoleSchema, indexNum: z.number().int().min(1).max(99).default(1), hostname: z.string().min(1).optional(), providerTag: z.string().optional().nullable(), notes: z.string().optional().nullable(), ipv4: z.string().min(1), ipv6: z.string().optional().nullable(), }); export type NodeCreate = z.infer; export const nodePatchSchema = z.object({ locationId: z.string().optional(), role: nodeRoleSchema.optional(), indexNum: z.number().int().min(1).max(99).optional(), hostname: z.string().min(1).optional(), providerTag: z.string().optional().nullable(), notes: z.string().optional().nullable(), ipv4: z.string().min(1).optional(), ipv6: z.string().optional().nullable(), }); export type NodePatch = z.infer; export const aliasSchema = z.object({ id: z.string(), zoneId: z.string(), name: z.string(), purpose: aliasPurposeSchema, mode: aliasModeSchema, targetNodeId: z.string(), targetHostname: z.string().optional(), syncStatus: syncStatusSchema, cfRecordId: z.string().nullable(), lastError: z.string().nullable(), createdAt: z.string(), updatedAt: z.string(), }); export type Alias = z.infer; export const aliasCreateSchema = z.object({ zoneId: z.string().min(1), name: z.string().min(1), purpose: aliasPurposeSchema.default("geo"), mode: aliasModeSchema.default("primary"), targetNodeId: z.string().min(1), }); export type AliasCreate = z.infer; export const aliasPatchSchema = z.object({ name: z.string().min(1).optional(), purpose: aliasPurposeSchema.optional(), mode: aliasModeSchema.optional(), targetNodeId: z.string().min(1).optional(), }); export type AliasPatch = z.infer; export const aliasRetargetSchema = z.object({ targetNodeId: z.string().min(1), }); export type AliasRetarget = z.infer; export const syncDiffOpSchema = z.object({ id: z.string(), kind: z.enum([ "create", "update", "delete", "orphan", "proxy_violation", "noop", ]), entityType: z.enum(["node_a", "node_aaaa", "alias", "orphan"]), entityId: z.string().nullable(), recordName: z.string(), recordType: z.string(), desired: z.record(z.string(), z.unknown()).nullable(), observed: z.record(z.string(), z.unknown()).nullable(), detail: z.string().optional(), }); export type SyncDiffOp = z.infer; export const syncJobSchema = z.object({ id: z.string(), zoneId: z.string(), status: z.enum(["pending", "running", "done", "failed"]), diff: z.array(syncDiffOpSchema).optional(), error: z.string().nullable(), createdAt: z.string(), finishedAt: z.string().nullable(), }); export type SyncJob = z.infer; export const syncApplySchema = z.object({ opIds: z.array(z.string()).optional(), }); export type SyncApply = z.infer; export const dashboardStatsSchema = z.object({ nodes: z.number(), aliases: z.number(), syncOk: z.number(), drift: z.number(), proxyViolations: z.number(), orphans: z.number(), lastSyncAt: z.string().nullable(), nodesWithoutIp: z.number(), brokenAliases: z.number(), }); export type DashboardStats = z.infer; export const topologyNodeSchema = z.object({ id: z.string(), hostname: z.string(), role: nodeRoleSchema, locationCode: z.string(), ipv4: z.string().nullable(), syncStatus: syncStatusSchema, }); export const topologyEdgeSchema = z.object({ id: z.string(), aliasName: z.string(), purpose: aliasPurposeSchema, fromNodeId: z.string(), toHostname: z.string(), }); export const topologySchema = z.object({ nodes: z.array(topologyNodeSchema), edges: z.array(topologyEdgeSchema), locations: z.array(locationSchema), }); export type Topology = z.infer; export const bindExportSchema = z.object({ zoneName: z.string(), content: z.string(), }); export type BindExport = z.infer; export const orphanIgnoreSchema = z.object({ recordName: z.string().min(1), recordType: z.string().min(1), });