import * as path from "@std/path"; import fastify from "fastify"; import fastifyView from "@fastify/view"; import fastifyStatic from "@fastify/static"; import handlebars from "handlebars"; import mime from "mime/lite"; const server = fastify(); const __dirname = new URL(".", import.meta.url).pathname; // 处理视图资源 server.register(fastifyView, { engine: { handlebars }, root: path.resolve(__dirname, "views"), }); // 处理静态资源 maxAge 30天 server.register(fastifyStatic, { prefix: "/static/", root: path.resolve(__dirname, "static"), cacheControl: true, maxAge: 2592000, }); enum Headers { FormatHeader = "x-format", CodeHeader = "x-code", OriginalURI = "x-original-uri", ContentType = "content-type", Namespace = "x-namespace", IngressName = "x-ingress-name", ServiceName = "x-service-name", ServicePort = "x-service-port", RequestId = "x-request-id", } const CodeMessageMap: Record = { 403: "禁止访问此内容: 抱歉,由于安全问题您被禁止访问此内容。如果您认为这是个错误,请联系我们获取帮助。", 404: "内容未找到: 您正在寻找的内容可能已被删除、名称已更改或暂时不可用。", 500: "服务器错误: 请稍后再试,或者返回主页。如果问题持续存在,请联系我们,我们会尽快修复。", }; // 默认处理函数 server.all("/", (request, reply) => { const headers = request.headers; // 获取OriginalURI的文件后缀 const originalURI: string = headers[Headers.OriginalURI] || ""; const urlExtWithDot = path.extname(originalURI); // 获取FormatHeader对应的文件后缀 const format: string = headers[Headers.FormatHeader] || ""; // 排除format存在多个的情况 目前只支持一个 const firstFormat = format.split(",")[0]; const formatExt = mime.getExtension(firstFormat); const formatExtWithDot = formatExt ? `.${formatExt}` : ""; const ext = formatExtWithDot || urlExtWithDot || ".html"; // 获取header中的其他内容 const requestId: string = headers[Headers.RequestId] || ""; // 获取CodeHeader对应的状态码 const code: number = Number(headers[Headers.CodeHeader] || 404); // 获取兼容code 比如没有403的code,就使用404 没有502的code,就使用500 取第一位相同的但是最小的code let compatibleCode = code; const isCodeExist = Reflect.has(CodeMessageMap, code); if (!isCodeExist) { const codeList = Object.keys(CodeMessageMap).map(Number).sort((a, b) => a - b ); compatibleCode = Number( codeList.find((c) => String(c).startsWith(String(code).charAt(0))) || 404, ); } const message = CodeMessageMap[compatibleCode]; const data = { code, requestId, message }; console.log( `[${ new Date().toISOString() }] code:${code} url:'${originalURI}' ext:'${ext}' requestId:'${requestId}'`, ); // 根据后缀渲染不同的内容 if (ext === ".html" || ext === ".htm") { const [title, content] = message.split(":"); const htmlContent = content.trim().replace(/。(?=.+)/, "。
"); reply.code(code).view("error.hbs", { ...data, title, content: htmlContent, }); } else if (ext === ".json") { reply.code(code).send(data); } else { reply.code(code).send(message); } }); // 提供健康检查接口 server.get("/healthz", (_request, reply) => reply.send("OK")); server.listen({ port: 8080, host: "0.0.0.0" }, (_err, address) => { if (_err) throw _err; console.log(`Server listening at ${address}`); });