From 58e1fc9d031fb7f7f402e267996ea0d466a472df Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Mon, 20 Apr 2026 08:51:37 +0800 Subject: [PATCH] feat(cloud-probes): implement cloud probe middleware and configuration - Added constants for common cloud probe paths and prefixes. - Introduced middleware to handle cloud probe requests with appropriate cache control headers. - Updated Nuxt configuration to include route rules for cloud probe paths. This enhances the application's ability to respond to health checks from various cloud platforms. --- nuxt.config.ts | 23 ++++++++++++++++++++ packages/drizzle-pkg/db.sqlite | Bin 147456 -> 147456 bytes server/constants/cloud-probes.ts | 42 ++++++++++++++++++++++++++++++++++++ server/middleware/00.cloud-probe.ts | 25 +++++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 server/constants/cloud-probes.ts create mode 100644 server/middleware/00.cloud-probe.ts diff --git a/nuxt.config.ts b/nuxt.config.ts index 0f7dc65..888da21 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -4,9 +4,17 @@ import { createRequire } from "node:module"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; +import { + CLOUD_PROBE_PATH_PREFIXES, + CLOUD_PROBE_PATHS, +} from "./server/constants/cloud-probes"; + const configDir = dirname(fileURLToPath(import.meta.url)); const require = createRequire(import.meta.url); +const probeCacheControl = + "no-store, no-cache, must-revalidate, proxy-revalidate"; + function resolveNuxtNitroErrorHandler(): string { const path = join( dirname(require.resolve("nuxt/package.json")), @@ -38,6 +46,21 @@ export default defineNuxtConfig({ fonts: false }, devtools: { enabled: true }, + /** 探针路径与 `server/middleware/00.cloud-probe.ts` 一致:禁止 CDN/LB 长期缓存探测响应 */ + routeRules: { + ...Object.fromEntries( + CLOUD_PROBE_PATHS.map((path) => [ + path, + { headers: { "cache-control": probeCacheControl } }, + ]), + ), + ...Object.fromEntries( + CLOUD_PROBE_PATH_PREFIXES.flatMap((prefix) => [ + [`${prefix}/**`, { headers: { "cache-control": probeCacheControl } }], + [prefix, { headers: { "cache-control": probeCacheControl } }], + ]), + ), + }, typescript: { tsConfig: { compilerOptions: { diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite index 6f45afc614f350e4388e268c4bf19b8313a768e8..0de95d44e1a16d6bbb45caf204c532d6cd880dc0 100644 GIT binary patch delta 194 zcmZo@;B08%oFL7(W1@^RiaEEsT?pOuA~laZzQ MQ~dT%@r-;A0GW3<#Q*>R delta 89 zcmZo@;B08%oFL7(eWHvroHaXgUB~Y}Df&T`7 r8~=KsXa)b|Hvc-H*e3@5Km6bMKW!IGV0_Ha$lClVe*33*M!p9C?us4( diff --git a/server/constants/cloud-probes.ts b/server/constants/cloud-probes.ts new file mode 100644 index 0000000..719f08c --- /dev/null +++ b/server/constants/cloud-probes.ts @@ -0,0 +1,42 @@ +/** + * 各云平台 / 容器编排常见 HTTP 探针路径(不含 `/`,避免误伤站点根)。 + * 可按负载均衡控制台实际配置增删;若与业务路由同名请从列表中移除对应项。 + */ +export const CLOUD_PROBE_PATHS = [ + // 通用 / Kubernetes + "/health", + "/healthz", + "/livez", + "/readyz", + "/liveness", + "/readiness", + "/startup", + "/health/live", + "/health/ready", + "/health/startup", + // Spring Actuator(经网关暴露时偶见) + "/actuator/health", + "/actuator/health/liveness", + "/actuator/health/readiness", + // Azure App Service 相关默认探测 + "/robots933456.txt", + // 国内云控制台常见示例静态页 + "/check.html", + "/status.html", + "/ping", + "/status", + "/alive", +] as const; + +/** 阿里云等:健康检查为 `/rpc` 或带后缀路径(如控制台填写的 `/rpc/...`),按前缀匹配 */ +export const CLOUD_PROBE_PATH_PREFIXES = ["/rpc"] as const; + +export const CLOUD_PROBE_PATH_SET = new Set(CLOUD_PROBE_PATHS); + +export function isCloudProbePath(pathname: string): boolean { + if (CLOUD_PROBE_PATH_SET.has(pathname)) return true; + for (const prefix of CLOUD_PROBE_PATH_PREFIXES) { + if (pathname === prefix || pathname.startsWith(`${prefix}/`)) return true; + } + return false; +} diff --git a/server/middleware/00.cloud-probe.ts b/server/middleware/00.cloud-probe.ts new file mode 100644 index 0000000..60f50dd --- /dev/null +++ b/server/middleware/00.cloud-probe.ts @@ -0,0 +1,25 @@ +import { getRequestURL } from "h3"; +import { isCloudProbePath } from "#server/constants/cloud-probes"; + +const METHODS = new Set(["GET", "HEAD"]); + +export default eventHandler((event) => { + if (!METHODS.has(event.method)) return; + + const pathname = getRequestURL(event).pathname; + if (!isCloudProbePath(pathname)) return; + + setHeader(event, "content-type", "text/plain; charset=utf-8"); + setHeader( + event, + "cache-control", + "no-store, no-cache, must-revalidate, proxy-revalidate", + ); + + if (event.method === "HEAD") { + setResponseStatus(event, 200); + return; + } + + return "OK"; +});