From 4c59780ff0d2c412f48ef9eaa13a6f9302bce6bf Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 20 Aug 2026 14:01:43 +0700 Subject: [PATCH] =?UTF-8?q?fix(health):=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20isHealthy=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=BA=D0=B8=D1=85=20FQDN=20=D0=BD=D0=B0=20=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D0=B8=20IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Схлопывание дубликатов в группе вызывало ReferenceError при сохранении сервиса. Co-authored-by: Cursor --- .../src/services/service-config-service.ts | 1 + apps/api/test/service-bindings-prune.test.ts | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/apps/api/src/services/service-config-service.ts b/apps/api/src/services/service-config-service.ts index bdf1354..5646ab1 100644 --- a/apps/api/src/services/service-config-service.ts +++ b/apps/api/src/services/service-config-service.ts @@ -29,6 +29,7 @@ import * as domainService from "./domain-service.js"; import { syncServiceToVpsTracker } from "./vps-tracker-sync.js"; import { fireEnsureHealthWorker, DEFAULT_HEALTH_FALLBACKS } from "./health/health-worker-deploy.js"; import { + isHealthy, selectActiveIpsByMode, withBindingLock, type LbIpRow, diff --git a/apps/api/test/service-bindings-prune.test.ts b/apps/api/test/service-bindings-prune.test.ts index 1772e43..b5e24b1 100644 --- a/apps/api/test/service-bindings-prune.test.ts +++ b/apps/api/test/service-bindings-prune.test.ts @@ -78,4 +78,53 @@ describe("service bindings prune", () => { expect(bindings).toHaveLength(2); expect(bindings.map((b) => b.hostname).sort()).toEqual(["api", "www"]); }); + + it("updateConfig with extra FQDN per IP does not throw when group health-check is on", async () => { + const db = setupDb(); + const cf = mockCf(); + const health = { + health_check_enabled: true, + health_check_type: "tcp" as const, + health_check_port: 443, + health_check_providers: ["local", "cloudflare", "globalping"] as const, + health_check_aggregate: "majority" as const, + }; + + repos.createDomain(db, null, "example.com", "cf-zone-example"); + const group = repos.createServiceGroup( + db, + "VPN", + "vpn", + null, + "vpn.example.com", + { ...health }, + ); + const service = repos.createService(db, "GT", "gt"); + repos.setServiceGroup(db, service.id, group.id); + repos.setServiceEnabled(db, service.id, true); + + const view = await updateConfig(db, cf, service.id, { + ips: ["93.115.203.183", "130.49.213.153"], + domains: [ + { + fqdn: "gt.example.com", + target_ips: ["93.115.203.183", "130.49.213.153"], + ...health, + }, + { + fqdn: "rutg.example.com", + target_ips: ["93.115.203.183"], + ...health, + }, + { + fqdn: "nsgt.example.com", + target_ips: ["130.49.213.153"], + ...health, + }, + ], + }); + + expect(view.domains).toHaveLength(3); + expect(view.ips.sort()).toEqual(["130.49.213.153", "93.115.203.183"]); + }); });