aps-agent/server/entry/static.ts

53 lines
2.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 客户端静态资源托管(任务 A1.5)。
*
* 构建后的客户端(client/app/dist)由平台服务托管,这样一个端口就能跑起完整平台——
* 桌面端与浏览器都指向同一个服务地址。未构建时返回 false,由调用方按 404 处理。
*/
import { existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import type { ServerResponse } from 'node:http';
import { extname, join, normalize, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
const MIME_TYPES: Record<string, string> = {
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.woff2': 'font/woff2',
};
/** 默认目录:仓库内 client/app/dist;可用 APS_CLIENT_DIST 覆盖 */
export function clientDistDir(): string {
const override = process.env.APS_CLIENT_DIST;
if (override && override.trim() !== '') return resolve(override);
// server/dist/entry/static.js → 仓库根
return fileURLToPath(new URL('../../../client/app/dist', import.meta.url));
}
export function createStaticHandler(root = clientDistDir()): (urlPath: string, res: ServerResponse) => Promise<boolean> {
const rootDir = resolve(root);
const indexFile = join(rootDir, 'index.html');
return async function serveStatic(urlPath: string, res: ServerResponse): Promise<boolean> {
if (!existsSync(indexFile)) return false;
const relativePath = normalize(decodeURIComponent(urlPath)).replace(/^[/\\]+/, '');
const candidate = resolve(rootDir, relativePath);
const insideRoot = candidate === rootDir || candidate.startsWith(rootDir + sep);
if (!insideRoot) return false; // 目录穿越
const file = relativePath === '' || !existsSync(candidate) || !extname(candidate) ? indexFile : candidate;
const body = await readFile(file);
res.writeHead(200, {
'content-type': MIME_TYPES[extname(file)] ?? 'application/octet-stream',
'cache-control': file === indexFile ? 'no-store' : 'public, max-age=3600',
});
res.end(body);
return true;
};
}