#!/usr/bin/env node /** * AI 智能抽背助手 - 原型系统本地预览服务器 * * 使用方法: * node server.js * * 功能: * - 启动本地 HTTP 服务器 * - 自动打开浏览器访问原型系统 * - 支持热重载(修改文件后自动刷新) * * @author generate-prototype-grid skill * @date 2026-03-28 */ const http = require('http'); const fs = require('fs'); const path = require('path'); const { exec } = require('child_process'); const { URL } = require('url'); const PORT = 8080; const PROTOTYPE_DIR = __dirname; const PARENT_DIR = path.dirname(__dirname); // MIME 类型映射 const MIME_TYPES = { '.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'application/javascript; charset=utf-8', '.json': 'application/json; charset=utf-8', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf', '.md': 'text/markdown; charset=utf-8', }; // 创建服务器 const server = http.createServer((req, res) => { // 使用 URL 模块安全解析 URL let urlPath; try { const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`); urlPath = parsedUrl.pathname || '/'; } catch { urlPath = '/'; } // 处理根路径 if (urlPath === '/') { urlPath = '/index.html'; } // 安全验证:防止路径遍历攻击 // 规范化路径并检查是否在允许的目录内 const isPlanningArtifacts = urlPath.startsWith("/planning-artifacts/"); const baseDir = isPlanningArtifacts ? PARENT_DIR : PROTOTYPE_DIR; const relativePath = isPlanningArtifacts ? urlPath : urlPath; const normalizedPath = path.normalize(path.join(baseDir, relativePath)); if (!normalizedPath.startsWith(baseDir)) { // 路径遍历攻击检测 res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' }); res.end('400 - 错误请求

400 - 错误请求

无效的路径

'); return; } const filePath = normalizedPath; const extname = path.extname(filePath).toLowerCase(); // 设置响应头 const contentType = MIME_TYPES[extname] || 'application/octet-stream'; // 读取并返回文件 fs.readFile(filePath, (err, content) => { if (err) { if (err.code === 'ENOENT') { // 文件不存在 res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(` 404 - 文件未找到

404

文件未找到

返回首页
`); } else { // 服务器错误 - 不暴露内部错误详情 res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' }); res.end('500 - 服务器错误

500 - 服务器错误

服务器内部错误,请稍后重试

'); } } else { // 成功返回文件 res.writeHead(200, { 'Content-Type': contentType }); res.end(content); } }); }); // 启动服务器 server.listen(PORT, () => { console.log('='.repeat(60)); console.log(' AI 智能抽背助手 - 原型系统本地预览服务器'); console.log('='.repeat(60)); console.log(''); console.log(` 服务器地址: http://localhost:${PORT}`); console.log(''); console.log(' 可用页面:'); console.log(` - 统一入口: http://localhost:${PORT}/index.html`); console.log(` - 平铺展示: http://localhost:${PORT}/admin-index.html`); console.log(` - 文档中心: http://localhost:${PORT}/docs-index.html`); console.log(''); console.log(' 后台管理页面:'); console.log(` - 数据概览: http://localhost:${PORT}/pages/admin/dashboard.html`); console.log(` - 抽背记录查询: http://localhost:${PORT}/pages/admin/quiz-records.html`); console.log(` - 学生管理: http://localhost:${PORT}/pages/admin/student-management.html`); console.log(` - 知识点管理: http://localhost:${PORT}/pages/admin/knowledge-management.html`); console.log(` - 学习任务管理: http://localhost:${PORT}/pages/admin/task-management.html`); console.log(` - 系统设置: http://localhost:${PORT}/pages/admin/settings.html`); console.log(''); console.log(' 按 Ctrl+C 停止服务器'); console.log('='.repeat(60)); console.log(''); // 自动打开浏览器 const openCommand = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open'; exec(`${openCommand} http://localhost:${PORT}`, (error) => { if (error) { console.log(' 提示: 无法自动打开浏览器,请手动访问上述地址'); } else { console.log(' 已自动打开浏览器'); } console.log(''); }); }); // 优雅退出 process.on('SIGINT', () => { console.log(''); console.log('正在停止服务器...'); server.close(() => { console.log('服务器已停止'); process.exit(0); }); }); module.exports = server;