/** * 认证中间件 * 通过外部 API 验证 Token */ import fetch from 'node-fetch'; import { prisma } from '../db/client.js'; // 是否禁用认证(开发/测试模式) const AUTH_DISABLED = process.env.AUTH_DISABLED === 'true' || process.env.NODE_ENV === 'test'; // 默认认证 API URL const DEFAULT_AUTH_CHECK_URL = 'https://sxwz.xueai.art/api/auth/check/checkTokenRn'; // Token 缓存(减少外部 API 调用) const tokenCache = new Map(); const CACHE_TTL = 5 * 60 * 1000; // 5 分钟 // 测试用户(认证禁用时使用) const TEST_USER = { id: 'test-user-001', username: 'testuser', nickname: '测试用户' }; /** * 验证 Token * @param {string} token - JWT Token * @returns {Promise} 用户信息或 null */ export async function verifyToken(token) { if (!token) return null; // 1. 检查缓存 const cached = tokenCache.get(token); if (cached && cached.expiresAt > Date.now()) { return cached.user; } // 2. 调用外部 API 验证 const authCheckUrl = process.env.AUTH_CHECK_URL || DEFAULT_AUTH_CHECK_URL; const checkUrl = `${authCheckUrl}/${token}`; try { const response = await fetch(checkUrl, { method: 'GET', headers: { 'Accept': 'application/json', 'User-Agent': 'PaperBurner-LocalProxy/1.0' }, signal: AbortSignal.timeout(5000) // 5 秒超时 }); if (!response.ok) { console.warn('[Auth] Token verification failed:', response.status); return null; } const data = await response.json(); // 检查响应格式 if (data.code !== '0' && data.success !== true) { console.warn('[Auth] Token verification returned error:', data.msg || data.message); return null; } const user = data.data; if (!user || !user.id) { console.warn('[Auth] Invalid user data in response'); return null; } // 3. 缓存结果 tokenCache.set(token, { user: { id: String(user.id), username: user.username, nickname: user.nickname || user.username }, expiresAt: Date.now() + CACHE_TTL }); return user; } catch (error) { console.error('[Auth] Token verification error:', error.message); return null; } } /** * 确保用户存在(首次访问时自动创建) * @param {string} userId - 用户 ID * @param {string} name - 用户名称 */ export async function ensureUserExists(userId, name) { try { await prisma.user.upsert({ where: { id: userId }, update: {}, // 不更新任何字段 create: { id: userId, name: name || `用户${userId.slice(-6)}` } }); } catch (error) { console.error('[Auth] Failed to ensure user exists:', error.message); } } /** * 认证中间件 * 验证 Token 并自动创建用户 */ export async function requireAuth(req, res, next) { // 认证禁用模式(开发/测试) if (AUTH_DISABLED) { await ensureUserExists(TEST_USER.id, TEST_USER.nickname); req.user = TEST_USER; if (typeof next === 'function') { next(); } return; } const authHeader = req.headers.authorization; const token = authHeader?.split(' ')[1]; if (!token) { res.writeHead(401, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': req.headers.origin || '*' }); res.end(JSON.stringify({ error: 'Token required' })); return; } const user = await verifyToken(token); if (!user) { res.writeHead(401, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': req.headers.origin || '*' }); res.end(JSON.stringify({ error: 'Invalid or expired token' })); return; } // 自动创建用户(首次访问) await ensureUserExists(user.id, user.nickname || user.username); // 将用户信息附加到请求对象 req.user = user; // 调用下一个处理器 if (typeof next === 'function') { next(); } } /** * 可选认证中间件 * 如果有 Token 则验证,没有则跳过 */ export async function optionalAuth(req, res, next) { const authHeader = req.headers.authorization; const token = authHeader?.split(' ')[1]; if (token) { const user = await verifyToken(token); if (user) { await ensureUserExists(user.id, user.nickname || user.username); req.user = user; } } if (typeof next === 'function') { next(); } } /** * 清理过期的 Token 缓存 */ export function cleanupTokenCache() { const now = Date.now(); for (const [token, data] of tokenCache.entries()) { if (data.expiresAt < now) { tokenCache.delete(token); } } } // 每 10 分钟清理一次缓存 setInterval(cleanupTokenCache, 10 * 60 * 1000); export default { verifyToken, ensureUserExists, requireAuth, optionalAuth };