feat: 同步服务托管网页应用,daywk 单域名部署就绪

This commit is contained in:
root
2026-10-16 12:05:47 +08:00
parent acfb0cfdd2
commit b73d5ef2cc
16 changed files with 188 additions and 3 deletions
+37
View File
@@ -122,6 +122,22 @@ function readBody(req, limit = 20 * 1024 * 1024) {
})
}
// ---------- 静态托管网页应用 ----------
// 容器内为 /app/dist;本机直跑时回退到仓库内的 intern-log/dist
const DIST_CANDIDATES = [path.join(__dirname, 'dist'), path.join(__dirname, '..', 'intern-log', 'dist')]
const DIST_DIR = process.env.DIST_DIR || DIST_CANDIDATES.find(p => fs.existsSync(path.join(p, 'index.html'))) || path.join(__dirname, 'dist')
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript',
'.css': 'text/css',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.json': 'application/json',
'.webmanifest': 'application/manifest+json',
'.ico': 'image/x-icon',
'.woff2': 'font/woff2'
}
// ---------- 路由 ----------
async function handle(req, res) {
const url = new URL(req.url, 'http://localhost')
@@ -129,6 +145,27 @@ async function handle(req, res) {
if (req.method === 'OPTIONS') { json(res, 204, {}); return }
// 非 API 的 GET 请求:返回网页应用(构建产物)
if (req.method === 'GET' && !route.startsWith('/api')) {
let rel = decodeURIComponent(url.pathname)
if (rel === '/' || rel === '') rel = '/index.html'
const file = path.normalize(path.join(DIST_DIR, rel))
if (!file.startsWith(DIST_DIR)) { json(res, 403, { error: 'forbidden' }); return }
fs.readFile(file, (err, data) => {
if (err) {
fs.readFile(path.join(DIST_DIR, 'index.html'), (e2, d2) => {
if (e2) { json(res, 404, { error: 'not found' }); return }
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
res.end(d2)
})
return
}
res.writeHead(200, { 'Content-Type': MIME[path.extname(file).toLowerCase()] || 'application/octet-stream' })
res.end(data)
})
return
}
if (req.method === 'GET' && route === '/api/health') { json(res, 200, { ok: true }); return }
// SSE 事件流:token 经查询参数传递(EventSource 无法自定义请求头)