// 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"; // src/validators.ts var NAME_RE = /^(@|\*|[a-zA-Z0-9_]([a-zA-Z0-9_-]*[a-zA-Z0-9_])?(\.[a-zA-Z0-9_]([a-zA-Z0-9_-]*[a-zA-Z0-9_])?)*)$/; 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 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; }); } // 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.includes(".")) return rn; return null; } function subdomainLabelToFqdn(label, zoneName) { return label === "@" ? zoneName : `${label}.${zoneName}`; } // 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"; 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(), domain: z.string().nullable(), enabled: z.boolean(), 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.boolean().optional(), computed_fqdn: z.string().nullable().optional(), 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(), target_ips: z.array(z.string()).optional(), target_ip: z.string().nullable().optional(), sync_status: z.string().nullable() }).transform((binding) => ({ ...binding, target_ips: binding.target_ips && binding.target_ips.length > 0 ? binding.target_ips : binding.target_ip ? [binding.target_ip] : [] })); var serviceViewSchema = serviceSchema.extend({ subdomain: z.string().default(""), enabled: z.boolean().default(false), ips: z.array(z.string()).default([]), domains: z.array(serviceDomainBindingSchema).default([]) }); var serviceGroupViewSchema = serviceGroupSchema.extend({ services: z.array(serviceViewSchema).default([]) }); 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(), 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() }); 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(), sync_status: z.string().nullable(), created_at: z.string(), updated_at: z.string() }); 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(), 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 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 serviceDomainInputSchema = z.object({ fqdn: z.string().min(1, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 FQDN"), target_ips: z.array(ipv4Schema).min(1, "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0445\u043E\u0442\u044F \u0431\u044B \u043E\u0434\u0438\u043D IP") }); 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([]), 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(), created_at: z.string(), updated_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(), 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() }); var toggleEnabledSchema = z.object({ enabled: z.boolean() }); export { CERT_ERROR, CERT_EXPIRED, CERT_OK, CERT_UNKNOWN, CERT_WARNING, SYNC_CONFLICT, SYNC_ERROR, SYNC_PENDING_DELETE, SYNC_PENDING_PUSH, SYNC_SYNCED, ValidationError, bindingToFqdn, certStatusFromExpiry, certificateSchema, createDnsRecordSchema, createDomainSchema, createGroupSchema, createServiceBindingSchema, createServiceGroupSchema, createServiceSchema, createServiceWithConfigSchema, dnsNameToSubdomainLabel, dnsRecordSchema, domainListItemSchema, domainSchema, fqdnToDisplay, groupSchema, groupWithStatsSchema, isValidIpv4, loginSchema, parseFqdn, serviceBindingSchema, serviceDomainBindingSchema, serviceGroupSchema, serviceGroupTypeSchema, serviceGroupViewSchema, serviceGroupsResponseSchema, serviceSchema, serviceViewSchema, subdomainLabelToFqdn, subdomainSchema, toggleEnabledSchema, updateDomainGroupSchema, updateServiceConfigSchema, validateDnsRecord };