diff --git a/login.html b/login.html
new file mode 100644
index 0000000..25223e1
--- /dev/null
+++ b/login.html
@@ -0,0 +1,166 @@
+
+
+
+
+
+原型访问登录 - 健康长庆升级
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..15a5fc3
--- /dev/null
+++ b/server.js
@@ -0,0 +1,228 @@
+/**
+ * 原型站轻量登录网关
+ *
+ * 功能:
+ * - 所有静态文件访问前必须通过 Cookie 认证
+ * - 未登录自动跳转到自定义登录页
+ * - 密码写死在本文件中,按需修改
+ *
+ * 启动:node server.js
+ * 默认端口:3000(可通过环境变量 PORT 修改)
+ */
+
+const http = require('http');
+const fs = require('fs');
+const path = require('path');
+const url = require('url');
+const crypto = require('crypto');
+const querystring = require('querystring');
+
+/* ========== 配置区 ========== */
+
+/** 访问密码(按需修改) */
+const PASSWORD = 'JKCQ_2026!';
+
+/** Cookie 签名密钥(部署时建议替换为随机字符串) */
+const SECRET = 'hc-prototype-secret-key-2026';
+
+/** Cookie 名称 */
+const COOKIE_NAME = 'prototype_auth';
+
+/** Cookie 有效期(秒),默认 7 天 */
+const COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
+
+/** 服务端口 */
+const PORT = process.env.PORT || 3010;
+
+/** 静态文件根目录(当前目录) */
+const STATIC_ROOT = __dirname;
+
+/** 不需要登录即可访问的路径 */
+const PUBLIC_PATHS = ['/login', '/login.html', '/favicon.ico'];
+
+/* ========== 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/plain; charset=utf-8',
+};
+
+/* ========== 工具函数 ========== */
+
+/** 生成签名 Cookie 值 */
+function signToken() {
+ var expires = Date.now() + COOKIE_MAX_AGE * 1000;
+ var payload = 'ok|' + expires;
+ var sig = crypto.createHmac('sha256', SECRET).update(payload).digest('hex').slice(0, 16);
+ return payload + '|' + sig;
+}
+
+/** 校验签名 Cookie 值 */
+function verifyToken(token) {
+ if (!token) return false;
+ var parts = token.split('|');
+ if (parts.length !== 3) return false;
+ var status = parts[0];
+ var expires = parseInt(parts[1], 10);
+ var sig = parts[2];
+ if (status !== 'ok' || isNaN(expires)) return false;
+ if (Date.now() > expires) return false;
+ var expected = crypto.createHmac('sha256', SECRET).update(status + '|' + expires).digest('hex').slice(0, 16);
+ return sig === expected;
+}
+
+/** 从请求头解析指定 Cookie */
+function getCookie(req, name) {
+ var header = req.headers.cookie || '';
+ var cookies = header.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var pair = cookies[i].trim().split('=');
+ if (pair[0] === name) return decodeURIComponent(pair.slice(1).join('='));
+ }
+ return null;
+}
+
+/** 读取 POST 请求体 */
+function readBody(req, callback) {
+ var chunks = [];
+ req.on('data', function(chunk) { chunks.push(chunk); });
+ req.on('end', function() { callback(Buffer.concat(chunks).toString()); });
+}
+
+/** 安全校验回跳地址:只允许站内相对路径 */
+function safeRedirect(target) {
+ if (!target || typeof target !== 'string') return '/';
+ if (target.charAt(0) !== '/') return '/';
+ if (target.indexOf('//') === 0) return '/';
+ return target;
+}
+
+/** 发送静态文件 */
+function serveFile(res, filePath) {
+ /* 防止路径穿越攻击 */
+ var resolved = path.resolve(filePath);
+ if (resolved.indexOf(path.resolve(STATIC_ROOT)) !== 0) {
+ res.writeHead(403);
+ res.end('Forbidden');
+ return;
+ }
+
+ /* 禁止访问敏感文件 */
+ var basename = path.basename(resolved);
+ if (basename === 'server.js' || basename === '.env' || basename === '.gitignore') {
+ res.writeHead(403);
+ res.end('Forbidden');
+ return;
+ }
+
+ fs.stat(resolved, function(err, stats) {
+ if (err || !stats.isFile()) {
+ /* 目录请求尝试补 index.html */
+ if (!err && stats.isDirectory()) {
+ var indexFile = path.join(resolved, 'index.html');
+ serveFile(res, indexFile);
+ return;
+ }
+ res.writeHead(404);
+ res.end('Not Found');
+ return;
+ }
+
+ var ext = path.extname(resolved).toLowerCase();
+ var contentType = MIME_TYPES[ext] || 'application/octet-stream';
+
+ res.writeHead(200, { 'Content-Type': contentType, 'Content-Length': stats.size });
+ fs.createReadStream(resolved).pipe(res);
+ });
+}
+
+/* ========== 请求处理 ========== */
+
+var server = http.createServer(function(req, res) {
+ var parsed = url.parse(req.url, true);
+ var pathname = decodeURIComponent(parsed.pathname);
+
+ /* --- POST /simple-login:校验密码 --- */
+ if (req.method === 'POST' && pathname === '/simple-login') {
+ readBody(req, function(body) {
+ var data = querystring.parse(body);
+ var password = data.password || '';
+ var redirect = safeRedirect(data.redirect);
+
+ if (password !== PASSWORD) {
+ res.writeHead(302, { 'Location': '/login.html?error=1&redirect=' + encodeURIComponent(redirect) });
+ res.end();
+ return;
+ }
+
+ var token = signToken();
+ var cookie = COOKIE_NAME + '=' + encodeURIComponent(token)
+ + '; Path=/; HttpOnly; SameSite=Lax; Max-Age=' + COOKIE_MAX_AGE;
+
+ res.writeHead(302, {
+ 'Set-Cookie': cookie,
+ 'Location': redirect
+ });
+ res.end();
+ });
+ return;
+ }
+
+ /* --- POST /simple-logout:退出登录 --- */
+ if (req.method === 'POST' && pathname === '/simple-logout') {
+ var clearCookie = COOKIE_NAME + '=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0';
+ res.writeHead(302, { 'Set-Cookie': clearCookie, 'Location': '/login.html' });
+ res.end();
+ return;
+ }
+
+ /* --- GET /simple-logout:也支持 GET 方式退出 --- */
+ if (req.method === 'GET' && pathname === '/simple-logout') {
+ var clearCookie2 = COOKIE_NAME + '=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0';
+ res.writeHead(302, { 'Set-Cookie': clearCookie2, 'Location': '/login.html' });
+ res.end();
+ return;
+ }
+
+ /* --- 公开路径放行 --- */
+ var isPublic = PUBLIC_PATHS.some(function(p) { return pathname === p; });
+
+ if (isPublic) {
+ var loginFile = pathname === '/login' ? '/login.html' : pathname;
+ var fullPath = path.join(STATIC_ROOT, loginFile);
+ serveFile(res, fullPath);
+ return;
+ }
+
+ /* --- 鉴权:检查 Cookie --- */
+ var token = getCookie(req, COOKIE_NAME);
+ if (!verifyToken(token)) {
+ res.writeHead(302, { 'Location': '/login.html?redirect=' + encodeURIComponent(pathname) });
+ res.end();
+ return;
+ }
+
+ /* --- 已登录:返回静态文件 --- */
+ if (pathname === '/') pathname = '/index.html';
+
+ var filePath = path.join(STATIC_ROOT, pathname);
+ serveFile(res, filePath);
+});
+
+server.listen(PORT, function() {
+ console.log('[原型网关] 已启动 -> http://localhost:' + PORT);
+ console.log('[原型网关] 访问密码: ' + PASSWORD);
+ console.log('[原型网关] 按 Ctrl+C 停止');
+});