/* eslint-disable @typescript-eslint/no-require-imports */ const { createServer } = require('http'); const { parse } = require('url'); const next = require('next'); const port = Number(process.env.PORT || 4000); /** * 自定义 server 若写 `next({ dev: false })` 不传 port,Next 内部仍默认 3000(见 next/dist/server/next.js), * 与 `listen(4000)` 不一致时,307 Location 会变成 `http://jinclab.com:3000/zh` 这类错误地址。 * * 反代终止 TLS 后须传 `X-Forwarded-Proto: https`;若漏传,协议会判成 http。 * 若 Host 误带内网端口,与 NEXT_PUBLIC_SITE_URL 对齐时去掉端口。 */ function normalizeProxyHeaders(req) { const site = process.env.NEXT_PUBLIC_SITE_URL?.trim().replace(/\/$/, ''); if (!site || process.env.NODE_ENV !== 'production') return; let canonicalHost; try { canonicalHost = new URL(site).hostname; } catch { return; } const allowed = new Set([canonicalHost, `www.${canonicalHost}`]); const raw = req.headers.host; if (typeof raw !== 'string') return; const colon = raw.indexOf(':'); const hostnameOnly = colon === -1 ? raw : raw.slice(0, colon); if (!allowed.has(hostnameOnly)) return; if (colon !== -1) { req.headers.host = hostnameOnly; } if (site.startsWith('https:') && !req.headers['x-forwarded-proto']) { req.headers['x-forwarded-proto'] = 'https'; } } // port 必须与 listen 一致,避免 Next 内部仍按 3000 拼绝对 URL(勿改 hostname,以免内部 initURL 变成 127.0.0.1) const app = next({ dev: false, port }); const handle = app.getRequestHandler(); app.prepare().then(() => { const server = createServer((req, res) => { normalizeProxyHeaders(req); handle(req, res, parse(req.url, true)); }); server.on('error', (error) => { if (error.code === 'EADDRINUSE') { const fallbackPort = port + 1; console.warn(`Port ${port} is in use, retrying on ${fallbackPort}...`); server.listen(fallbackPort); return; } throw error; }); server.listen(port); });