Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

227 lines
6.0 KiB
JavaScript
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.

/**
* persona-studio · 登录校验路由
* POST /api/ps/auth/login { dev_id: "EXP-000" }
* GET /api/ps/auth/session { token }
* POST /api/ps/auth/wechat/callback { code } — 微信登录回调(待配置)
* POST /api/ps/auth/bind { openid, dev_id } — 绑定开发者编号(待配置)
*
* 📜 Copyright: 国作登字-2026-A-00037559
*/
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const HUMAN_REGISTRY_PATH = path.join(__dirname, '..', '..', 'brain', 'human-registry.json');
const REGISTRY_PATH = path.join(__dirname, '..', '..', 'brain', 'registry.json');
const USERS_DB_PATH = path.join(__dirname, '..', '..', '..', 'data', 'users.json');
// In-memory session store (server restart clears sessions)
const sessions = new Map();
const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
// Periodic session cleanup (every hour)
setInterval(() => {
const now = Date.now();
for (const [token, session] of sessions) {
if (now - new Date(session.created_at).getTime() > SESSION_TTL_MS) {
sessions.delete(token);
}
}
}, 60 * 60 * 1000);
function loadHumanRegistry() {
try {
return JSON.parse(fs.readFileSync(HUMAN_REGISTRY_PATH, 'utf-8'));
} catch {
return { developers: [] };
}
}
function loadRegistry() {
try {
return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8'));
} catch {
return { developers: {}, guest_mode: {} };
}
}
function loadUsersDB() {
try {
return JSON.parse(fs.readFileSync(USERS_DB_PATH, 'utf-8'));
} catch {
return { users: {}, dev_id_map: {} };
}
}
function findDeveloper(devId) {
const humanReg = loadHumanRegistry();
if (humanReg.developers && Array.isArray(humanReg.developers)) {
const found = humanReg.developers.find(d => d.exp_id === devId);
if (found) {
return { name: found.name, status: found.status, role: found.role };
}
}
const registry = loadRegistry();
const entry = registry.developers && registry.developers[devId];
if (entry) {
return { name: entry.name, status: entry.status, role: entry.role };
}
return null;
}
// POST /api/ps/auth/login
router.post('/login', (req, res) => {
const { dev_id } = req.body || {};
// 访客体验模式
if (dev_id === 'GUEST') {
const registry = loadRegistry();
const guestConfig = registry.guest_mode || {};
if (!guestConfig.enabled) {
return res.status(403).json({
error: true,
code: 'GUEST_DISABLED',
message: '访客体验暂未开放正式编号用户EXP-XXX可正常登录'
});
}
const token = crypto.randomBytes(32).toString('hex');
sessions.set(token, {
dev_id: 'GUEST',
name: guestConfig.name || '访客体验者',
role: 'visitor',
created_at: new Date().toISOString()
});
return res.json({
error: false,
dev_id: 'GUEST',
name: guestConfig.name || '访客体验者',
status: 'guest',
role: 'visitor',
token,
auth_method: 'guest'
});
}
if (!dev_id || !/^EXP-\d{3,}$/.test(dev_id)) {
return res.status(400).json({
error: true,
code: 'INVALID_ID',
message: '编号格式不正确,请使用 EXP-XXX 格式'
});
}
const entry = findDeveloper(dev_id);
if (!entry) {
return res.status(404).json({
error: true,
code: 'NOT_FOUND',
message: '编号未注册,请联系管理员获取编号'
});
}
if (entry.status !== 'active' && entry.status !== 'pending_activation') {
return res.status(403).json({
error: true,
code: 'INACTIVE',
message: '编号未激活,请联系管理员'
});
}
// 生成 session token
const token = crypto.randomBytes(32).toString('hex');
sessions.set(token, {
dev_id,
name: entry.name,
role: entry.role || 'developer',
created_at: new Date().toISOString()
});
res.json({
error: false,
dev_id,
name: entry.name,
status: entry.status,
role: entry.role || 'developer',
token,
auth_method: 'dev_id'
});
});
// GET /api/ps/auth/session — 验证 session token
router.get('/session', (req, res) => {
const token = req.headers['x-session-token'] || req.query.token;
if (!token) {
return res.status(401).json({
error: true,
code: 'NO_TOKEN',
message: '未提供 session token'
});
}
const session = sessions.get(token);
if (!session) {
return res.status(401).json({
error: true,
code: 'INVALID_TOKEN',
message: 'Session 已过期或无效'
});
}
res.json({
error: false,
dev_id: session.dev_id,
name: session.name,
role: session.role
});
});
// POST /api/ps/auth/wechat/callback — 微信登录回调(预留接口)
router.post('/wechat/callback', (req, res) => {
// 微信登录需要冰朔提供 AppID/AppSecret 后才能启用
// 当前返回提示信息
res.status(501).json({
error: true,
code: 'WECHAT_NOT_CONFIGURED',
message: '微信登录尚未配置。需要冰朔提供微信开放平台 AppID/AppSecret。当前请使用开发者编号登录。'
});
});
// POST /api/ps/auth/bind — 绑定微信 openid 和开发者编号(预留接口)
router.post('/bind', (req, res) => {
res.status(501).json({
error: true,
code: 'BIND_NOT_CONFIGURED',
message: '绑定功能尚未启用。需要先完成微信登录配置。'
});
});
// GET /api/ps/auth/methods — 获取可用登录方式
router.get('/methods', (_req, res) => {
const usersDB = loadUsersDB();
const methods = usersDB.auth_methods || {
dev_id: { enabled: true },
guest: { enabled: true },
wechat: { enabled: false }
};
res.json({
error: false,
methods: {
dev_id: { enabled: methods.dev_id ? methods.dev_id.enabled : true },
guest: { enabled: methods.guest ? methods.guest.enabled : true },
wechat: { enabled: methods.wechat ? methods.wechat.enabled : false }
}
});
});
module.exports = router;