/** * NAC原生认证模块 * 直连 nac_id MySQL数据库进行用户认证 * * 安全说明:所有密钥通过 secrets.ts 模块读取,绝不硬编码 */ import mysql from "mysql2/promise"; import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import { getNacMysqlUrl, getNacJwtSecret } from "./secrets"; let pool: mysql.Pool | null = null; function getNacMysqlPool(): mysql.Pool { if (pool) return pool; const url = new URL(getNacMysqlUrl()); pool = mysql.createPool({ host: url.hostname, port: parseInt(url.port || "3306"), user: url.username, password: url.password, database: url.pathname.slice(1), waitForConnections: true, connectionLimit: 5, connectTimeout: 10000, }); return pool; } export interface NacUser { id: number; name: string; email: string; kyc_level: number; node_status: string; is_active: number; role: string; // admin | reviewer | legal } /** * 根据用户的KYC等级和节点状态确定管理后台角色 * - constitutional节点 + kyc_level>=2 → admin * - kyc_level>=1 → legal * - 其他 → reviewer */ function resolveRole(kyc_level: number, node_status: string): string { if (kyc_level >= 2 && node_status === "constitutional") return "admin"; if (kyc_level >= 1) return "legal"; return "reviewer"; } /** * 使用NAC账户(email+password)登录 * 返回用户信息和JWT令牌 */ export async function loginWithNacCredentials( email: string, password: string ): Promise<{ user: NacUser; token: string } | null> { const pool = getNacMysqlPool(); const [rows] = await pool.execute( "SELECT id, name, email, password, kyc_level, node_status, is_active FROM users WHERE email = ? AND is_active = 1", [email] ); if (rows.length === 0) return null; const dbUser = rows[0]; const valid = await bcrypt.compare(password, dbUser.password); if (!valid) return null; const role = resolveRole(dbUser.kyc_level, dbUser.node_status); const user: NacUser = { id: dbUser.id, name: dbUser.name, email: dbUser.email, kyc_level: dbUser.kyc_level, node_status: dbUser.node_status, is_active: dbUser.is_active, role, }; const token = jwt.sign( { id: user.id, email: user.email, role: user.role }, getNacJwtSecret(), { expiresIn: "24h" } ); return { user, token }; } /** * 验证JWT令牌有效性 */ export function verifyNacToken(token: string): { id: number; email: string; role: string } | null { try { return jwt.verify(token, getNacJwtSecret()) as { id: number; email: string; role: string }; } catch { return null; } } /** * 通过ID查询NAC用户 */ export async function getNacUserById(id: number): Promise { const pool = getNacMysqlPool(); const [rows] = await pool.execute( "SELECT id, name, email, kyc_level, node_status, is_active FROM users WHERE id = ? AND is_active = 1", [id] ); if (rows.length === 0) return null; const r = rows[0]; return { ...r, role: resolveRole(r.kyc_level, r.node_status) } as NacUser; } /** * 获取NAC用户列表(管理员功能) */ export async function listNacUsers(limit = 50, offset = 0): Promise { const pool = getNacMysqlPool(); const [rows] = await pool.execute( "SELECT id, name, email, kyc_level, node_status, is_active, created_at, last_login_at FROM users ORDER BY id DESC LIMIT ? OFFSET ?", [limit, offset] ); return rows.map((r) => ({ ...r, role: resolveRole(r.kyc_level, r.node_status) } as NacUser)); } /** * 获取NAC用户总数 */ export async function getNacUserCount(): Promise { const pool = getNacMysqlPool(); const [rows] = await pool.execute("SELECT COUNT(*) as cnt FROM users"); return (rows[0] as mysql.RowDataPacket)?.cnt || 0; }