diff --git a/apps/web/src/components/service-edit-sheet.tsx b/apps/web/src/components/service-edit-sheet.tsx index 76be066..3551324 100644 --- a/apps/web/src/components/service-edit-sheet.tsx +++ b/apps/web/src/components/service-edit-sheet.tsx @@ -141,6 +141,14 @@ function buildDomainsPayload(bindings: ServiceBindingDraft[]) { ? { fqdn: binding.fqdn.trim(), target_cname: binding.target_cname.trim(), + lb_mode: binding.lb_mode, + health_check_enabled: binding.health.enabled, + health_check_type: binding.health.type, + health_check_port: binding.health.port, + health_check_path: binding.health.path, + health_check_expected_status: binding.health.expected_status, + health_check_interval_sec: binding.health.interval_sec, + health_check_timeout_ms: binding.health.timeout_ms, } : { fqdn: binding.fqdn.trim(), @@ -529,9 +537,12 @@ export function ServiceEditSheet({ {bindings.map((binding, index) => { const showLbBlock = - binding.record_type === 'A' && binding.target_ips.length > 0 + (binding.record_type === 'A' && binding.target_ips.length > 0) || + (binding.record_type === 'CNAME' && binding.target_cname.trim().length > 0) const showMeta = - binding.target_ips.length > 1 && binding.lb_mode !== 'round_robin' + binding.record_type === 'A' && + binding.target_ips.length > 1 && + binding.lb_mode !== 'round_robin' return ( @@ -602,9 +613,11 @@ export function ServiceEditSheet({ - {binding.target_ips.length > 1 - ? 'Балансировка и Health-check (multi-A)' - : 'Health-check'} + {binding.record_type === 'CNAME' + ? 'Health-check' + : binding.target_ips.length > 1 + ? 'Балансировка и Health-check (multi-A)' + : 'Health-check'} 1} + showLbMode={ + binding.record_type === 'A' && + binding.target_ips.length > 1 + } idPrefix={`binding-${index}-health`} /> diff --git a/apps/web/src/components/services-board/service-row.tsx b/apps/web/src/components/services-board/service-row.tsx index f908386..a200a28 100644 --- a/apps/web/src/components/services-board/service-row.tsx +++ b/apps/web/src/components/services-board/service-row.tsx @@ -75,7 +75,9 @@ export const ServiceRow = memo(function ServiceRow({ const fqdn = serviceDisplayFqdn(service) const allFqdns = (service.domains ?? []).map((d) => bindingToFqdn(d)) const healthBindings = (service.domains ?? []).filter( - (d) => d.record_type === 'A' && (d.target_ips?.length ?? 0) > 0, + (d) => + (d.record_type === 'A' && (d.target_ips?.length ?? 0) > 0) || + (d.record_type === 'CNAME' && Boolean(d.target_cname?.trim())), ) const style = transform diff --git a/packages/db/dist/index.js b/packages/db/dist/index.js index 17b5d2a..cb7c8c7 100644 --- a/packages/db/dist/index.js +++ b/packages/db/dist/index.js @@ -1172,10 +1172,46 @@ function listHealthCheckTargets(db) { AND sg.enabled = 1 AND sb.health_check_enabled = 0 `); + const cnameBindingTargets = db.all(sql2` + SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip, + sb.hostname AS hostname, + sb.health_check_type AS type, + sb.health_check_port AS port, + sb.health_check_path AS path, + sb.health_check_expected_status AS expected_status, + sb.health_check_timeout_ms AS timeout_ms + FROM service_bindings sb + JOIN services s ON s.id = sb.service_id + WHERE sb.health_check_enabled = 1 + AND sb.cname_target IS NOT NULL + AND sb.cname_target <> '' + AND s.enabled = 1 + `); + const groupInheritedCnameBindingTargets = db.all(sql2` + SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip, + sb.hostname AS hostname, + sg.health_check_type AS type, + sg.health_check_port AS port, + sg.health_check_path AS path, + sg.health_check_expected_status AS expected_status, + sg.health_check_timeout_ms AS timeout_ms + FROM service_bindings sb + JOIN services s ON s.id = sb.service_id + JOIN service_groups sg ON sg.id = s.service_group_id + WHERE sg.health_check_enabled = 1 + AND sg.domain IS NOT NULL + AND s.enabled = 1 + AND sg.enabled = 1 + AND sb.health_check_enabled = 0 + AND sb.cname_target IS NOT NULL + AND sb.cname_target <> '' + `); return [ ...bindingTargets, ...groupTargets, - ...groupInheritedBindingTargets + ...groupInheritedBindingTargets, + ...cnameBindingTargets, + ...groupInheritedCnameBindingTargets ]; } export { diff --git a/packages/db/src/repos.ts b/packages/db/src/repos.ts index 32c3802..938eb29 100644 --- a/packages/db/src/repos.ts +++ b/packages/db/src/repos.ts @@ -1539,9 +1539,50 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] { AND sb.health_check_enabled = 0 `); + // CNAME-bindings with their own health_check_enabled: probe the CNAME target host. + const cnameBindingTargets = db.all(sql` + SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip, + sb.hostname AS hostname, + sb.health_check_type AS type, + sb.health_check_port AS port, + sb.health_check_path AS path, + sb.health_check_expected_status AS expected_status, + sb.health_check_timeout_ms AS timeout_ms + FROM service_bindings sb + JOIN services s ON s.id = sb.service_id + WHERE sb.health_check_enabled = 1 + AND sb.cname_target IS NOT NULL + AND sb.cname_target <> '' + AND s.enabled = 1 + `); + + // CNAME-bindings of services in a group with group health-check enabled + // (inherit group config). Only for bindings without their own health_check_enabled. + const groupInheritedCnameBindingTargets = db.all(sql` + SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip, + sb.hostname AS hostname, + sg.health_check_type AS type, + sg.health_check_port AS port, + sg.health_check_path AS path, + sg.health_check_expected_status AS expected_status, + sg.health_check_timeout_ms AS timeout_ms + FROM service_bindings sb + JOIN services s ON s.id = sb.service_id + JOIN service_groups sg ON sg.id = s.service_group_id + WHERE sg.health_check_enabled = 1 + AND sg.domain IS NOT NULL + AND s.enabled = 1 + AND sg.enabled = 1 + AND sb.health_check_enabled = 0 + AND sb.cname_target IS NOT NULL + AND sb.cname_target <> '' + `); + return [ ...bindingTargets, ...groupTargets, ...groupInheritedBindingTargets, + ...cnameBindingTargets, + ...groupInheritedCnameBindingTargets, ]; }