diff --git a/apps/web/src/components/services-board/service-group-item.tsx b/apps/web/src/components/services-board/service-group-item.tsx index d0392d4..8528789 100644 --- a/apps/web/src/components/services-board/service-group-item.tsx +++ b/apps/web/src/components/services-board/service-group-item.tsx @@ -80,6 +80,9 @@ export function ServiceGroupItem({ const serviceIds = column.items.map((s) => s.id) const groupAllSelected = isAllSelected?.(serviceIds) ?? false const groupSomeSelected = isSomeSelected?.(serviceIds) ?? false + const groupHealthEnabled = Boolean( + column.group?.domain && column.group?.health_check_enabled && column.enabled, + ) useEffect(() => { if (isOver && isDragging && !dragDisabled) { @@ -138,6 +141,7 @@ export function ServiceGroupItem({ onSelectedChange={(selected) => onSelectedChange?.(service.id, selected) } + groupHealthEnabled={groupHealthEnabled} /> ))} diff --git a/apps/web/src/components/services-board/service-row.tsx b/apps/web/src/components/services-board/service-row.tsx index 46df7fe..f908386 100644 --- a/apps/web/src/components/services-board/service-row.tsx +++ b/apps/web/src/components/services-board/service-row.tsx @@ -42,6 +42,7 @@ export interface ServiceRowProps { selected?: boolean onSelectedChange?: (selected: boolean) => void showCheckbox?: boolean + groupHealthEnabled?: boolean } export const ServiceRow = memo(function ServiceRow({ @@ -56,6 +57,7 @@ export const ServiceRow = memo(function ServiceRow({ selected = false, onSelectedChange, showCheckbox = false, + groupHealthEnabled = false, }: ServiceRowProps) { const { attributes, @@ -159,7 +161,11 @@ export const ServiceRow = memo(function ServiceRow({
{healthBindings.map((binding) => ( - + ))} {syncStatus ? : null} {isToggling ? ( @@ -207,8 +213,14 @@ export const ServiceRow = memo(function ServiceRow({ ) }) -function BindingHealthBadge({ binding }: { binding: ServiceDomainBinding }) { - const enabled = Boolean(binding.health_check_enabled) +function BindingHealthBadge({ + binding, + groupHealthEnabled, +}: { + binding: ServiceDomainBinding + groupHealthEnabled: boolean +}) { + const enabled = Boolean(binding.health_check_enabled) || groupHealthEnabled const { data: health } = useAggregatedHealth( 'binding', binding.binding_id, diff --git a/packages/db/dist/index.js b/packages/db/dist/index.js index ebf6150..17b5d2a 100644 --- a/packages/db/dist/index.js +++ b/packages/db/dist/index.js @@ -1154,7 +1154,29 @@ function listHealthCheckTargets(db) { AND s.enabled = 1 AND (sg.enabled = 1) `); - return [...bindingTargets, ...groupTargets]; + const groupInheritedBindingTargets = db.all(sql2` + SELECT 'binding' AS scope, sb.id AS ref_id, sbi.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_binding_ips sbi + JOIN service_bindings sb ON sb.id = sbi.binding_id + 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 + `); + return [ + ...bindingTargets, + ...groupTargets, + ...groupInheritedBindingTargets + ]; } export { ConflictError, diff --git a/packages/db/src/repos.ts b/packages/db/src/repos.ts index c60f717..32c3802 100644 --- a/packages/db/src/repos.ts +++ b/packages/db/src/repos.ts @@ -1516,5 +1516,32 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] { AND (sg.enabled = 1) `); - return [...bindingTargets, ...groupTargets]; + // IPs of A-bindings of enabled services in a group whose group has health-check enabled. + // These inherit the group's health-check config (scope='binding', ref_id=binding_id), + // so per-domain badges reflect group rules. Skipped for bindings that already have + // their own health_check_enabled=1 (covered by bindingTargets above). + const groupInheritedBindingTargets = db.all(sql` + SELECT 'binding' AS scope, sb.id AS ref_id, sbi.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_binding_ips sbi + JOIN service_bindings sb ON sb.id = sbi.binding_id + 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 + `); + + return [ + ...bindingTargets, + ...groupTargets, + ...groupInheritedBindingTargets, + ]; }