// src/constants.ts var SYNC_SYNCED = "synced"; var SYNC_PENDING_PUSH = "pending_push"; var SYNC_PENDING_DELETE = "pending_delete"; var SYNC_CONFLICT = "conflict"; var SYNC_ERROR = "error"; var CERT_OK = "ok"; var CERT_WARNING = "warning"; var CERT_EXPIRED = "expired"; var CERT_ERROR = "error"; var CERT_UNKNOWN = "unknown"; var CERT_MONITOR_AUTO = "auto"; var CERT_MONITOR_REQUIRED = "required"; var CERT_MONITOR_SKIPPED = "skipped"; var CERT_MONITORING_VALUES = [ CERT_MONITOR_AUTO, CERT_MONITOR_REQUIRED, CERT_MONITOR_SKIPPED ]; // src/validators.ts var LABEL_RE = "[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?"; var NAME_RE = new RegExp( `^(@|\\*|(\\*\\.)?${LABEL_RE}(?:\\.${LABEL_RE})*)$` ); var IPV4_RE = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/; var IPV6_RE = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/; var ALLOWED_TYPES = ["A", "AAAA", "CNAME", "TXT", "MX", "NS", "SRV", "CAA"]; var ValidationError = class extends Error { constructor(message) { super(message); this.name = "ValidationError"; } }; function validateDnsRecord(recordType, name, content, ttl, proxied) { const rt = recordType.toUpperCase(); if (!ALLOWED_TYPES.includes(rt)) { throw new ValidationError(`unsupported record type: ${recordType}`); } if (!NAME_RE.test(name)) { throw new ValidationError(`invalid record name: ${name}`); } if (ttl !== 1 && (ttl < 60 || ttl > 86400)) { throw new ValidationError("ttl must be 1 (auto) or 60-86400"); } if (proxied && !["A", "AAAA", "CNAME"].includes(rt)) { throw new ValidationError("proxied only allowed for A, AAAA, CNAME"); } switch (rt) { case "A": if (!IPV4_RE.test(content)) { throw new ValidationError("A record requires valid IPv4"); } break; case "AAAA": if (!IPV6_RE.test(content)) { throw new ValidationError("AAAA record requires valid IPv6"); } break; case "CNAME": case "NS": if (!content || content.includes(" ")) { throw new ValidationError("CNAME/NS requires valid hostname"); } break; case "TXT": if (!content || content.length > 2048) { throw new ValidationError("TXT content length 1-2048"); } break; } } function certStatusFromExpiry(daysLeft) { if (daysLeft < 0) return CERT_EXPIRED; if (daysLeft <= 30) return CERT_WARNING; return CERT_OK; } function shouldMonitorService(service, group) { if (!service.enabled) return false; if (!service.service_group_id) return true; return group?.enabled ?? false; } function isValidIpv4(ip) { const parts = ip.split("."); if (parts.length !== 4) return false; return parts.every((p) => { const n = Number(p); return Number.isInteger(n) && n >= 0 && n <= 255; }); } function isIpLiteral(value) { const v = value.trim(); if (!v) return false; if (isValidIpv4(v)) return true; return IPV6_RE.test(v); } // src/subdomain.ts function dnsNameToSubdomainLabel(recordName, zoneName) { const rn = recordName.trim().replace(/\.+$/, ""); const zn = zoneName.trim().replace(/\.+$/, ""); if (!rn || !zn) return null; if (rn === "*") return "*"; const wildcardFqdn = `*.${zn}`; if (rn.toLowerCase() === wildcardFqdn.toLowerCase()) return "*"; if (rn.toLowerCase() === zn.toLowerCase()) return "@"; const zoneSuffix = `.${zn}`; if (rn.toLowerCase().endsWith(zoneSuffix.toLowerCase())) { const prefix = rn.slice(0, rn.length - zoneSuffix.length); return prefix || "@"; } if (rn.startsWith("*.")) return rn; if (!rn.includes(".")) return rn; return null; } function subdomainLabelToFqdn(label, zoneName) { return label === "@" ? zoneName : `${label}.${zoneName}`; } function normalizeDnsRecordName(recordName, zoneName) { const label = dnsNameToSubdomainLabel(recordName, zoneName); if (label == null) { return recordName.trim().replace(/\.+$/, ""); } return subdomainLabelToFqdn(label, zoneName); } function dnsRecordNamesMatch(left, right, zoneName) { return normalizeDnsRecordName(left, zoneName).toLowerCase() === normalizeDnsRecordName(right, zoneName).toLowerCase(); } // src/parse-fqdn.ts function fqdnToDisplay(hostname, zoneName) { if (hostname === "@") { return zoneName; } return `${hostname}.${zoneName}`; } function parseFqdn(fqdn, knownZones) { const normalized = fqdn.trim().toLowerCase(); if (!normalized) { return null; } const zones = [...knownZones].sort((a, b) => b.length - a.length); for (const zone of zones) { const zoneLower = zone.toLowerCase(); if (normalized === zoneLower) { return { zoneName: zone, hostname: "@", fqdn: fqdnToDisplay("@", zone) }; } const suffix = `.${zoneLower}`; if (normalized.endsWith(suffix)) { const prefix = normalized.slice(0, -suffix.length); if (prefix) { return { zoneName: zone, hostname: prefix, fqdn: fqdnToDisplay(prefix, zone) }; } } } return null; } function bindingToFqdn(binding) { return binding.fqdn ?? fqdnToDisplay(binding.hostname, binding.zone_name); } // src/schemas.ts import { z } from "zod"; // src/health-providers.ts var HEALTH_CHECK_PROVIDERS = [ "local", "cloudflare", "globalping" ]; var HEALTH_STATUS_PROVIDERS = [ ...HEALTH_CHECK_PROVIDERS, "aggregate" ]; var HEALTH_CHECK_AGGREGATES = ["any", "all", "majority"]; var PROVIDER_SET = new Set(HEALTH_CHECK_PROVIDERS); var STATUS_SET = new Set(HEALTH_STATUS_PROVIDERS); var AGGREGATE_SET = new Set(HEALTH_CHECK_AGGREGATES); function normalizeProbeProvider(value) { return value === "cloudflare" || value === "globalping" || value === "local" ? value : "local"; } function normalizeStatusProvider(value) { if (typeof value === "string" && STATUS_SET.has(value)) { return value; } return "local"; } function uniqueHealthProviders(values) { const out = []; for (const value of values) { if (!PROVIDER_SET.has(String(value))) continue; const next = value; if (!out.includes(next)) out.push(next); } return out; } function parseHealthProviders(json, fallback) { if (Array.isArray(json)) { const parsed = uniqueHealthProviders(json); if (parsed.length > 0) return parsed; } if (typeof json === "string" && json.trim()) { const trimmed = json.trim(); if (trimmed.startsWith("[")) { try { const parsed = uniqueHealthProviders(JSON.parse(trimmed)); if (parsed.length > 0) return parsed; } catch { } } const one = uniqueHealthProviders(trimmed.split(",")); if (one.length > 0) return one; } return [normalizeProbeProvider(fallback)]; } function serializeHealthProviders(providers) { const unique = uniqueHealthProviders(providers); return JSON.stringify(unique.length > 0 ? unique : ["local"]); } function parseHealthAggregate(value) { if (typeof value === "string" && AGGREGATE_SET.has(value)) { return value; } return "majority"; } function derivePrimaryProvider(providers) { return uniqueHealthProviders(providers)[0] ?? "local"; } function targetProviders(target) { if (target.providers && target.providers.length > 0) { return uniqueHealthProviders(target.providers); } return [normalizeProbeProvider(target.provider)]; } function targetHasProvider(target, provider) { return targetProviders(target).includes(provider); } function aggregateHealthOk(oks, policy) { const n = oks.length; if (n === 0) return false; const down = oks.filter((ok) => !ok).length; if (policy === "any") return down === 0; if (policy === "all") return down < n; return down < Math.floor(n / 2) + 1; } function clampGlobalpingLimit(value, fallback = 3) { const n = typeof value === "number" ? value : Number(value); if (!Number.isFinite(n)) return fallback; return Math.min(10, Math.max(1, Math.trunc(n))); } function parseGlobalpingLocations(value) { const raw = typeof value === "string" ? value : ""; const parts = raw.split(",").map((part) => part.trim()).filter(Boolean); return parts.length > 0 ? parts : ["World"]; } // src/schemas.ts var certMonitoringSchema = z.enum(["auto", "required", "skipped"]); var lbModeSchema = z.enum(["round_robin", "failover", "weighted"]); var healthCheckTypeSchema = z.enum(["tcp", "http", "ping", "dns"]); var domainEnvironmentSchema = z.enum(["prod", "staging", "dev"]); var domainMonitorTypeSchema = z.enum(["http", "ping", "dns"]); var ipHealthStateSchema = z.enum(["up", "down", "degraded", "unknown"]); var nodeHealthStateSchema = z.enum([ "unknown", "checking", "healthy", "degraded", "unhealthy", "disabled" ]); var healthCheckProviderSchema = z.enum(HEALTH_CHECK_PROVIDERS); var healthStatusProviderSchema = z.enum(HEALTH_STATUS_PROVIDERS); var healthCheckAggregateSchema = z.enum(HEALTH_CHECK_AGGREGATES); var healthCheckProvidersSchema = z.preprocess( (value) => { if (value === void 0) return void 0; return Array.isArray(value) ? value : parseHealthProviders(value); }, z.array(healthCheckProviderSchema).min(1).transform((arr) => { const unique = uniqueHealthProviders(arr); return unique.length > 0 ? unique : ["local"]; }) ); var healthCheckScopeSchema = z.enum(["binding", "group"]); var ipHealthStatusSchema = z.object({ scope: healthCheckScopeSchema, ref_id: z.number(), ip: z.string(), status: ipHealthStateSchema, latency_ms: z.number().nullable(), consecutive_failures: z.number(), consecutive_successes: z.number().optional().default(0), last_checked_at: z.string().nullable(), last_error: z.string().nullable(), colo: z.string().nullable().optional(), provider: healthStatusProviderSchema.optional() }); var serviceIpHealthSchema = z.object({ ip: z.string(), status: ipHealthStateSchema, latency_ms: z.number().nullable(), last_checked_at: z.string().nullable().optional(), last_error: z.string().nullable().optional(), provider: healthStatusProviderSchema.optional(), colo: z.string().nullable().optional() }); var healthProbeLogSchema = z.object({ id: z.number(), scope: z.string(), ref_id: z.number(), ip: z.string(), provider: healthCheckProviderSchema, status: ipHealthStateSchema, ok: z.coerce.boolean(), latency_ms: z.number().nullable(), colo: z.string().nullable(), error: z.string().nullable(), checked_at: z.string() }); var failoverLogSchema = z.object({ id: z.number(), service_id: z.number(), binding_id: z.number(), fqdn: z.string(), ip: z.string(), action: z.enum(["added", "removed"]), created_at: z.string() }); var groupSchema = z.object({ id: z.number(), name: z.string(), slug: z.string(), created_at: z.string(), updated_at: z.string() }); var groupWithStatsSchema = groupSchema.extend({ domain_count: z.number() }); var serviceGroupTypeSchema = z.enum([ "vpn", "network", "internet", "bgp", "custom" ]); var serviceGroupSchema = z.object({ id: z.number(), name: z.string(), type: serviceGroupTypeSchema.catch("custom"), icon: z.string().nullable().default(null), domain: z.string().nullable().default(null), enabled: z.coerce.boolean(), lb_mode: lbModeSchema.catch("round_robin"), health_check_enabled: z.coerce.boolean().default(false), health_check_type: healthCheckTypeSchema.catch("tcp"), health_check_port: z.number().nullable().default(null), health_check_path: z.string().nullable().default(null), health_check_expected_status: z.number().nullable().default(null), health_check_interval_sec: z.number().default(30), health_check_timeout_ms: z.number().default(3e3), health_check_verify_tls: z.coerce.boolean().default(false), health_check_provider: healthCheckProviderSchema.catch("local"), health_check_providers: healthCheckProvidersSchema.catch(["local"]), health_check_aggregate: healthCheckAggregateSchema.catch("majority"), created_at: z.string(), updated_at: z.string() }); var serviceSchema = z.object({ id: z.number(), name: z.string(), slug: z.string(), service_group_id: z.number().nullable().optional(), subdomain: z.string().optional(), enabled: z.coerce.boolean().optional(), computed_fqdn: z.string().nullable().optional(), lb_weight: z.number().default(1), lb_priority: z.number().default(1), created_at: z.string(), updated_at: z.string() }); var serviceDomainBindingSchema = z.object({ binding_id: z.number(), domain_id: z.number(), zone_name: z.string(), hostname: z.string(), fqdn: z.string(), record_type: z.enum(["A", "CNAME"]).default("A"), target_ips: z.array(z.string()).optional(), target_ip: z.string().nullable().optional(), target_ip_weights: z.record(z.string(), z.coerce.number()).optional(), target_ip_priorities: z.record(z.string(), z.coerce.number()).optional(), target_cname: z.string().nullable().optional(), lb_mode: lbModeSchema.catch("round_robin"), health_check_enabled: z.coerce.boolean().default(false), health_check_type: healthCheckTypeSchema.catch("tcp"), health_check_port: z.number().nullable().default(null), health_check_path: z.string().nullable().default(null), health_check_expected_status: z.number().nullable().default(null), health_check_interval_sec: z.number().default(30), health_check_timeout_ms: z.number().default(3e3), health_check_verify_tls: z.coerce.boolean().default(false), health_check_provider: healthCheckProviderSchema.catch("local"), health_check_providers: healthCheckProvidersSchema.catch(["local"]), health_check_aggregate: healthCheckAggregateSchema.catch("majority"), cert_monitoring: certMonitoringSchema.default("auto"), sync_status: z.string().nullable().default(null), active_ips: z.array(z.string()).default([]) }).transform((binding) => ({ ...binding, target_ips: binding.target_ips && binding.target_ips.length > 0 ? binding.target_ips : binding.target_ip ? [binding.target_ip] : [], target_ip_weights: binding.target_ip_weights ?? {}, target_ip_priorities: binding.target_ip_priorities ?? {}, target_cname: binding.target_cname?.trim() || null, record_type: binding.target_cname?.trim() ? "CNAME" : binding.record_type ?? "A" })); var serviceViewSchema = serviceSchema.extend({ subdomain: z.string().default(""), enabled: z.coerce.boolean().default(false), ips: z.array(z.string()).default([]), domains: z.array(serviceDomainBindingSchema).default([]), health_status: ipHealthStateSchema.default("unknown"), health_latency_ms: z.number().nullable().default(null), ip_health: z.array(serviceIpHealthSchema).default([]), ip_enabled: z.record(z.string(), z.boolean()).default({}), lb_mode: lbModeSchema.catch("round_robin"), active_ips: z.array(z.string()).default([]) }); var serviceGroupViewSchema = serviceGroupSchema.extend({ services: z.array(serviceViewSchema).default([]), health_status: ipHealthStateSchema.default("unknown"), health_latency_ms: z.number().nullable().default(null) }); var serviceGroupsResponseSchema = z.object({ groups: z.array(serviceGroupViewSchema).default([]), ungrouped: z.array(serviceViewSchema).default([]) }); var domainSchema = z.object({ id: z.number(), group_id: z.number().nullable(), zone_name: z.string(), cf_zone_id: z.string(), status: z.string(), cert_monitoring: certMonitoringSchema.default("auto"), environment: domainEnvironmentSchema.nullable().optional().default(null), last_synced_at: z.string().nullable(), created_at: z.string(), updated_at: z.string() }); var domainListItemSchema = domainSchema.extend({ group_name: z.string().nullable(), service_count: z.number(), health_status: ipHealthStateSchema.default("unknown"), health_latency_ms: z.number().nullable().default(null), tags: z.array(z.string()).default([]) }); var serviceBindingSchema = z.object({ id: z.number(), domain_id: z.number(), service_id: z.number(), hostname: z.string(), dns_record_id: z.number().nullable(), zone_name: z.string(), group_id: z.number().nullable(), group_name: z.string().nullable(), service_name: z.string(), service_slug: z.string(), target_ip: z.string().nullable(), target_ips: z.array(z.string()).optional(), target_ip_weights: z.record(z.string(), z.coerce.number()).optional(), target_ip_priorities: z.record(z.string(), z.coerce.number()).optional(), lb_mode: lbModeSchema.catch("round_robin"), health_check_enabled: z.coerce.boolean().default(false), health_check_type: healthCheckTypeSchema.catch("tcp"), health_check_port: z.number().nullable().default(null), health_check_path: z.string().nullable().default(null), health_check_expected_status: z.number().nullable().default(null), health_check_interval_sec: z.number().default(30), health_check_timeout_ms: z.number().default(3e3), health_check_verify_tls: z.coerce.boolean().default(false), cert_monitoring: certMonitoringSchema.default("auto"), sync_status: z.string().nullable().default(null), created_at: z.string(), updated_at: z.string() }).transform((binding) => ({ ...binding, target_ips: binding.target_ips && binding.target_ips.length > 0 ? binding.target_ips : binding.target_ip ? [binding.target_ip] : [], target_ip_weights: binding.target_ip_weights ?? {}, target_ip_priorities: binding.target_ip_priorities ?? {} })); var dnsRecordSchema = z.object({ id: z.number(), domain_id: z.number(), cf_record_id: z.string().nullable(), record_type: z.string(), name: z.string(), content: z.string(), ttl: z.number(), proxied: z.boolean(), priority: z.number().nullable(), sync_status: z.string(), origin: z.string(), last_error: z.string().nullable(), created_at: z.string(), updated_at: z.string() }); var certificateSchema = z.object({ id: z.number(), domain_id: z.number(), subdomain_id: z.number().nullable(), service_id: z.number().nullable().optional().default(null), service_name: z.string().nullable().optional().default(null), hostname: z.string(), expires_at: z.string().nullable(), last_checked_at: z.string().nullable(), last_error: z.string().nullable(), status: z.string(), created_at: z.string(), updated_at: z.string() }); var serviceCertificateRowSchema = z.object({ binding_id: z.number(), domain_id: z.number(), service_id: z.number(), hostname: z.string(), cert_monitoring: certMonitoringSchema, id: z.number().nullable(), status: z.string(), expires_at: z.string().nullable(), last_checked_at: z.string().nullable(), last_error: z.string().nullable() }); var createGroupSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435"), slug: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 slug") }); var ipv4Schema = z.string().regex( /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, "\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 IPv4" ); var nodeAddressSchema = z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 IP \u0438\u043B\u0438 hostname").max(255); var healthCheckConfigFields = { health_check_enabled: z.coerce.boolean().optional(), health_check_type: healthCheckTypeSchema.optional(), health_check_port: z.number().int().min(1).max(65535).nullable().optional(), health_check_path: z.string().nullable().optional(), health_check_expected_status: z.number().int().min(100).max(599).nullable().optional(), health_check_interval_sec: z.number().int().min(5).max(3600).optional(), health_check_timeout_ms: z.number().int().min(100).max(3e4).optional(), health_check_verify_tls: z.coerce.boolean().optional(), health_check_provider: healthCheckProviderSchema.optional(), health_check_providers: healthCheckProvidersSchema.optional(), health_check_aggregate: healthCheckAggregateSchema.optional() }; var healthCheckConfigSchema = z.object(healthCheckConfigFields); var serviceDomainInputSchema = z.object({ fqdn: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 FQDN"), target_ips: z.array(ipv4Schema).optional(), target_cname: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 CNAME-\u0446\u0435\u043B\u044C").optional(), target_ip_weights: z.record(ipv4Schema, z.number().int().min(1).max(100)).optional(), target_ip_priorities: z.record(ipv4Schema, z.number().int().min(1).max(100)).optional(), lb_mode: lbModeSchema.optional(), ...healthCheckConfigFields }).superRefine((data, ctx) => { const hasIps = (data.target_ips?.length ?? 0) > 0; const hasCname = Boolean(data.target_cname?.trim()); if (!hasIps && !hasCname) { ctx.addIssue({ code: "custom", message: "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 IP \u0438\u043B\u0438 CNAME-\u0446\u0435\u043B\u044C", path: ["target_ips"] }); } if (hasIps && hasCname) { ctx.addIssue({ code: "custom", message: "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043B\u0438\u0431\u043E IP, \u043B\u0438\u0431\u043E CNAME-\u0446\u0435\u043B\u044C", path: ["target_cname"] }); } }); var createServiceSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435"), slug: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 slug") }); var createServiceWithConfigSchema = createServiceSchema.extend({ service_group_id: z.number().nullable().optional(), ips: z.array(ipv4Schema).default([]), lb_weight: z.number().int().min(1).max(100).optional(), lb_priority: z.number().int().min(1).max(100).optional(), domains: z.array(serviceDomainInputSchema).default([]) }); var createServiceBindingSchema = z.object({ domain_id: z.string().min(1, "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u043E\u043C\u0435\u043D"), service_id: z.string().min(1, "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441"), hostname: z.string().optional(), target_ip: z.string().optional() }); var updateDomainGroupSchema = z.object({ group_id: z.number().nullable(), status: z.string().optional() }); var createDomainSchema = z.object({ zone_name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0438\u043C\u044F \u0437\u043E\u043D\u044B"), group_id: z.string() }); var loginSchema = z.object({ username: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F"), password: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C") }); var createDnsRecordSchema = z.object({ record_type: z.enum(["A", "AAAA", "CNAME", "TXT", "MX"]), name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0438\u043C\u044F"), content: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"), ttl: z.number().int().min(1), proxied: z.boolean() }); var subdomainSchema = z.object({ id: z.number(), domain_id: z.number(), name: z.string(), fqdn: z.string(), enabled: z.boolean(), cert_monitoring: certMonitoringSchema.default("auto"), created_at: z.string(), updated_at: z.string() }); var createSubdomainSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0438\u043C\u044F \u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D\u0430") }); var updateSubdomainSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0438\u043C\u044F \u043F\u043E\u0434\u0434\u043E\u043C\u0435\u043D\u0430").optional(), enabled: z.boolean().optional(), cert_monitoring: certMonitoringSchema.optional() }); var updateDomainSchema = z.object({ group_id: z.number().nullable().optional(), status: z.string().optional(), cert_monitoring: certMonitoringSchema.optional(), environment: domainEnvironmentSchema.nullable().optional(), tags: z.array(z.string().min(1).max(64)).max(20).optional() }); var bulkUpdateDomainsSchema = z.object({ ids: z.array(z.number().int().positive()).min(1), group_id: z.number().nullable().optional(), environment: domainEnvironmentSchema.nullable().optional(), tags_add: z.array(z.string().min(1).max(64)).max(20).optional() }); var domainMonitorSchema = z.object({ id: z.number(), domain_id: z.number(), hostname: z.string(), type: domainMonitorTypeSchema, enabled: z.coerce.boolean(), interval_sec: z.number(), timeout_ms: z.number(), path: z.string().nullable(), expected_status: z.number().nullable(), last_status: ipHealthStateSchema.default("unknown"), last_latency_ms: z.number().nullable(), last_checked_at: z.string().nullable(), last_error: z.string().nullable(), created_at: z.string(), updated_at: z.string() }); var createDomainMonitorSchema = z.object({ hostname: z.string().min(1), type: domainMonitorTypeSchema, enabled: z.boolean().optional().default(true), interval_sec: z.number().int().min(10).max(3600).optional().default(60), timeout_ms: z.number().int().min(500).max(3e4).optional().default(5e3), path: z.string().nullable().optional(), expected_status: z.number().int().min(100).max(599).nullable().optional() }); var domainMonitorResultSchema = z.object({ id: z.number(), monitor_id: z.number(), status: ipHealthStateSchema, latency_ms: z.number().nullable(), error: z.string().nullable(), checked_at: z.string() }); var notificationLogSchema = z.object({ id: z.number(), kind: z.string(), ref_type: z.string(), ref_id: z.number().nullable(), title: z.string(), message: z.string(), created_at: z.string() }); var updateServiceConfigSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435").optional(), slug: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 slug").optional(), service_group_id: z.number().nullable().optional(), ips: z.array(ipv4Schema).optional(), lb_weight: z.number().int().min(1).max(100).optional(), lb_priority: z.number().int().min(1).max(100).optional(), domains: z.array(serviceDomainInputSchema).optional() }); var createServiceGroupSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435"), type: serviceGroupTypeSchema.default("custom"), icon: z.string().nullable().optional(), domain: z.string().nullable().optional(), lb_mode: lbModeSchema.optional(), ...healthCheckConfigFields }); var updateServiceGroupSchema = z.object({ name: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435").optional(), type: serviceGroupTypeSchema.optional(), icon: z.string().nullable().optional(), domain: z.string().nullable().optional(), lb_mode: lbModeSchema.optional(), ...healthCheckConfigFields }); var toggleEnabledSchema = z.object({ enabled: z.boolean() }); var toggleServiceIpSchema = z.object({ ip: ipv4Schema, enabled: z.boolean() }); var reorderServicesSchema = z.object({ group_id: z.union([z.number(), z.null()]).optional().default(null), service_ids: z.array(z.number().int().positive()).min(1) }); var healthStatusQuerySchema = z.object({ scope: healthCheckScopeSchema, ref_id: z.coerce.number().int().positive() }); var serviceNodeSchema = z.object({ id: z.number(), service_id: z.number(), address: z.string(), protocol: z.string(), port: z.number().nullable(), enabled: z.coerce.boolean(), priority: z.number(), weight: z.number(), health_status: nodeHealthStateSchema, health_check_id: z.number().nullable(), consecutive_failures: z.number(), consecutive_successes: z.number(), last_check_at: z.string().nullable(), last_failure_reason: z.string().nullable(), created_at: z.string(), updated_at: z.string() }); var createServiceNodeSchema = z.object({ address: nodeAddressSchema, protocol: z.enum(["tcp", "http", "https"]).optional().default("tcp"), port: z.number().int().min(1).max(65535).nullable().optional(), enabled: z.boolean().optional().default(true), priority: z.number().int().min(1).max(100).optional().default(1), weight: z.number().int().min(1).max(100).optional().default(1), health_check_id: z.number().int().positive().nullable().optional() }); var updateServiceNodeSchema = createServiceNodeSchema.partial(); var originHealthCheckSchema = z.object({ id: z.number(), provider: healthCheckProviderSchema, cf_healthcheck_id: z.string().nullable(), cf_zone_id: z.string().nullable(), name: z.string(), protocol: z.string(), path: z.string().nullable(), method: z.string().nullable(), timeout: z.number(), interval_sec: z.number(), retries: z.number(), expected_status: z.number().nullable(), consecutive_fails: z.number(), consecutive_successes: z.number(), suspended: z.coerce.boolean(), created_at: z.string(), updated_at: z.string() }); var createOriginHealthCheckSchema = z.object({ provider: healthCheckProviderSchema, name: z.string().min(1).max(64), cf_zone_id: z.string().nullable().optional(), protocol: z.enum(["HTTP", "HTTPS", "TCP", "tcp", "http", "https"]).optional(), path: z.string().nullable().optional(), method: z.string().nullable().optional(), timeout: z.number().int().min(1).max(60).optional(), interval_sec: z.number().int().min(5).max(3600).optional(), retries: z.number().int().min(0).max(10).optional(), expected_status: z.number().int().min(100).max(599).nullable().optional(), consecutive_fails: z.number().int().min(1).max(20).optional(), consecutive_successes: z.number().int().min(1).max(20).optional(), suspended: z.boolean().optional(), node_id: z.number().int().positive().optional() }); var changeIpSchema = z.object({ from_ip: z.string().optional(), to_ip: z.string().min(1).optional(), node_id: z.number().int().positive().optional(), dry_run: z.boolean().optional().default(false) }); var changeDomainSchema = z.object({ from_domain_id: z.number().int().positive(), to_domain_id: z.number().int().positive(), hostnames: z.array(z.string().min(1)).optional(), dry_run: z.boolean().optional().default(false) }); // src/app-switcher.ts import { z as z2 } from "zod"; var appSwitcherIconSchema = z2.enum([ "server", "cloud", "globe", "dashboard", "chart" ]); var appSwitcherEntrySchema = z2.object({ id: z2.string(), name: z2.string(), subtitle: z2.string().optional(), url: z2.string().url(), icon: appSwitcherIconSchema.default("server"), enabled: z2.boolean().optional(), sort: z2.number().optional(), shortcut: z2.string().optional() }); var appSwitcherConfigSchema = z2.object({ menuLabel: z2.string().default("\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F"), apps: z2.array(appSwitcherEntrySchema).min(1) }); // src/integration-vps-tracker.ts import { z as z3 } from "zod"; var cfdmBindingSyncItemSchema = z3.object({ bindingId: z3.number().int().positive(), serviceId: z3.number().int().positive(), serviceName: z3.string().min(1), serviceSlug: z3.string().min(1), fqdn: z3.string().min(1), zoneName: z3.string().min(1), hostname: z3.string(), ips: z3.array(z3.string()), /** CNAME-цель (FQDN), если binding — CNAME; для матчинга в VPS Tracker. */ cnameTarget: z3.string().optional(), /** HA-режим binding (fallback — service group). Optional для старых payload. */ lbMode: z3.enum(["round_robin", "failover", "weighted"]).optional(), deleted: z3.boolean().optional() }); var cfdmSyncBindingsBodySchema = z3.object({ bindings: z3.array(cfdmBindingSyncItemSchema), /** Полная пересинхронизация: удалить CFDM-домены, которых нет в payload. */ fullSync: z3.boolean().optional() }).refine((data) => data.fullSync === true || data.bindings.length >= 1, { message: "bindings \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D, \u0435\u0441\u043B\u0438 fullSync \u043D\u0435 \u0437\u0430\u0434\u0430\u043D", path: ["bindings"] }); var appSettingsPatchSchema = z3.object({ vpsTrackerUrl: z3.string().url().or(z3.literal("")).optional(), vpsTrackerIntegrationToken: z3.string().optional(), vpsTrackerSyncEnabled: z3.boolean().optional(), showQuickActions: z3.boolean().optional(), healthCheckCron: z3.string().trim().min(1).max(64).optional(), healthDegradedFailures: z3.number().int().min(1).max(20).optional(), healthDownFailures: z3.number().int().min(1).max(50).optional(), healthLatencyWarnMs: z3.number().int().min(50).max(6e4).optional(), healthSuccessRecoveries: z3.number().int().min(1).max(20).optional(), healthWorkerUrl: z3.string().url().or(z3.literal("")).optional(), healthWorkerToken: z3.string().optional(), globalpingToken: z3.string().optional(), globalpingLocations: z3.string().trim().max(200).optional(), globalpingLimit: z3.number().int().min(1).max(10).optional() }).superRefine((data, ctx) => { if (data.healthDegradedFailures != null && data.healthDownFailures != null && data.healthDownFailures < data.healthDegradedFailures) { ctx.addIssue({ code: "custom", message: "\u043E\u0448\u0438\u0431\u043E\u043A \u0434\u043E down \u043D\u0435 \u043C\u0435\u043D\u044C\u0448\u0435, \u0447\u0435\u043C \u0434\u043E degraded", path: ["healthDownFailures"] }); } }); var vpsTrackerEventSchema = z3.object({ event: z3.enum(["vps_down", "vps_up"]), vps: z3.array( z3.object({ id: z3.string().min(1), ip: z3.string().optional(), label: z3.string().optional() }) ), timestamp: z3.string().datetime().optional() }); // src/health-probe-mailbox.ts var HEALTH_PROBE_SCRIPT_NAME = "cfdm-health-probe"; var HEALTH_PROBE_KV_TITLE = "cfdm-health-probe"; var HEALTH_KV_TARGETS_KEY = "targets"; var HEALTH_KV_RESULTS_KEY = "results"; var HEALTH_KV_CURSOR_KEY = "cursor"; var HEALTH_PROBE_BATCH = 48; var HEALTH_PROBE_CONCURRENCY = 5; // src/audit.ts import { z as z4 } from "zod"; var AUDIT_SEVERITIES = ["info", "warning", "critical"]; var auditSeveritySchema = z4.enum(AUDIT_SEVERITIES); var AUDIT_SOURCE_APPS = [ "portal", "vps", "cfdm", "bgp", "fw" ]; var auditSourceAppSchema = z4.enum(AUDIT_SOURCE_APPS); var AUDIT_TARGET_TYPES = [ "user", "settings", "session", "system", "app_resource" ]; var auditTargetTypeSchema = z4.enum(AUDIT_TARGET_TYPES); var auditLogEntrySchema = z4.object({ id: z4.string(), event_id: z4.string().nullable(), source_app: auditSourceAppSchema, action: z4.string(), severity: auditSeveritySchema, actor_user_id: z4.string().nullable(), actor_email: z4.string().nullable(), actor_name: z4.string().nullable(), target_type: auditTargetTypeSchema.nullable(), target_id: z4.string().nullable(), summary: z4.string(), details: z4.record(z4.string(), z4.unknown()).nullable(), ip: z4.string().nullable(), created_at: z4.string() }); var auditListQuerySchema = z4.object({ action: z4.string().optional(), severity: auditSeveritySchema.optional(), user_id: z4.string().optional(), source_app: auditSourceAppSchema.optional(), limit: z4.coerce.number().int().min(1).max(500).default(200) }); var ingestAuditEventSchema = z4.object({ event_id: z4.string().min(1).max(128), source_app: z4.enum(["vps", "cfdm", "bgp", "fw"]), action: z4.string().min(1).max(200), severity: auditSeveritySchema.optional(), actor_user_id: z4.string().nullable().optional(), actor_email: z4.string().email().nullable().optional(), actor_name: z4.string().nullable().optional(), target_type: auditTargetTypeSchema.nullable().optional(), target_id: z4.string().nullable().optional(), summary: z4.string().min(1).max(500), details: z4.record(z4.string(), z4.unknown()).nullable().optional(), ip: z4.string().nullable().optional(), created_at: z4.string().optional() }); export { AUDIT_SEVERITIES, AUDIT_SOURCE_APPS, AUDIT_TARGET_TYPES, CERT_ERROR, CERT_EXPIRED, CERT_MONITORING_VALUES, CERT_MONITOR_AUTO, CERT_MONITOR_REQUIRED, CERT_MONITOR_SKIPPED, CERT_OK, CERT_UNKNOWN, CERT_WARNING, HEALTH_CHECK_AGGREGATES, HEALTH_CHECK_PROVIDERS, HEALTH_KV_CURSOR_KEY, HEALTH_KV_RESULTS_KEY, HEALTH_KV_TARGETS_KEY, HEALTH_PROBE_BATCH, HEALTH_PROBE_CONCURRENCY, HEALTH_PROBE_KV_TITLE, HEALTH_PROBE_SCRIPT_NAME, HEALTH_STATUS_PROVIDERS, SYNC_CONFLICT, SYNC_ERROR, SYNC_PENDING_DELETE, SYNC_PENDING_PUSH, SYNC_SYNCED, ValidationError, aggregateHealthOk, appSettingsPatchSchema, appSwitcherConfigSchema, appSwitcherEntrySchema, appSwitcherIconSchema, auditListQuerySchema, auditLogEntrySchema, auditSeveritySchema, auditSourceAppSchema, auditTargetTypeSchema, bindingToFqdn, bulkUpdateDomainsSchema, certMonitoringSchema, certStatusFromExpiry, certificateSchema, cfdmBindingSyncItemSchema, cfdmSyncBindingsBodySchema, changeDomainSchema, changeIpSchema, clampGlobalpingLimit, createDnsRecordSchema, createDomainMonitorSchema, createDomainSchema, createGroupSchema, createOriginHealthCheckSchema, createServiceBindingSchema, createServiceGroupSchema, createServiceNodeSchema, createServiceSchema, createServiceWithConfigSchema, createSubdomainSchema, derivePrimaryProvider, dnsNameToSubdomainLabel, dnsRecordNamesMatch, dnsRecordSchema, domainEnvironmentSchema, domainListItemSchema, domainMonitorResultSchema, domainMonitorSchema, domainMonitorTypeSchema, domainSchema, failoverLogSchema, fqdnToDisplay, groupSchema, groupWithStatsSchema, healthCheckAggregateSchema, healthCheckConfigSchema, healthCheckProviderSchema, healthCheckProvidersSchema, healthCheckScopeSchema, healthCheckTypeSchema, healthProbeLogSchema, healthStatusProviderSchema, healthStatusQuerySchema, ingestAuditEventSchema, ipHealthStateSchema, ipHealthStatusSchema, isIpLiteral, isValidIpv4, lbModeSchema, loginSchema, nodeHealthStateSchema, normalizeDnsRecordName, normalizeProbeProvider, normalizeStatusProvider, notificationLogSchema, originHealthCheckSchema, parseFqdn, parseGlobalpingLocations, parseHealthAggregate, parseHealthProviders, reorderServicesSchema, serializeHealthProviders, serviceBindingSchema, serviceCertificateRowSchema, serviceDomainBindingSchema, serviceGroupSchema, serviceGroupTypeSchema, serviceGroupViewSchema, serviceGroupsResponseSchema, serviceIpHealthSchema, serviceNodeSchema, serviceSchema, serviceViewSchema, shouldMonitorService, subdomainLabelToFqdn, subdomainSchema, targetHasProvider, targetProviders, toggleEnabledSchema, toggleServiceIpSchema, uniqueHealthProviders, updateDomainGroupSchema, updateDomainSchema, updateServiceConfigSchema, updateServiceGroupSchema, updateServiceNodeSchema, updateSubdomainSchema, validateDnsRecord, vpsTrackerEventSchema };