refactor: Update health check service to use undici's fetch for improved HTTP probing and enhance FormFieldSimple component with optional hint prop
Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m4s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m25s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 7s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m4s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m25s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 7s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { connect } from "node:net";
|
import { connect } from "node:net";
|
||||||
import { Agent } from "undici";
|
import { Agent, fetch as undiciFetch } from "undici";
|
||||||
import type { Db } from "@cfdm/db";
|
import type { Db } from "@cfdm/db";
|
||||||
import { repos } from "@cfdm/db";
|
import { repos } from "@cfdm/db";
|
||||||
import type { HealthCheckTarget, IpHealthState } from "@cfdm/shared";
|
import type { HealthCheckTarget, IpHealthState } from "@cfdm/shared";
|
||||||
@@ -67,14 +67,13 @@ async function httpProbe(
|
|||||||
const path = target.path?.trim() || "/";
|
const path = target.path?.trim() || "/";
|
||||||
const pathWithSlash = path.startsWith("/") ? path : `/${path}`;
|
const pathWithSlash = path.startsWith("/") ? path : `/${path}`;
|
||||||
const port = target.port ?? 80;
|
const port = target.port ?? 80;
|
||||||
const hostHeader = target.hostname || ip;
|
|
||||||
const useTls = port === 443;
|
const useTls = port === 443;
|
||||||
// For HTTPS probing against a raw IP, set SNI/Host so TLS handshake and vhost routing work.
|
// For HTTPS on 443, probe via the hostname (URL host = hostname) so TLS SNI, Host header
|
||||||
const authorityHost = target.hostname || ip;
|
// and any edge/vhost protection (e.g. Cloudflare origin 421 on direct-IP) all line up.
|
||||||
const url = useTls
|
// For multi-A records this loses strict per-IP HTTPS granularity — use TCP probe for that.
|
||||||
? `https://${authorityHost}${pathWithSlash}`
|
// For plain HTTP we still hit the literal IP (per-record target).
|
||||||
: `http://${ip}${pathWithSlash}`;
|
const urlHost = useTls ? target.hostname || ip : ip;
|
||||||
// Custom dispatcher for HTTPS-to-IP: connect to the literal IP, but present hostname via SNI.
|
const url = `${useTls ? "https" : "http"}://${urlHost}${pathWithSlash}`;
|
||||||
const dispatcher =
|
const dispatcher =
|
||||||
useTls && target.hostname
|
useTls && target.hostname
|
||||||
? new Agent({
|
? new Agent({
|
||||||
@@ -85,12 +84,11 @@ async function httpProbe(
|
|||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
const response = await undiciFetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: { Host: hostHeader },
|
headers: { Host: target.hostname || ip },
|
||||||
signal: AbortSignal.timeout(timeoutMs),
|
signal: AbortSignal.timeout(timeoutMs),
|
||||||
redirect: "manual",
|
redirect: "manual",
|
||||||
// @ts-expect-error undici dispatcher option is not in fetch types
|
|
||||||
dispatcher,
|
dispatcher,
|
||||||
});
|
});
|
||||||
const latency = Date.now() - started;
|
const latency = Date.now() - started;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ interface FormFieldSimpleProps {
|
|||||||
label: string
|
label: string
|
||||||
htmlFor: string
|
htmlFor: string
|
||||||
error?: { message?: string }
|
error?: { message?: string }
|
||||||
|
hint?: string
|
||||||
className?: string
|
className?: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}
|
}
|
||||||
@@ -71,6 +72,7 @@ export function FormFieldSimple({
|
|||||||
label,
|
label,
|
||||||
htmlFor,
|
htmlFor,
|
||||||
error,
|
error,
|
||||||
|
hint,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
}: FormFieldSimpleProps) {
|
}: FormFieldSimpleProps) {
|
||||||
@@ -78,6 +80,9 @@ export function FormFieldSimple({
|
|||||||
<Field data-invalid={!!error} className={cn(className)}>
|
<Field data-invalid={!!error} className={cn(className)}>
|
||||||
<FieldLabel htmlFor={htmlFor}>{label}</FieldLabel>
|
<FieldLabel htmlFor={htmlFor}>{label}</FieldLabel>
|
||||||
{children}
|
{children}
|
||||||
|
{hint && !error && (
|
||||||
|
<p className="text-xs text-muted-foreground">{hint}</p>
|
||||||
|
)}
|
||||||
<FieldError errors={[error]} />
|
<FieldError errors={[error]} />
|
||||||
</Field>
|
</Field>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -141,10 +141,11 @@ export function HealthCheckConfigFields({
|
|||||||
<FormFieldSimple
|
<FormFieldSimple
|
||||||
label="HTTP path"
|
label="HTTP path"
|
||||||
htmlFor={`${idPrefix}-path`}
|
htmlFor={`${idPrefix}-path`}
|
||||||
|
hint="Опционально. По умолчанию проверяется /"
|
||||||
>
|
>
|
||||||
<AppInput
|
<AppInput
|
||||||
id={`${idPrefix}-path`}
|
id={`${idPrefix}-path`}
|
||||||
placeholder="/health"
|
placeholder="/"
|
||||||
value={value.path ?? ''}
|
value={value.path ?? ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
patch({ path: e.target.value === '' ? null : e.target.value })
|
patch({ path: e.target.value === '' ? null : e.target.value })
|
||||||
|
|||||||
Reference in New Issue
Block a user