From 5d84c7bf6c7d104416825afe412249f015868485 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 20 Aug 2026 01:03:00 +0700 Subject: [PATCH] =?UTF-8?q?fix(services):=20=D1=81=D0=BE=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D1=8C=20=D0=B8=D1=81=D1=82=D0=BE=D1=87=D0=BD?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=20health-check=20=D0=B8=20=D0=B0=D0=B3=D1=80?= =?UTF-8?q?=D0=B5=D0=B3=D0=B0=D1=86=D0=B8=D1=8E=20=D0=B2=20=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B5=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET отдавал sqlite-типы (0/1 и JSON-строка), Zod отклонял PATCH; форма сбрасывалась на refetch. Co-authored-by: Cursor --- apps/api/test/services-create-list.test.ts | 85 ++++++++++- .../components/health-check-config-fields.tsx | 10 +- .../reui-kit/health-source-tiles.tsx | 4 +- .../web/src/components/service-edit-sheet.tsx | 17 ++- apps/web/src/lib/schemas.ts | 4 +- packages/db/dist/index.d.ts | 75 ++++++++- packages/db/dist/index.js | 69 +++++++-- packages/db/src/repos.ts | 10 +- packages/shared/dist/index.d.ts | 142 +++++++++++++----- packages/shared/dist/index.js | 35 ++++- packages/shared/src/schemas.ts | 18 ++- 11 files changed, 385 insertions(+), 84 deletions(-) diff --git a/apps/api/test/services-create-list.test.ts b/apps/api/test/services-create-list.test.ts index 522ad46..7f46aa5 100644 --- a/apps/api/test/services-create-list.test.ts +++ b/apps/api/test/services-create-list.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { serviceGroupsResponseSchema } from "@cfdm/shared"; +import { serviceGroupsResponseSchema, updateServiceConfigSchema } from "@cfdm/shared"; import { repos } from "@cfdm/db"; import type { CloudflareClient } from "../src/lib/cf-client.js"; import { buildApp } from "../src/app.js"; @@ -51,6 +51,26 @@ async function authHeaders(app: Awaited>) { } describe("create service then list groups", () => { + it("accepts sqlite-shaped health fields on service config PATCH", () => { + const parsed = updateServiceConfigSchema.parse({ + domains: [ + { + fqdn: "gw.example.com", + target_ips: ["1.2.3.4"], + health_check_enabled: 1, + health_check_verify_tls: 0, + health_check_providers: '["local","cloudflare"]', + health_check_aggregate: "majority", + }, + ], + }); + expect(parsed.domains?.[0]?.health_check_enabled).toBe(true); + expect(parsed.domains?.[0]?.health_check_verify_tls).toBe(false); + expect(parsed.domains?.[0]?.health_check_providers).toEqual([ + "local", + "cloudflare", + ]); + }); it("create + updateConfig then listGroupViews parses with shared Zod schema", async () => { const app = await buildApp({ config: { ...loadConfig(), staticDir: null }, @@ -133,6 +153,69 @@ describe("create service then list groups", () => { await app.close(); }); + it("PATCH /services/:id persists health providers and aggregate", async () => { + const app = await buildApp({ + config: { ...loadConfig(), staticDir: null }, + memory: true, + }); + const headers = await authHeaders(app); + const cf = mockCf(); + + repos.createDomain(app.db, null, "example.com", "zone-1"); + const createRes = await app.inject({ + method: "POST", + url: "/api/v1/services", + headers, + payload: { name: "GW", slug: "gw" }, + }); + expect(createRes.statusCode).toBe(200); + const created = createRes.json() as { id: number }; + + await updateConfig(app.db, cf, created.id, { + ips: ["1.2.3.4"], + domains: [ + { + fqdn: "gw.example.com", + target_ips: ["1.2.3.4"], + health_check_enabled: true, + health_check_type: "tcp", + health_check_interval_sec: 30, + health_check_timeout_ms: 3000, + health_check_providers: ["local", "cloudflare"], + health_check_aggregate: "majority", + }, + ], + }); + + const stored = repos.listBindingsByService(app.db, created.id)[0]!; + expect(stored.health_check_enabled).toBe(true); + expect(Array.isArray(stored.health_check_providers)).toBe(true); + expect(stored.health_check_providers).toEqual(["local", "cloudflare"]); + expect(stored.health_check_aggregate).toBe("majority"); + + const getRes = await app.inject({ + method: "GET", + url: `/api/v1/services/${created.id}`, + headers, + }); + expect(getRes.statusCode).toBe(200); + const view = getRes.json() as { + domains: Array<{ + health_check_enabled: boolean; + health_check_providers: string[]; + health_check_aggregate: string; + }>; + }; + expect(view.domains[0]?.health_check_enabled).toBe(true); + expect(view.domains[0]?.health_check_providers).toEqual([ + "local", + "cloudflare", + ]); + expect(view.domains[0]?.health_check_aggregate).toBe("majority"); + + await app.close(); + }); + it("POST /services returns resolved ServiceView with numeric id", async () => { const app = await buildApp({ config: { ...loadConfig(), staticDir: null }, diff --git a/apps/web/src/components/health-check-config-fields.tsx b/apps/web/src/components/health-check-config-fields.tsx index 806d473..9fd35d0 100644 --- a/apps/web/src/components/health-check-config-fields.tsx +++ b/apps/web/src/components/health-check-config-fields.tsx @@ -28,7 +28,7 @@ import { type HealthAggregate, type HealthProvider, } from '@/components/reui-kit/health-source-tiles' -import { uniqueHealthProviders } from '@cfdm/shared' +import { parseHealthProviders } from '@cfdm/shared' export type LbMode = 'round_robin' | 'failover' | 'weighted' export type HealthCheckType = 'tcp' | 'http' @@ -116,10 +116,10 @@ export function HealthCheckConfigFields({ onChange({ ...value, ...next }) } - const providers = - value.providers?.length > 0 - ? uniqueHealthProviders(value.providers) - : uniqueHealthProviders([value.provider ?? 'local']) + const providers = parseHealthProviders( + value.providers, + value.provider ?? 'local', + ) const aggregate = value.aggregate ?? 'majority' const isHttp = value.type === 'http' const rowClass = 'gap-3 px-0 py-3' diff --git a/apps/web/src/components/reui-kit/health-source-tiles.tsx b/apps/web/src/components/reui-kit/health-source-tiles.tsx index 2da1ad0..b497fee 100644 --- a/apps/web/src/components/reui-kit/health-source-tiles.tsx +++ b/apps/web/src/components/reui-kit/health-source-tiles.tsx @@ -14,7 +14,7 @@ import { } from '@cfdm/ui/components/item' import { HealthCheckBadge } from '@/components/health-check-badge' import { cn } from '@cfdm/ui/lib/utils' -import type { HealthCheckAggregate, HealthCheckProvider } from '@cfdm/shared' +import { parseHealthProviders, type HealthCheckAggregate, type HealthCheckProvider } from '@cfdm/shared' import type { HealthLogStatus } from '@/lib/health-log' export type HealthProvider = HealthCheckProvider @@ -189,7 +189,7 @@ export function HealthSourceTiles({ value: HealthProvider[] onChange: (next: HealthProvider[]) => void }) { - const selected = value.length > 0 ? value : (['local'] as HealthProvider[]) + const selected = parseHealthProviders(value) function toggle(id: HealthProvider) { if (selected.includes(id)) { diff --git a/apps/web/src/components/service-edit-sheet.tsx b/apps/web/src/components/service-edit-sheet.tsx index 1a9d4af..53deaed 100644 --- a/apps/web/src/components/service-edit-sheet.tsx +++ b/apps/web/src/components/service-edit-sheet.tsx @@ -18,6 +18,7 @@ import type { ServiceView, UpdateServiceConfigInput, } from '@/lib/schemas' +import { parseHealthProviders } from '@cfdm/shared' import { bindingToFqdn, parseFqdn } from '@/lib/parse-fqdn' import { Badge } from '@/components/reui/badge' import { toast } from 'sonner' @@ -108,19 +109,19 @@ function toBindingDrafts(service: ServiceView): ServiceBindingDraft[] { target_cname: binding.target_cname ?? '', lb_mode: binding.lb_mode, health: { - enabled: binding.health_check_enabled, + enabled: Boolean(binding.health_check_enabled), type: binding.health_check_type === 'http' ? 'http' : 'tcp', port: binding.health_check_port, path: binding.health_check_path, expected_status: binding.health_check_expected_status, interval_sec: binding.health_check_interval_sec, timeout_ms: binding.health_check_timeout_ms, - verify_tls: binding.health_check_verify_tls ?? false, + verify_tls: Boolean(binding.health_check_verify_tls), provider: binding.health_check_provider ?? 'local', - providers: - binding.health_check_providers?.length > 0 - ? binding.health_check_providers - : [binding.health_check_provider ?? 'local'], + providers: parseHealthProviders( + binding.health_check_providers, + binding.health_check_provider ?? 'local', + ), aggregate: binding.health_check_aggregate ?? 'majority', }, target_ip_weights: binding.target_ip_weights ?? {}, @@ -232,6 +233,8 @@ export function ServiceEditSheet({ [groups], ) + // Reset only when the sheet opens or the service id changes. + // Health polling replaces `service` by identity and would wipe unsaved settings. useEffect(() => { if (!open) return if (mode === 'edit' && service) { @@ -260,7 +263,7 @@ export function ServiceEditSheet({ setLbWeight(1) setLbPriority(1) } - }, [open, mode, service, defaultGroupId]) + }, [open, mode, service?.id, defaultGroupId]) const zoneHints = useMemo( () => knownDomains.map((domain) => domain.zone_name), diff --git a/apps/web/src/lib/schemas.ts b/apps/web/src/lib/schemas.ts index a5548e9..9efa6e0 100644 --- a/apps/web/src/lib/schemas.ts +++ b/apps/web/src/lib/schemas.ts @@ -291,14 +291,14 @@ const lbModeSchema = z.enum(['round_robin', 'failover', 'weighted']) const healthCheckTypeSchema = z.enum(['tcp', 'http', 'ping', 'dns']) const healthCheckConfigFields = { - health_check_enabled: z.boolean().optional(), + 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(30000).optional(), - health_check_verify_tls: z.boolean().optional(), + health_check_verify_tls: z.coerce.boolean().optional(), health_check_provider: z.enum(['local', 'cloudflare', 'globalping']).optional(), health_check_providers: z.array(z.enum(['local', 'cloudflare', 'globalping'])).min(1).optional(), health_check_aggregate: z.enum(['any', 'all', 'majority']).optional(), diff --git a/packages/db/dist/index.d.ts b/packages/db/dist/index.d.ts index fd8d9d9..203ab6c 100644 --- a/packages/db/dist/index.d.ts +++ b/packages/db/dist/index.d.ts @@ -1613,6 +1613,25 @@ declare const serviceBindings: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{ }, {}, { length: number | undefined; }>; + cert_monitoring: drizzle_orm_sqlite_core.SQLiteColumn<{ + name: "cert_monitoring"; + tableName: "service_bindings"; + dataType: "string"; + columnType: "SQLiteText"; + data: string; + driverParam: string; + notNull: true; + hasDefault: true; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; + enumValues: [string, ...string[]]; + baseColumn: never; + identity: undefined; + generated: undefined; + }, {}, { + length: number | undefined; + }>; routing_strategy: drizzle_orm_sqlite_core.SQLiteColumn<{ name: "routing_strategy"; tableName: "service_bindings"; @@ -2682,6 +2701,23 @@ declare const certificates: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{ identity: undefined; generated: undefined; }, {}, {}>; + service_id: drizzle_orm_sqlite_core.SQLiteColumn<{ + name: "service_id"; + tableName: "certificates"; + dataType: "number"; + columnType: "SQLiteInteger"; + data: number; + driverParam: number; + notNull: false; + hasDefault: false; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; + enumValues: undefined; + baseColumn: never; + identity: undefined; + generated: undefined; + }, {}, {}>; hostname: drizzle_orm_sqlite_core.SQLiteColumn<{ name: "hostname"; tableName: "certificates"; @@ -6296,6 +6332,25 @@ declare const schema: { }, {}, { length: number | undefined; }>; + cert_monitoring: drizzle_orm_sqlite_core.SQLiteColumn<{ + name: "cert_monitoring"; + tableName: "service_bindings"; + dataType: "string"; + columnType: "SQLiteText"; + data: string; + driverParam: string; + notNull: true; + hasDefault: true; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; + enumValues: [string, ...string[]]; + baseColumn: never; + identity: undefined; + generated: undefined; + }, {}, { + length: number | undefined; + }>; routing_strategy: drizzle_orm_sqlite_core.SQLiteColumn<{ name: "routing_strategy"; tableName: "service_bindings"; @@ -7365,6 +7420,23 @@ declare const schema: { identity: undefined; generated: undefined; }, {}, {}>; + service_id: drizzle_orm_sqlite_core.SQLiteColumn<{ + name: "service_id"; + tableName: "certificates"; + dataType: "number"; + columnType: "SQLiteInteger"; + data: number; + driverParam: number; + notNull: false; + hasDefault: false; + isPrimaryKey: false; + isAutoincrement: false; + hasRuntimeDefault: false; + enumValues: undefined; + baseColumn: never; + identity: undefined; + generated: undefined; + }, {}, {}>; hostname: drizzle_orm_sqlite_core.SQLiteColumn<{ name: "hostname"; tableName: "certificates"; @@ -9677,6 +9749,7 @@ interface BindingLbPatch { health_check_provider?: HealthCheckProvider; health_check_providers?: HealthCheckProvider[]; health_check_aggregate?: HealthCheckAggregate; + cert_monitoring?: string; } declare function updateBindingLbConfig(db: Db, bindingId: number, patch: BindingLbPatch): void; declare function setBindingCnameTarget(db: Db, bindingId: number, target: string | null): void; @@ -9702,7 +9775,7 @@ declare function deleteBindingsExcept(db: Db, serviceId: number, keepIds: number declare function deleteBinding(db: Db, id: number): void; declare function listCertificates(db: Db, status?: string): Certificate[]; declare function getCertificate(db: Db, id: number): Certificate; -declare function upsertCertificateCheck(db: Db, domainId: number, subdomainId: number | null, hostname: string, expiresAt: string | null, status: string, lastError: string | null): Certificate; +declare function upsertCertificateCheck(db: Db, domainId: number, subdomainId: number | null, hostname: string, expiresAt: string | null, status: string, lastError: string | null, serviceId?: number | null): Certificate; declare function countCertificatesByStatus(db: Db): Array<[string, number]>; declare function deleteCertificatesNotIn(db: Db, hostnames: string[]): number; declare function createSyncJob(db: Db, id: string, domainId: number | null): void; diff --git a/packages/db/dist/index.js b/packages/db/dist/index.js index 67f6ec2..4de7e09 100644 --- a/packages/db/dist/index.js +++ b/packages/db/dist/index.js @@ -123,6 +123,7 @@ var serviceBindings = sqliteTable( health_check_provider: text("health_check_provider").notNull().default("local"), health_check_providers: text("health_check_providers").notNull().default('["local"]'), health_check_aggregate: text("health_check_aggregate").notNull().default("majority"), + cert_monitoring: text("cert_monitoring").notNull().default("auto"), routing_strategy: text("routing_strategy").notNull().default("round_robin"), operation_version: integer("operation_version").notNull().default(0), created_at: text("created_at").notNull().default(sql`datetime('now')`), @@ -228,6 +229,9 @@ var certificates = sqliteTable("certificates", { subdomain_id: integer("subdomain_id").references(() => subdomains.id, { onDelete: "set null" }), + service_id: integer("service_id").references(() => services.id, { + onDelete: "set null" + }), hostname: text("hostname").notNull().unique(), expires_at: text("expires_at"), last_checked_at: text("last_checked_at"), @@ -1221,15 +1225,16 @@ function mapServiceBinding(row) { cname_target: row.cname_target, dns_record_id: row.dns_record_id, lb_mode: row.lb_mode, - health_check_enabled: row.health_check_enabled, + health_check_enabled: Boolean(row.health_check_enabled), health_check_type: row.health_check_type, health_check_port: row.health_check_port, health_check_path: row.health_check_path, health_check_expected_status: row.health_check_expected_status, health_check_interval_sec: row.health_check_interval_sec, health_check_timeout_ms: row.health_check_timeout_ms, - health_check_verify_tls: row.health_check_verify_tls, + health_check_verify_tls: Boolean(row.health_check_verify_tls), ...mapHealthFields(row), + cert_monitoring: row.cert_monitoring ?? "auto", routing_strategy: row.routing_strategy, operation_version: row.operation_version, created_at: row.created_at, @@ -1625,6 +1630,8 @@ function updateBindingLbConfig(db, bindingId, patch) { update.health_check_timeout_ms = patch.health_check_timeout_ms; if (patch.health_check_verify_tls !== void 0) update.health_check_verify_tls = patch.health_check_verify_tls; + if (patch.cert_monitoring !== void 0) + update.cert_monitoring = patch.cert_monitoring; Object.assign(update, healthProviderColumns(patch)); db.update(serviceBindings).set(update).where(eq3(serviceBindings.id, bindingId)).run(); } @@ -1685,7 +1692,7 @@ var SERVICE_BINDING_SELECT_COLUMNS = `sb.id, sb.domain_id, sb.service_id, sb.hos sb.lb_mode, sb.health_check_enabled, sb.health_check_type, sb.health_check_port, sb.health_check_path, sb.health_check_expected_status, sb.health_check_interval_sec, sb.health_check_timeout_ms, sb.health_check_verify_tls, sb.health_check_provider, - sb.health_check_providers, sb.health_check_aggregate, sb.cname_target, + sb.health_check_providers, sb.health_check_aggregate, sb.cert_monitoring, sb.cname_target, d.zone_name, d.group_id, g.name AS group_name, s.name AS service_name, s.slug AS service_slug, dr.content AS target_ip, dr.sync_status, @@ -1733,6 +1740,7 @@ function enrichServiceBindingView(db, row) { return { ...row, cname_target: row.cname_target ?? null, + cert_monitoring: row.cert_monitoring ?? "auto", ...mapHealthFields(row), target_ips, target_ip: target_ips[0] ?? null, @@ -1765,11 +1773,15 @@ function listBindingsByDomain(db, domainId) { `).map((row) => enrichServiceBindingView(db, row)); } function listBindingsByService(db, serviceId) { - return db.all(sql2` + const rows = db.all(sql2` SELECT sb.*, d.zone_name FROM service_bindings sb JOIN domains d ON d.id = sb.domain_id WHERE sb.service_id = ${serviceId} `); + return rows.map((row) => ({ + ...mapServiceBinding(row), + zone_name: row.zone_name + })); } function getBinding(db, id) { const row = db.select().from(serviceBindings).where(eq3(serviceBindings.id, id)).get(); @@ -1839,23 +1851,57 @@ function deleteBinding(db, id) { const result = db.delete(serviceBindings).where(eq3(serviceBindings.id, id)).run(); if (result.changes === 0) throw new NotFoundError(`service binding ${id}`); } +var CERTIFICATE_SELECT = `c.id, c.domain_id, c.subdomain_id, c.service_id, c.hostname, + c.expires_at, c.last_checked_at, c.last_error, c.status, c.created_at, c.updated_at, + s.name AS service_name`; +function mapCertificate(row) { + return { + id: row.id, + domain_id: row.domain_id, + subdomain_id: row.subdomain_id, + service_id: row.service_id ?? null, + service_name: row.service_name ?? null, + hostname: row.hostname, + expires_at: row.expires_at, + last_checked_at: row.last_checked_at, + last_error: row.last_error, + status: row.status, + created_at: row.created_at, + updated_at: row.updated_at + }; +} function listCertificates(db, status) { - if (status) { - return db.select().from(certificates).where(eq3(certificates.status, status)).orderBy(asc(certificates.expires_at)).all(); - } - return db.select().from(certificates).orderBy(asc(certificates.expires_at)).all(); + const rows = status ? db.all(sql2` + SELECT ${sql2.raw(CERTIFICATE_SELECT)} + FROM certificates c + LEFT JOIN services s ON s.id = c.service_id + WHERE c.status = ${status} + ORDER BY c.expires_at ASC + `) : db.all(sql2` + SELECT ${sql2.raw(CERTIFICATE_SELECT)} + FROM certificates c + LEFT JOIN services s ON s.id = c.service_id + ORDER BY c.expires_at ASC + `); + return rows.map(mapCertificate); } function getCertificate(db, id) { - const row = db.select().from(certificates).where(eq3(certificates.id, id)).get(); + const row = db.all(sql2` + SELECT ${sql2.raw(CERTIFICATE_SELECT)} + FROM certificates c + LEFT JOIN services s ON s.id = c.service_id + WHERE c.id = ${id} + `)[0]; if (!row) throw new NotFoundError(`certificate ${id}`); - return row; + return mapCertificate(row); } -function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt, status, lastError) { +function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt, status, lastError, serviceId) { const existing = db.select().from(certificates).where(eq3(certificates.hostname, hostname)).get(); if (existing) { db.update(certificates).set({ domain_id: domainId, subdomain_id: subdomainId, + service_id: serviceId === void 0 ? existing.service_id : serviceId, expires_at: expiresAt, last_checked_at: sql2`datetime('now')`, last_error: lastError, @@ -1867,6 +1913,7 @@ function upsertCertificateCheck(db, domainId, subdomainId, hostname, expiresAt, const id = db.insert(certificates).values({ domain_id: domainId, subdomain_id: subdomainId, + service_id: serviceId ?? null, hostname, expires_at: expiresAt, last_checked_at: sql2`datetime('now')`, diff --git a/packages/db/src/repos.ts b/packages/db/src/repos.ts index 2416dc4..46381d8 100644 --- a/packages/db/src/repos.ts +++ b/packages/db/src/repos.ts @@ -842,14 +842,14 @@ function mapServiceBinding( cname_target: row.cname_target, dns_record_id: row.dns_record_id, lb_mode: row.lb_mode as LbMode, - health_check_enabled: row.health_check_enabled, + health_check_enabled: Boolean(row.health_check_enabled), health_check_type: row.health_check_type as HealthCheckType, health_check_port: row.health_check_port, health_check_path: row.health_check_path, health_check_expected_status: row.health_check_expected_status, health_check_interval_sec: row.health_check_interval_sec, health_check_timeout_ms: row.health_check_timeout_ms, - health_check_verify_tls: row.health_check_verify_tls, + health_check_verify_tls: Boolean(row.health_check_verify_tls), ...mapHealthFields(row), cert_monitoring: row.cert_monitoring ?? "auto", routing_strategy: row.routing_strategy as LbMode, @@ -1777,11 +1777,15 @@ export function listBindingsByDomain(db: Db, domainId: number): ServiceBindingVi } export function listBindingsByService(db: Db, serviceId: number): Array { - return db.all(sql` + const rows = db.all(sql` SELECT sb.*, d.zone_name FROM service_bindings sb JOIN domains d ON d.id = sb.domain_id WHERE sb.service_id = ${serviceId} `); + return rows.map((row) => ({ + ...mapServiceBinding(row), + zone_name: row.zone_name, + })); } export function getBinding(db: Db, id: number): ServiceBinding { diff --git a/packages/shared/dist/index.d.ts b/packages/shared/dist/index.d.ts index 8474482..722a9d2 100644 --- a/packages/shared/dist/index.d.ts +++ b/packages/shared/dist/index.d.ts @@ -96,6 +96,7 @@ interface ServiceBinding { health_check_provider: HealthCheckProvider; health_check_providers: HealthCheckProvider[]; health_check_aggregate: HealthCheckAggregate; + cert_monitoring: string; routing_strategy: LbMode; operation_version: number; created_at: string; @@ -129,6 +130,7 @@ interface ServiceBindingView { health_check_provider: HealthCheckProvider; health_check_providers: HealthCheckProvider[]; health_check_aggregate: HealthCheckAggregate; + cert_monitoring: string; sync_status: string | null; created_at: string; updated_at: string; @@ -156,6 +158,7 @@ interface ServiceDomainBindingView { health_check_provider: HealthCheckProvider; health_check_providers: HealthCheckProvider[]; health_check_aggregate: HealthCheckAggregate; + cert_monitoring: string; sync_status: string | null; } interface ServiceView$1 { @@ -424,11 +427,11 @@ declare const healthCheckAggregateSchema: z.ZodEnum<{ all: "all"; majority: "majority"; }>; -declare const healthCheckProvidersSchema: z.ZodPipe>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>; +}>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; declare const healthCheckScopeSchema: z.ZodEnum<{ binding: "binding"; group: "group"; @@ -560,11 +563,11 @@ declare const serviceGroupSchema: z.ZodObject<{ cloudflare: "cloudflare"; globalping: "globalping"; }>>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; + cert_monitoring: z.ZodDefault>; sync_status: z.ZodDefault>; }, z.core.$strip>, z.ZodTransform<{ target_ips: string[]; @@ -658,6 +666,7 @@ declare const serviceDomainBindingSchema: z.ZodPipe>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; + cert_monitoring: z.ZodDefault>; sync_status: z.ZodDefault>; }, z.core.$strip>, z.ZodTransform<{ target_ips: string[]; @@ -771,6 +786,7 @@ declare const serviceViewSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ip?: string | null | undefined; }, { @@ -792,6 +808,7 @@ declare const serviceViewSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ips?: string[] | undefined; target_ip?: string | null | undefined; @@ -869,11 +886,11 @@ declare const serviceGroupViewSchema: z.ZodObject<{ cloudflare: "cloudflare"; globalping: "globalping"; }>>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; + cert_monitoring: z.ZodDefault>; sync_status: z.ZodDefault>; }, z.core.$strip>, z.ZodTransform<{ target_ips: string[]; @@ -966,6 +988,7 @@ declare const serviceGroupViewSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ip?: string | null | undefined; }, { @@ -987,6 +1010,7 @@ declare const serviceGroupViewSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ips?: string[] | undefined; target_ip?: string | null | undefined; @@ -1073,11 +1097,11 @@ declare const serviceGroupsResponseSchema: z.ZodObject<{ cloudflare: "cloudflare"; globalping: "globalping"; }>>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; + cert_monitoring: z.ZodDefault>; sync_status: z.ZodDefault>; }, z.core.$strip>, z.ZodTransform<{ target_ips: string[]; @@ -1170,6 +1199,7 @@ declare const serviceGroupsResponseSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ip?: string | null | undefined; }, { @@ -1191,6 +1221,7 @@ declare const serviceGroupsResponseSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ips?: string[] | undefined; target_ip?: string | null | undefined; @@ -1291,16 +1322,21 @@ declare const serviceGroupsResponseSchema: z.ZodObject<{ cloudflare: "cloudflare"; globalping: "globalping"; }>>; - health_check_providers: z.ZodCatch>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodCatch>; + cert_monitoring: z.ZodDefault>; sync_status: z.ZodDefault>; }, z.core.$strip>, z.ZodTransform<{ target_ips: string[]; @@ -1325,6 +1361,7 @@ declare const serviceGroupsResponseSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ip?: string | null | undefined; }, { @@ -1346,6 +1383,7 @@ declare const serviceGroupsResponseSchema: z.ZodObject<{ health_check_provider: "local" | "cloudflare" | "globalping"; health_check_providers: ("local" | "cloudflare" | "globalping")[] | readonly ["local"]; health_check_aggregate: "any" | "all" | "majority"; + cert_monitoring: "auto" | "required" | "skipped"; sync_status: string | null; target_ips?: string[] | undefined; target_ip?: string | null | undefined; @@ -1471,6 +1509,11 @@ declare const serviceBindingSchema: z.ZodPipe; health_check_timeout_ms: z.ZodDefault; health_check_verify_tls: z.ZodDefault>; + cert_monitoring: z.ZodDefault>; sync_status: z.ZodDefault>; created_at: z.ZodString; updated_at: z.ZodString; @@ -1498,6 +1541,7 @@ declare const serviceBindingSchema: z.ZodPipe; + service_id: z.ZodDefault>>; + service_name: z.ZodDefault>>; hostname: z.ZodString; expires_at: z.ZodNullable; last_checked_at: z.ZodNullable; @@ -1557,6 +1604,22 @@ declare const certificateSchema: z.ZodObject<{ created_at: z.ZodString; updated_at: z.ZodString; }, z.core.$strip>; +declare const serviceCertificateRowSchema: z.ZodObject<{ + binding_id: z.ZodNumber; + domain_id: z.ZodNumber; + service_id: z.ZodNumber; + hostname: z.ZodString; + cert_monitoring: z.ZodEnum<{ + auto: "auto"; + required: "required"; + skipped: "skipped"; + }>; + id: z.ZodNullable; + status: z.ZodString; + expires_at: z.ZodNullable; + last_checked_at: z.ZodNullable; + last_error: z.ZodNullable; +}, z.core.$strip>; type Group = z.infer; type GroupWithStats = z.infer; type Service = z.infer; @@ -1569,12 +1632,13 @@ type Domain = z.infer; type DomainListItem = z.infer; type DnsRecord = z.infer; type Certificate = z.infer; +type ServiceCertificateRow = z.infer; declare const createGroupSchema: z.ZodObject<{ name: z.ZodString; slug: z.ZodString; }, z.core.$strip>; declare const healthCheckConfigSchema: z.ZodObject<{ - health_check_enabled: z.ZodOptional; + health_check_enabled: z.ZodOptional>; health_check_type: z.ZodOptional>; health_check_interval_sec: z.ZodOptional; health_check_timeout_ms: z.ZodOptional; - health_check_verify_tls: z.ZodOptional; + health_check_verify_tls: z.ZodOptional>; health_check_provider: z.ZodOptional>; - health_check_providers: z.ZodOptional>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodOptional; lb_priority: z.ZodOptional; domains: z.ZodDefault; + health_check_enabled: z.ZodOptional>; health_check_type: z.ZodOptional>; health_check_interval_sec: z.ZodOptional; health_check_timeout_ms: z.ZodOptional; - health_check_verify_tls: z.ZodOptional; + health_check_verify_tls: z.ZodOptional>; health_check_provider: z.ZodOptional>; - health_check_providers: z.ZodOptional>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodOptional; lb_priority: z.ZodOptional; domains: z.ZodOptional; + health_check_enabled: z.ZodOptional>; health_check_type: z.ZodOptional>; health_check_interval_sec: z.ZodOptional; health_check_timeout_ms: z.ZodOptional; - health_check_verify_tls: z.ZodOptional; + health_check_verify_tls: z.ZodOptional>; health_check_provider: z.ZodOptional>; - health_check_providers: z.ZodOptional>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodOptional; type UpdateServiceConfigInput = z.infer; declare const createServiceGroupSchema: z.ZodObject<{ - health_check_enabled: z.ZodOptional; + health_check_enabled: z.ZodOptional>; health_check_type: z.ZodOptional>; health_check_interval_sec: z.ZodOptional; health_check_timeout_ms: z.ZodOptional; - health_check_verify_tls: z.ZodOptional; + health_check_verify_tls: z.ZodOptional>; health_check_provider: z.ZodOptional>; - health_check_providers: z.ZodOptional>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodOptional>; }, z.core.$strip>; declare const updateServiceGroupSchema: z.ZodObject<{ - health_check_enabled: z.ZodOptional; + health_check_enabled: z.ZodOptional>; health_check_type: z.ZodOptional>; health_check_interval_sec: z.ZodOptional; health_check_timeout_ms: z.ZodOptional; - health_check_verify_tls: z.ZodOptional; + health_check_verify_tls: z.ZodOptional>; health_check_provider: z.ZodOptional>; - health_check_providers: z.ZodOptional>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>; + }>>, z.ZodTransform<("local" | "cloudflare" | "globalping")[] | readonly ["local"], ("local" | "cloudflare" | "globalping")[]>>>>; health_check_aggregate: z.ZodOptional; type IngestAuditEvent = z.infer; -export { AUDIT_SEVERITIES, AUDIT_SOURCE_APPS, AUDIT_TARGET_TYPES, type AppSettingsPatch, type AppSwitcherConfig, type AppSwitcherEntry, type AuditListQuery, type AuditLogEntry, type AuditSeverity, type AuditSourceApp, type AuditTargetType, type BulkUpdateDomainsInput, CERT_ERROR, CERT_EXPIRED, CERT_MONITORING_VALUES, CERT_MONITOR_AUTO, CERT_MONITOR_REQUIRED, CERT_MONITOR_SKIPPED, CERT_OK, CERT_UNKNOWN, CERT_WARNING, type CertMonitoring, type Certificate, type CfDnsRecord, type CfHealthCheck, type CfZone, type CfdmBindingSyncItem, type ChangeDomainInput, type ChangeIpInput, type CreateDnsRecordInput, type CreateDnsRecordPayload, type CreateDomainInput, type CreateDomainMonitorInput, type CreateGroupInput, type CreateOriginHealthCheckInput, type CreateServiceBindingInput, type CreateServiceGroupInput, type CreateServiceInput, type CreateServiceNodeInput, type CreateServiceWithConfigInput, type CreateSubdomainInput, type DnsRecord, type Domain, type DomainEnvironment, type DomainListItem, type DomainMonitor, type DomainMonitorResult, type DomainMonitorType, type Group, type GroupWithStats, 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, type HealthCheckAggregate, type HealthCheckConfig, type HealthCheckProvider, type HealthCheckScope, type HealthCheckTarget, type HealthCheckType, type HealthProbeLog, type HealthProbeResultItem, type HealthProbeResultsDoc, type HealthProbeTargetItem, type HealthProbeTargetsDoc, type HealthStatusProvider, type HealthStatusQuery, type HealthWorkerStatus, type IngestAuditEvent, type IpHealthState, type IpHealthStatus, type JwtClaims, type LbMode, type LoginInput, type LoginRequest, type LoginResponse, type NodeHealthState, type NotificationLog, type OriginHealthCheck, type OriginHealthCheckRecord, type ParsedFqdn, type PatchDnsRecordPayload, type ReorderServicesInput, SYNC_CONFLICT, SYNC_ERROR, SYNC_PENDING_DELETE, SYNC_PENDING_PUSH, SYNC_SYNCED, type Service, type ServiceBinding, type ServiceBindingView, type ServiceDomainBinding, type ServiceDomainBindingView, type ServiceGroup, type ServiceGroupView, type ServiceGroupsResponse, type ServiceIpHealth, type ServiceNode, type ServiceNodeRecord, type ServiceOverview, type ServiceView, type Subdomain, type SubdomainRecord, type SyncJob, type ToggleEnabledInput, type ToggleServiceIpInput, type UpdateDomainInput, type UpdateServiceConfigInput, type UpdateServiceGroupInput, type UpdateServiceNodeInput, type UpdateSubdomainInput, ValidationError, type VpsTrackerEvent, 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, 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, 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 }; +export { AUDIT_SEVERITIES, AUDIT_SOURCE_APPS, AUDIT_TARGET_TYPES, type AppSettingsPatch, type AppSwitcherConfig, type AppSwitcherEntry, type AuditListQuery, type AuditLogEntry, type AuditSeverity, type AuditSourceApp, type AuditTargetType, type BulkUpdateDomainsInput, CERT_ERROR, CERT_EXPIRED, CERT_MONITORING_VALUES, CERT_MONITOR_AUTO, CERT_MONITOR_REQUIRED, CERT_MONITOR_SKIPPED, CERT_OK, CERT_UNKNOWN, CERT_WARNING, type CertMonitoring, type Certificate, type CfDnsRecord, type CfHealthCheck, type CfZone, type CfdmBindingSyncItem, type ChangeDomainInput, type ChangeIpInput, type CreateDnsRecordInput, type CreateDnsRecordPayload, type CreateDomainInput, type CreateDomainMonitorInput, type CreateGroupInput, type CreateOriginHealthCheckInput, type CreateServiceBindingInput, type CreateServiceGroupInput, type CreateServiceInput, type CreateServiceNodeInput, type CreateServiceWithConfigInput, type CreateSubdomainInput, type DnsRecord, type Domain, type DomainEnvironment, type DomainListItem, type DomainMonitor, type DomainMonitorResult, type DomainMonitorType, type Group, type GroupWithStats, 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, type HealthCheckAggregate, type HealthCheckConfig, type HealthCheckProvider, type HealthCheckScope, type HealthCheckTarget, type HealthCheckType, type HealthProbeLog, type HealthProbeResultItem, type HealthProbeResultsDoc, type HealthProbeTargetItem, type HealthProbeTargetsDoc, type HealthStatusProvider, type HealthStatusQuery, type HealthWorkerStatus, type IngestAuditEvent, type IpHealthState, type IpHealthStatus, type JwtClaims, type LbMode, type LoginInput, type LoginRequest, type LoginResponse, type NodeHealthState, type NotificationLog, type OriginHealthCheck, type OriginHealthCheckRecord, type ParsedFqdn, type PatchDnsRecordPayload, type ReorderServicesInput, SYNC_CONFLICT, SYNC_ERROR, SYNC_PENDING_DELETE, SYNC_PENDING_PUSH, SYNC_SYNCED, type Service, type ServiceBinding, type ServiceBindingView, type ServiceCertificateRow, type ServiceDomainBinding, type ServiceDomainBindingView, type ServiceGroup, type ServiceGroupView, type ServiceGroupsResponse, type ServiceIpHealth, type ServiceNode, type ServiceNodeRecord, type ServiceOverview, type ServiceView, type Subdomain, type SubdomainRecord, type SyncJob, type ToggleEnabledInput, type ToggleServiceIpInput, type UpdateDomainInput, type UpdateServiceConfigInput, type UpdateServiceGroupInput, type UpdateServiceNodeInput, type UpdateSubdomainInput, ValidationError, type VpsTrackerEvent, 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, 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 }; diff --git a/packages/shared/dist/index.js b/packages/shared/dist/index.js index c488ea4..08c4b7d 100644 --- a/packages/shared/dist/index.js +++ b/packages/shared/dist/index.js @@ -276,10 +276,16 @@ var nodeHealthStateSchema = z.enum([ var healthCheckProviderSchema = z.enum(HEALTH_CHECK_PROVIDERS); var healthStatusProviderSchema = z.enum(HEALTH_STATUS_PROVIDERS); var healthCheckAggregateSchema = z.enum(HEALTH_CHECK_AGGREGATES); -var healthCheckProvidersSchema = z.array(healthCheckProviderSchema).min(1).transform((arr) => { - const unique = uniqueHealthProviders(arr); - return unique.length > 0 ? unique : ["local"]; -}); +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, @@ -392,6 +398,7 @@ var serviceDomainBindingSchema = z.object({ 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) }).transform((binding) => ({ ...binding, @@ -465,6 +472,7 @@ var serviceBindingSchema = z.object({ 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() @@ -494,6 +502,8 @@ 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(), @@ -502,6 +512,18 @@ var certificateSchema = z.object({ 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") @@ -512,14 +534,14 @@ var ipv4Schema = z.string().regex( ); 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.boolean().optional(), + 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.boolean().optional(), + health_check_verify_tls: z.coerce.boolean().optional(), health_check_provider: healthCheckProviderSchema.optional(), health_check_providers: healthCheckProvidersSchema.optional(), health_check_aggregate: healthCheckAggregateSchema.optional() @@ -1029,6 +1051,7 @@ export { reorderServicesSchema, serializeHealthProviders, serviceBindingSchema, + serviceCertificateRowSchema, serviceDomainBindingSchema, serviceGroupSchema, serviceGroupTypeSchema, diff --git a/packages/shared/src/schemas.ts b/packages/shared/src/schemas.ts index c73e885..cfc245a 100644 --- a/packages/shared/src/schemas.ts +++ b/packages/shared/src/schemas.ts @@ -3,6 +3,7 @@ import { HEALTH_CHECK_AGGREGATES, HEALTH_CHECK_PROVIDERS, HEALTH_STATUS_PROVIDERS, + parseHealthProviders, uniqueHealthProviders, } from './health-providers.js' @@ -41,13 +42,16 @@ export const healthStatusProviderSchema = z.enum(HEALTH_STATUS_PROVIDERS) export const healthCheckAggregateSchema = z.enum(HEALTH_CHECK_AGGREGATES) -export const healthCheckProvidersSchema = z - .array(healthCheckProviderSchema) - .min(1) - .transform((arr) => { +export const healthCheckProvidersSchema = z.preprocess( + (value) => { + if (value === undefined) return undefined + return Array.isArray(value) ? value : parseHealthProviders(value) + }, + z.array(healthCheckProviderSchema).min(1).transform((arr) => { const unique = uniqueHealthProviders(arr) return unique.length > 0 ? unique : (['local'] as const) - }) + }), +) export const healthCheckScopeSchema = z.enum(['binding', 'group']) export type HealthCheckScope = z.infer @@ -361,14 +365,14 @@ const nodeAddressSchema = z .max(255) const healthCheckConfigFields = { - health_check_enabled: z.boolean().optional(), + 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(30000).optional(), - health_check_verify_tls: z.boolean().optional(), + health_check_verify_tls: z.coerce.boolean().optional(), health_check_provider: healthCheckProviderSchema.optional(), health_check_providers: healthCheckProvidersSchema.optional(), health_check_aggregate: healthCheckAggregateSchema.optional(),