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,
+ ];
}