feat(health-checks): enhance health check configuration and logging
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 51s
quality / api (push) Successful in 44s
CD / quality (push) Successful in 1m44s
CD / publish (push) Successful in 1m35s
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 51s
quality / api (push) Successful in 44s
CD / quality (push) Successful in 1m44s
CD / publish (push) Successful in 1m35s
- Added new health worker URL and token fields to the AppConfig interface, allowing for Cloudflare Worker integration. - Updated health check routes to utilize the new worker configuration, enabling dynamic health checks via Cloudflare Workers. - Introduced a health log endpoint for services, providing detailed logs of health probe results. - Enhanced health check service logic to support both local and Cloudflare Worker providers, improving flexibility in health monitoring. - Updated UI components to reflect changes in health check provider settings and display relevant health information. This commit significantly improves the health check management capabilities, allowing for better integration with Cloudflare Workers and enhanced logging features.
This commit is contained in:
@@ -54,6 +54,8 @@ interface HealthCheckBadgeProps {
|
||||
latencyMs?: number | null
|
||||
lastCheckedAt?: string | null
|
||||
lastError?: string | null
|
||||
colo?: string | null
|
||||
provider?: 'local' | 'cloudflare' | string | null
|
||||
title?: string
|
||||
showLatency?: boolean
|
||||
size?: 'xs' | 'sm'
|
||||
@@ -65,6 +67,8 @@ export function HealthCheckBadge({
|
||||
latencyMs,
|
||||
lastCheckedAt,
|
||||
lastError,
|
||||
colo,
|
||||
provider,
|
||||
title,
|
||||
showLatency = false,
|
||||
size = 'sm',
|
||||
@@ -79,6 +83,9 @@ export function HealthCheckBadge({
|
||||
tooltipParts.push(`Статус: ${label}`)
|
||||
if (latencyMs != null) tooltipParts.push(`Задержка: ${latencyMs} мс`)
|
||||
if (lastCheckedAt) tooltipParts.push(`Проверка: ${formatDate(lastCheckedAt)}`)
|
||||
if (colo) tooltipParts.push(`Colo: ${colo}`)
|
||||
if (provider === 'cloudflare') tooltipParts.push('Провайдер: Cloudflare Worker')
|
||||
if (provider === 'local') tooltipParts.push('Провайдер: Local')
|
||||
if (lastError) tooltipParts.push(`Ошибка: ${lastError}`)
|
||||
|
||||
return (
|
||||
|
||||
@@ -190,7 +190,7 @@ export function HealthCheckConfigFields({
|
||||
|
||||
<SettingRow
|
||||
title="Провайдер health-check"
|
||||
description="Local TCP/HTTP или Cloudflare Health Checks API"
|
||||
description="Откуда идёт проба: API CFDM или Cloudflare Worker (edge)"
|
||||
labelFor={`${idPrefix}-provider`}
|
||||
compact
|
||||
className={rowClass}
|
||||
@@ -208,21 +208,26 @@ export function HealthCheckConfigFields({
|
||||
</SettingRow>
|
||||
{value.provider === 'cloudflare' ? (
|
||||
<Alert>
|
||||
<AlertTitle>Cloudflare Health Checks</AlertTitle>
|
||||
<AlertTitle>Cloudflare Worker</AlertTitle>
|
||||
<AlertDescription>
|
||||
Поля соответствуют официальному API зоны. Если план не позволяет Health
|
||||
Checks, API вернёт ошибку — останется Local. Workers не используются.
|
||||
Проба с edge Cloudflare, не продукт Health Checks API (на Free его нет).
|
||||
Регионы WNAM/WEU недоступны — в результате будет colo ближайшего POP
|
||||
(например AMS). URL и токен Worker — в{' '}
|
||||
<Link to="/settings/health" className="text-foreground underline">
|
||||
Настройках → Health-check
|
||||
</Link>
|
||||
. Если Worker не задан, цель не пробируется как Local.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
<Alert>
|
||||
<AlertTitle>Local health-check</AlertTitle>
|
||||
<AlertDescription>
|
||||
Интервал и таймаут пробы — ниже. Cron и пороги Slow/Down задаются в{' '}
|
||||
Проба TCP/HTTP с сервера API. Cron и пороги Slow/Down — в{' '}
|
||||
<Link to="/settings/health" className="text-foreground underline">
|
||||
Настройках → Health-check
|
||||
</Link>
|
||||
, как параметры Cloudflare Health Checks в этой форме.
|
||||
. Интервал в карточке не используется.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
@@ -334,83 +339,18 @@ export function HealthCheckConfigFields({
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<FormFieldSimple label="Интервал, сек" htmlFor={`${idPrefix}-interval`}>
|
||||
<CompactNumberField
|
||||
id={`${idPrefix}-interval`}
|
||||
value={value.interval_sec}
|
||||
min={5}
|
||||
max={3600}
|
||||
placeholder="30"
|
||||
onValueChange={(next) =>
|
||||
patch({ interval_sec: next ?? 30 })
|
||||
}
|
||||
/>
|
||||
</FormFieldSimple>
|
||||
<FormFieldSimple label="Таймаут, мс" htmlFor={`${idPrefix}-timeout`}>
|
||||
<CompactNumberField
|
||||
id={`${idPrefix}-timeout`}
|
||||
value={value.timeout_ms}
|
||||
min={100}
|
||||
max={30000}
|
||||
placeholder="3000"
|
||||
onValueChange={(next) =>
|
||||
patch({ timeout_ms: next ?? 3000 })
|
||||
}
|
||||
/>
|
||||
</FormFieldSimple>
|
||||
</div>
|
||||
{value.provider === 'cloudflare' ? (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<FormFieldSimple
|
||||
label="Retries"
|
||||
htmlFor={`${idPrefix}-retries`}
|
||||
hint="Cloudflare retries"
|
||||
>
|
||||
<CompactNumberField
|
||||
id={`${idPrefix}-retries`}
|
||||
value={value.retries ?? 2}
|
||||
min={0}
|
||||
max={10}
|
||||
placeholder="2"
|
||||
onValueChange={(retries) => patch({ retries: retries ?? 2 })}
|
||||
/>
|
||||
</FormFieldSimple>
|
||||
<FormFieldSimple
|
||||
label="Successes"
|
||||
htmlFor={`${idPrefix}-successes`}
|
||||
hint="consecutive_successes"
|
||||
>
|
||||
<CompactNumberField
|
||||
id={`${idPrefix}-successes`}
|
||||
value={value.consecutive_successes ?? 2}
|
||||
min={1}
|
||||
max={20}
|
||||
placeholder="2"
|
||||
onValueChange={(consecutive_successes) =>
|
||||
patch({ consecutive_successes: consecutive_successes ?? 2 })
|
||||
}
|
||||
/>
|
||||
</FormFieldSimple>
|
||||
</div>
|
||||
) : null}
|
||||
{value.provider === 'cloudflare' && isHttp ? (
|
||||
<FormFieldSimple label="HTTP method" htmlFor={`${idPrefix}-method`}>
|
||||
<Select
|
||||
modal={false}
|
||||
value={value.method ?? 'GET'}
|
||||
onValueChange={(v) => patch({ method: v ?? 'GET' })}
|
||||
>
|
||||
<SelectTrigger id={`${idPrefix}-method`} className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="GET">GET</SelectItem>
|
||||
<SelectItem value="HEAD">HEAD</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormFieldSimple>
|
||||
) : null}
|
||||
<FormFieldSimple label="Таймаут, мс" htmlFor={`${idPrefix}-timeout`}>
|
||||
<CompactNumberField
|
||||
id={`${idPrefix}-timeout`}
|
||||
value={value.timeout_ms}
|
||||
min={100}
|
||||
max={30000}
|
||||
placeholder="3000"
|
||||
onValueChange={(next) =>
|
||||
patch({ timeout_ms: next ?? 3000 })
|
||||
}
|
||||
/>
|
||||
</FormFieldSimple>
|
||||
</div>
|
||||
) : null}
|
||||
</FieldGroup>
|
||||
|
||||
@@ -23,6 +23,8 @@ export interface HealthTimelineEvent {
|
||||
latency_ms?: number | null
|
||||
error?: string | null
|
||||
checked_at: string
|
||||
colo?: string | null
|
||||
provider?: string | null
|
||||
}
|
||||
|
||||
interface HealthTimelineProps {
|
||||
@@ -63,6 +65,8 @@ export function HealthTimeline({ events }: HealthTimelineProps) {
|
||||
<HealthCheckBadge
|
||||
status={event.status}
|
||||
latencyMs={event.latency_ms}
|
||||
colo={event.colo}
|
||||
provider={event.provider}
|
||||
size="xs"
|
||||
showLatency
|
||||
/>
|
||||
|
||||
@@ -110,7 +110,7 @@ function toBindingDrafts(service: ServiceView): ServiceBindingDraft[] {
|
||||
interval_sec: binding.health_check_interval_sec,
|
||||
timeout_ms: binding.health_check_timeout_ms,
|
||||
verify_tls: binding.health_check_verify_tls ?? false,
|
||||
provider: 'local',
|
||||
provider: binding.health_check_provider === 'cloudflare' ? 'cloudflare' : 'local',
|
||||
},
|
||||
target_ip_weights: binding.target_ip_weights ?? {},
|
||||
target_ip_priorities: binding.target_ip_priorities ?? {},
|
||||
@@ -138,6 +138,7 @@ function buildDomainsPayload(bindings: ServiceBindingDraft[]) {
|
||||
health_check_interval_sec: binding.health.interval_sec,
|
||||
health_check_timeout_ms: binding.health.timeout_ms,
|
||||
health_check_verify_tls: binding.health.verify_tls,
|
||||
health_check_provider: binding.health.provider,
|
||||
}
|
||||
: {
|
||||
fqdn: binding.fqdn.trim(),
|
||||
@@ -153,6 +154,7 @@ function buildDomainsPayload(bindings: ServiceBindingDraft[]) {
|
||||
health_check_interval_sec: binding.health.interval_sec,
|
||||
health_check_timeout_ms: binding.health.timeout_ms,
|
||||
health_check_verify_tls: binding.health.verify_tls,
|
||||
health_check_provider: binding.health.provider,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,6 +166,10 @@ export function ServiceIpList({
|
||||
<HealthCheckBadge
|
||||
status={health?.status ?? 'unknown'}
|
||||
latencyMs={health?.latency_ms}
|
||||
lastCheckedAt={health?.last_checked_at}
|
||||
lastError={health?.last_error}
|
||||
colo={health?.colo}
|
||||
provider={health?.provider}
|
||||
size="xs"
|
||||
/>
|
||||
<TruncatedText
|
||||
|
||||
Reference in New Issue
Block a user