const express = require('express'); const mysql = require('mysql2/promise'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const cors = require('cors'); const crypto = require('crypto'); const fs = require('fs'); // 使用同步版本的 fs const fsPromises = require('fs').promises; // 使用 promises 版本的 fs const path = require('path'); require('dotenv').config({ path: path.join(__dirname, '.env') }); const CryptoJS = require('crypto-js'); const util = require('util'); const fileUpload = require('express-fileupload'); const app = express(); // 创建日志文件流 const logStream = fs.createWriteStream(path.join(__dirname, 'server.log'), { flags: 'a' }); // 创建管理员日志文件流 const adminLogStream = fs.createWriteStream(path.join(__dirname, 'admin.log'), { flags: 'a' }); // 自定义日志函数 function log(message) { const timestamp = new Date().toISOString(); const logMessage = `${timestamp} - ${message}\n`; console.log(logMessage); logStream.write(logMessage); } // 允许所有源的 CORS 请求 app.use(cors()); app.use(express.json()); app.use(fileUpload({ limits: { fileSize: 5 * 1024 * 1024 }, // 限制文件大小为5MB abortOnLimit: true })); // 创建数据库连接池 const pool = mysql.createPool({ host: process.env.DB_HOST, port: process.env.DB_PORT, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, charset: 'utf8mb4' }); // 创建 SurveyKing 数据库连接池 const surveyKingPool = mysql.createPool({ host: process.env.SurveyKing_DB_HOST, port: process.env.SurveyKing_DB_PORT, user: process.env.SurveyKing_DB_USER, password: process.env.SurveyKing_DB_PASSWORD, database: process.env.SurveyKing_DB_NAME, charset: 'utf8mb4' }); // 在文件顶部的导入语句之后添加 pool.getConnection() .then(connection => { log('Successfully connected to the database.'); connection.release(); }) .catch(err => { log(`Error connecting to the database: ${err}`); }); // 在文件顶部的导入语句之后添加 surveyKingPool.getConnection() .then(connection => { log('Successfully connected to the SurveyKing database.'); connection.release(); }) .catch(err => { log(`Error connecting to the SurveyKing database: ${err}`); }); // 验证令牌的中间件 const authenticateToken = async (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) return res.sendStatus(401); try { const decoded = jwt.verify(token, process.env.JWT_SECRET); const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [decoded.userId]); if (rows.length === 0 || new Date() > new Date(rows[0].expiration_date)) { return res.status(403).json({ error: '无效的令牌或账号已过期' }); } // 检查token是否是最新的 if (token !== rows[0].active_token) { return res.status(403).json({ error: '您的账号已在其他设备登录' }); } req.user = rows[0]; next(); } catch (error) { return res.status(403).json({ error: '无效的令牌' }); } }; // 生成一个简单的密钥对 const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); // 添加获取公钥的路由 app.get('/public-key', (req, res) => { res.json({ publicKey }); }); // 登录路由 app.post('/login', async (req, res) => { const { data, key, iv } = req.body; try { // 解密数据 const decrypted = CryptoJS.AES.decrypt( data, CryptoJS.enc.Base64.parse(key), { iv: CryptoJS.enc.Base64.parse(iv), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ); // 将解密后的数据转换为字符串 const decryptedString = decrypted.toString(CryptoJS.enc.Utf8); // 解析 JSON 数据 const { student_id_or_username, password } = JSON.parse(decryptedString); log(`Login attempt for: ${student_id_or_username}`); try { const [rows] = await pool.query('SELECT * FROM users WHERE username = ? OR student_id = ?', [student_id_or_username, student_id_or_username]); log(`Database query result: ${rows}`); if (rows.length === 0) { log('User not found'); return res.status(401).json({ error: '用户名/邮箱或密码错误' }); } const user = rows[0]; if (new Date() > new Date(user.expiration_date)) { log(`Account expired for user: ${user.username}`); return res.status(403).json({ error: '账户已过期,请联系系统管理员xxx' }); } const isPasswordValid = await bcrypt.compare(password, user.password); log(`Password validation result: ${isPasswordValid}`); if (!isPasswordValid) { log('Invalid password'); return res.status(401).json({ error: '用户名/学号或密码错误' }); } const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '24h' }); // 更新用户的active_token await pool.query('UPDATE users SET active_token = ?, last_login = ? WHERE id = ?', [token, new Date(), user.id]); log(`Login successful for user: ${user.username}`); res.json({ success: true, username: user.username, token, level: user.level, model:LICENSE_INFO.model }); } catch (error) { log(`登录失败: ${error}`); res.status(500).json({ error: '登录失败', details: error.message }); } } catch (error) { log(`登录解密失败: ${error}`); res.status(500).json({ error: '登录失败', details: process.env.NODE_ENV === 'development' ? error.message : undefined }); } }); // 登入路由 app.post('/logout', authenticateToken, (req, res) => { res.json({ success: true }); }); // 检认证状态 app.get('/check-auth', authenticateToken, async (req, res) => { try { const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [req.user.id]); if (rows.length === 0 || new Date() > new Date(rows[0].expiration_date)) { return res.status(403).json({ error: '账户已过期或无效' }); } res.json({ isAuthenticated: true, username: req.user.username, level: req.user.level }); } catch (error) { log(`检查认证状态失败: ${error}`); res.status(500).json({ error: '检查认证状态失败' }); } }); // 验证令牌 app.post('/verify-token', async (req, res) => { const { token } = req.body; if (!token) { return res.json({ valid: false }); } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [decoded.userId]); if (rows.length === 0 || new Date() > new Date(rows[0].expiration_date)) { return res.json({ valid: false }); } res.json({ valid: true, username: rows[0].username, level: rows[0].level }); } catch (error) { res.json({ valid: false }); } }); // 修改获取用户信息路由 app.get('/user-info', authenticateToken, async (req, res) => { try { const [rows] = await pool.query( 'SELECT username, class_name, student_id, organization, created_at, last_login, level FROM users WHERE id = ?', [req.user.id] ); if (rows.length > 0) { res.json({ class_name: rows[0].class_name, username: rows[0].username, student_id: rows[0].student_id, organization: LICENSE_INFO.user, created_at: rows[0].created_at, last_login: rows[0].last_login, level: rows[0].level, model:LICENSE_INFO.model }); } else { res.status(404).json({ error: '用户不存在' }); } } catch (error) { log(`获取用户信息失败: ${error}`); res.status(500).json({ error: '获取用户信息失败' }); } }); // 验证管理员权限 app.get('/verify-admin', authenticateToken, async (req, res) => { try { const [rows] = await pool.query('SELECT level FROM users WHERE id = ?', [req.user.id]); if (rows.length > 0 && rows[0].level >= 7) { res.json({ isAdmin: true }); } else { res.json({ isAdmin: false }); } } catch (error) { log(`验证管理员权限失败: ${error}`); res.status(500).json({ error: '验证管理员权限失败' }); } }); // 查询用户 app.get('/admin/users', authenticateToken, async (req, res) => { try { const [userRows] = await pool.query('SELECT level FROM users WHERE id = ?', [req.user.id]); if (userRows.length === 0 || userRows[0].level < 7) { return res.status(403).json({ error: '没有权限访问此资源' }); } const [rows] = await pool.query( 'SELECT id, username, student_id, class_name, organization, created_at, last_login, level FROM users' ); res.json(rows); } catch (error) { log(`获取用户列表失败: ${error}`); res.status(500).json({ error: '获取用户列表失败' }); } }); // 批量创建用户 app.post('/admin/users', authenticateToken, async (req, res) => { try { // 权限校验:仅管理员(level >= 3)可执行 const [adminCheck] = await pool.query('SELECT level FROM users WHERE id = ?', [req.user.id]); if (adminCheck.length === 0 || adminCheck[0].level < 7) { return res.status(403).json({ success: false, error: '没有权限执行此操作' }); } const { class_name, student_ids } = req.body; if (!class_name || !student_ids) { return res.status(400).json({ success: false, error: '请提供班级和学号' }); } // 支持多种分隔符:空格、英文逗号、中文逗号、换行 const idList = student_ids .split(/[\s,,\n]+/) .map(id => id.trim()) .filter(id => id.length > 0); if (idList.length === 0) { return res.status(400).json({ success: false, error: '未检测到有效学号' }); } // 根据LICENSE_INFO.model确定最大用户数量 let maxUsers = 0; // 默认值 const licenseModel = LICENSE_INFO.model || ''; if (licenseModel.includes('EST-05E')) { maxUsers = 10; } else if (licenseModel.includes('EST-10E')) { maxUsers = 60; } else if (licenseModel.includes('EST-100E')) { maxUsers = 100; } else if (licenseModel.includes('EST-05C')) { maxUsers = 10; } else if (licenseModel.includes('EST-10C')) { maxUsers = 60; } else if (licenseModel.includes('EST-100C')) { maxUsers = 100; } else if (licenseModel.includes('EST-10A')) { maxUsers = 60; } else if (licenseModel.includes('EST-100A')) { maxUsers = 100; }else if (licenseModel.includes('EST-100D')) { maxUsers = 100; } // 检查当前用户数量 const [currentUsers] = await pool.query('SELECT COUNT(*) as count FROM users'); const currentUserCount = currentUsers[0].count -1; // 计算可以创建的最大新用户数量 const maxNewUsers = maxUsers - currentUserCount; // 如果新用户数量超过限制,返回错误 if (idList.length > maxNewUsers) { return res.status(400).json({ success: false, error: `超出许可证用户数量限制,当前许可证(${licenseModel})最多允许${maxUsers}个用户,已有${currentUserCount}个用户,最多可创建${maxNewUsers}个新用户` }); } let createdCount = 0; let skipped = []; for (const sid of idList) { // 检查是否已存在 const [exists] = await pool.query('SELECT id FROM users WHERE student_id = ?', [sid]); if (exists.length > 0) { skipped.push(sid); continue; } const plainPassword = sid; const password = await bcrypt.hash(plainPassword, 10); // saltRounds = 10 const organization = LICENSE_INFO.user; const level = 0 await pool.query( `INSERT INTO users (username, student_id, class_name, organization, level, password, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())`, [sid, sid, class_name, organization, level, password] ); createdCount++; } res.json({ success: true, createdCount, skipped, message: `成功创建 ${createdCount} 个用户,跳过 ${skipped.length} 个已存在的用户`, licenseInfo: { model: licenseModel, maxUsers: maxUsers, currentUsers: currentUserCount + createdCount, remainingSlots: maxUsers - (currentUserCount + createdCount) } }); } catch (error) { console.error('批量创建用户失败:', error); res.status(500).json({ success: false, error: '批量创建用户失败', details: error.message }); } }); // 删除用户 app.delete('/admin/users/:student_id', authenticateToken, async (req, res) => { try { const [adminCheck] = await pool.query('SELECT level FROM users WHERE id = ?', [req.user.id]); if (adminCheck.length === 0 || adminCheck[0].level < 7) { return res.status(403).json({ success: false, error: '没有权限执行此操作' }); } const studentId = req.params.student_id; const [result] = await pool.query('DELETE FROM users WHERE student_id = ?', [studentId]); if (result.affectedRows === 0) { return res.status(404).json({ success: false, error: '未找到该用户' }); } res.json({ success: true, message: `已删除用户 ${studentId}` }); } catch (error) { console.error('删除用户失败:', error); res.status(500).json({ success: false, error: '删除用户失败' }); } }); let onlineUsers = new Map(); let onlineHistory = []; const ONLINE_DATA_FILE = path.join(__dirname, 'online_data.json'); const LONG_TERM_HISTORY_FILE = path.join(__dirname, 'long_term_history.json'); // 加载在线用户数据 async function loadOnlineData() { try { const data = await fsPromises.readFile(ONLINE_DATA_FILE, 'utf8'); const parsedData = JSON.parse(data); onlineHistory = parsedData.history; onlineUsers = new Map(parsedData.users.map(([id, user]) => [parseInt(id), user])); } catch (error) { if (error.code !== 'ENOENT') { console.error('加载在线用户数据失败:', error); } } } // 保存在线用户数据 async function saveOnlineData() { try { const data = JSON.stringify({ history: onlineHistory, users: Array.from(onlineUsers.entries()) }); await fsPromises.writeFile(ONLINE_DATA_FILE, data, 'utf8'); } catch (error) { console.error('保存在线用户数据失败:', error); } } // 更新在线用户并记录历史 async function updateOnlineUsers() { const now = new Date(); // 清理超时的用户(例如,15分钟无活动) for (const [userId, userData] of onlineUsers.entries()) { if (now - new Date(userData.lastActivity) > 5 * 60 * 1000) { onlineUsers.delete(userId); } } const currentOnlineUsers = { time: now.toISOString(), count: onlineUsers.size, users: Array.from(onlineUsers.values()).map(u => u.username) }; onlineHistory.push(currentOnlineUsers); // 将超过24小时的记录移动到长期历史记录 const oneDayAgo = new Date(now - 24 * 60 * 60 * 1000); const oldRecords = onlineHistory.filter(item => new Date(item.time) <= oneDayAgo); onlineHistory = onlineHistory.filter(item => new Date(item.time) > oneDayAgo); await saveOnlineData(); } // 每10秒更新一次在线用户状态 setInterval(updateOnlineUsers, 10000); // 在服务器启动时加载在线用户数据 loadOnlineData().then(() => { console.log('在线用户数据加载完成'); }); // 登录路由 app.post('/login', async (req, res) => { // ... 现有的登录逻辑 ... if (response.data.success) { // 添加用户到在线列表 onlineUsers.set(user.id, { username: user.username, lastActivity: new Date().toISOString() }); await saveOnlineData(); } res.json({ success: true }); }); // 获取在线用户和历史记录 app.get('/online-users', authenticateToken, (req, res) => { res.json({ currentOnline: { count: onlineUsers.size, users: Array.from(onlineUsers.values()).map(u => u.username) }, history: onlineHistory }); }); // 更新用户活动时间 app.post('/update-activity', authenticateToken, async (req, res) => { if (onlineUsers.has(req.user.id)) { onlineUsers.get(req.user.id).lastActivity = new Date().toISOString(); } else { onlineUsers.set(req.user.id, { username: req.user.username, lastActivity: new Date().toISOString() }); } await saveOnlineData(); res.sendStatus(200); }); // 登出路由 app.post('/logout', authenticateToken, async (req, res) => { onlineUsers.delete(req.user.id); await saveOnlineData(); res.json({ success: true }); }); // --------------------------LIC验证------------------- // 全局许可证信息 const LICENSE_INFO = { isValid: false, model: "", user: "", serial: "", activation_code: "", activated_at: "", expires_at: "", gold_service_expires_at: "", issued_at: "", issuer: "", hardware_id: "" // 新增硬件码字段 }; // 获取系统硬件序列号 async function getHardwareSerial() { try { const hardwareSerial = await fsPromises.readFile('/hardware_serial', 'utf8'); return hardwareSerial.trim(); } catch (error) { log(`读取硬件序列号失败: ${error.message}`); throw error; } } // License 验证相关功能 // 获取目录下所有 .lic 文件 async function getLicenseFiles() { try { const licenseDir = path.join(__dirname, 'license'); const files = await fsPromises.readdir(licenseDir); return files.filter(file => file.endsWith('.lic')); } catch (error) { if (error.code === 'ENOENT') { // 如果目录不存在,返回空数组 return []; } log(`获取 License 文件失败: ${error.message}`); throw error; } } // 清空 LICENSE_INFO function clearLicenseInfo() { Object.keys(LICENSE_INFO).forEach(key => { LICENSE_INFO[key] = typeof LICENSE_INFO[key] === 'boolean' ? false : ""; }); } // 读取公钥文件 async function readPublicKey() { try { const pubKeyPath = path.join(__dirname, 'pub.pem'); const publicKeyData = await fsPromises.readFile(pubKeyPath, 'utf8'); return publicKeyData; } catch (error) { log(`读取公钥文件失败: ${error.message}`); throw error; } } // 验证 License 文件 async function verifyLicense(licenseFile, publicKey) { try { const licenseDir = path.join(__dirname, 'license'); const licenseData = await fsPromises.readFile(path.join(licenseDir, licenseFile), 'utf8'); const license = JSON.parse(licenseData); // 解码 payload const payloadStr = Buffer.from(license.payload, 'base64').toString('utf8'); const payload = JSON.parse(payloadStr); // 验证签名 const verify = crypto.createVerify('SHA256'); verify.update(payloadStr); const isValid = verify.verify(publicKey, license.signature, 'base64'); // 验证硬件码匹配 const hardwareSerial = await getHardwareSerial(); const hardwareMatches = payload.hardware_id === hardwareSerial; // 只有在签名验证成功且硬件码匹配时才更新许可证信息 if (isValid && hardwareMatches) { LICENSE_INFO.isValid = true; LICENSE_INFO.model = payload.model || ""; LICENSE_INFO.user = payload.user || ""; LICENSE_INFO.serial = payload.serial || ""; LICENSE_INFO.activation_code = payload.activation_code || ""; LICENSE_INFO.activated_at = payload.activated_at || ""; LICENSE_INFO.expires_at = payload.expires_at || ""; LICENSE_INFO.gold_service_expires_at = payload.gold_service_expires_at || ""; LICENSE_INFO.issued_at = payload.issued_at || ""; LICENSE_INFO.issuer = payload.issuer || ""; LICENSE_INFO.hardware_id = payload.hardware_id || ""; } else { clearLicenseInfo(); } return { isValid, hardwareMatches, licenseFile, payload }; } catch (error) { log(`验证 License 文件失败 (${licenseFile}): ${error.message}`); clearLicenseInfo(); return { isValid: false, hardwareMatches: false, licenseFile, error: error.message }; } } // 验证所有 License 文件 async function verifyAllLicenses() { try { const licenseFiles = await getLicenseFiles(); if (licenseFiles.length === 0) { log('未找到任何 License 文件'); clearLicenseInfo(); return []; } // 只验证最新的 license 文件 const latestLicenseFile = licenseFiles[licenseFiles.length - 1]; log(`验证最新的 License 文件: ${latestLicenseFile}`); try { const publicKey = await readPublicKey(); log('成功读取公钥文件 pub.pem'); const result = await verifyLicense(latestLicenseFile, publicKey); // 输出验证结果 if (result.isValid && result.hardwareMatches) { log(`License 验证成功: ${latestLicenseFile}`); log(`License 信息: ${JSON.stringify(result.payload, null, 2)}`); } else { log(`License 验证失败: ${latestLicenseFile}`); if (!result.isValid) { log('签名验证失败'); } if (!result.hardwareMatches) { log('硬件码不匹配'); } if (result.error) { log(`错误信息: ${result.error}`); } clearLicenseInfo(); } return [result]; } catch (error) { log(`读取公钥失败: ${error.message}`); clearLicenseInfo(); return [{ isValid: false, hardwareMatches: false, licenseFile: latestLicenseFile, error: '无法读取公钥文件' }]; } } catch (error) { log(`验证所有 License 文件失败: ${error.message}`); clearLicenseInfo(); throw error; } } // 获取许可证信息 function getLicenseInfo() { return LICENSE_INFO; } // 添加获取许可证信息的API端点 app.get('/license-info', authenticateToken, (req, res) => { res.json({ success: true, licenseInfo: LICENSE_INFO }); }); // 添加获取产品型号的API端点 app.get('/product-model', (req, res) => { res.json({ success: true, isValid: LICENSE_INFO.isValid, model: LICENSE_INFO.model }); }); // 添加上传许可证文件的API端点 app.post('/upload-license', async (req, res) => { if (!req.files || Object.keys(req.files).length === 0) { return res.status(400).json({ success: false, error: '未上传文件' }); } const licenseFile = req.files.license; // 验证文件扩展名 if (!licenseFile.name.endsWith('.lic')) { return res.status(400).json({ success: false, error: '文件必须是.lic格式' }); } try { // 验证硬件码匹配 const hardwareSerial = await getHardwareSerial(); // 读取并解析上传的 license 文件内容 const licenseContent = licenseFile.data.toString('utf8'); const license = JSON.parse(licenseContent); const payloadStr = Buffer.from(license.payload, 'base64').toString('utf8'); const payload = JSON.parse(payloadStr); // 检查硬件码是否匹配 if (payload.hardware_id !== hardwareSerial) { return res.status(400).json({ success: false, error: '硬件码不匹配,无法使用此许可证' }); } // 确保 license 目录存在 const licenseDir = path.join(__dirname, 'license'); await fsPromises.mkdir(licenseDir, { recursive: true }); // 删除现有的所有 license 文件 const existingFiles = await getLicenseFiles(); for (const file of existingFiles) { await fsPromises.unlink(path.join(licenseDir, file)); log(`删除旧的许可证文件: ${file}`); } // 保存新的 license 文件 await licenseFile.mv(path.join(licenseDir, licenseFile.name)); log(`新的许可证文件 ${licenseFile.name} 上传成功到 license 目录`); // 重新验证许可证 await verifyAllLicenses(); res.json({ success: true, message: '许可证文件上传并验证成功', licenseInfo: LICENSE_INFO }); } catch (error) { log(`许可证文件上传失败: ${error.message}`); res.status(500).json({ success: false, error: '许可证文件上传失败' }); } }); // 检查许可证状态的API端点 app.get('/license-status', (req, res) => { res.json({ success: true, isValid: LICENSE_INFO.isValid }); }); // 监视 license 目录变化 const licenseDir = path.join(__dirname, 'license'); fs.mkdir(licenseDir, { recursive: true }, (err) => { if (err) { log(`创建 license 目录失败: ${err.message}`); return; } fs.watch(licenseDir, async (eventType, filename) => { if (filename && filename.endsWith('.lic')) { log(`检测到 license 目录变化: ${eventType} - ${filename}`); try { await verifyAllLicenses(); } catch (error) { log(`处理 license 目录变化时发生错误: ${error.message}`); } } }); }); // 在服务器启动时验证 License 文件 (async function checkLicensesOnStartup() { log('正在验证 License 文件...'); try { await verifyAllLicenses(); log('License 验证完成'); } catch (error) { log(`License 验证过程中发生错误: ${error.message}`); } })(); // --------------------------------------------------------------- // -----------------添加查询 SurveyKing 答案数据的接口---------- app.get('/survey-answers', async (req, res) => { try { const { org } = req.query; if (!org) { return res.status(400).json({ success: false, error: '请提供组织名称参数 (org)' }); } // 基础查询 const query = ` SELECT project_id, answer, exam_score, update_at FROM t_answer WHERE 1=1 `; // 执行查询 const [rows] = await surveyKingPool.query(query); // 处理结果,解析 answer 列中的 JSON 数据并只返回匹配组织的数据 const processedData = rows.map(row => { try { const answerData = JSON.parse(row.answer); // 查找包含 estorg 的键 let organization = ''; let username = ''; // 遍历对象查找 estorg for (const key in answerData) { if (answerData[key].estorg) { organization = answerData[key].estorg; } if (answerData[key].estuser) { username = answerData[key].estuser; } } // 只返回匹配组织的数据 if (organization && organization.toLowerCase() === org.toLowerCase()) { return { projectId: row.project_id, organization: organization, username: username, score: row.exam_score, submitTime: row.update_at }; } return null; } catch (error) { console.error(`解析答案数据失败: ${error}`); return null; } }).filter(item => item !== null); // 移除不匹配的数据 // 按提交时间降序排序 processedData.sort((a, b) => new Date(b.submitTime) - new Date(a.submitTime)); res.json({ success: true, total: processedData.length, data: processedData }); } catch (error) { console.error(`获取答案数据失败: ${error}`); res.status(500).json({ success: false, error: '获取答案数据失败', message: error.message }); } }); // -------------------------------------------------- // -----------场景化功能切换API----------------------- app.post('/admin/toggle-scenario', authenticateToken, async (req, res) => { try { // 验证管理员权限 const [adminCheck] = await pool.query('SELECT level FROM users WHERE id = ?', [req.user.id]); if (adminCheck.length === 0 || adminCheck[0].level < 7) { return res.status(403).json({ success: false, message: '没有权限执行此操作' }); } const { student_id, new_level } = req.body; // 如果是要关闭场景化功能,直接执行 if (new_level === 0) { const [updateResult] = await pool.query( 'UPDATE users SET level = ? WHERE student_id = ?', [0, student_id] ); if (updateResult.affectedRows === 0) { return res.status(404).json({ success: false, message: '未找到该用户' }); } return res.json({ success: true, message: '已关闭场景化功能', new_level: 0 }); } // 如果是要开启场景化功能,需要检查许可证和当前已开启的用户数量 // 根据LICENSE_INFO.model确定最大用户数 let maxScenarioUsers = 0; const model = LICENSE_INFO.model; switch (model) { case 'EST-05E': maxScenarioUsers = 5; break; case 'EST-10E': maxScenarioUsers = 30; break; case 'EST-100E': maxScenarioUsers = 50; break; case 'EST-05C': maxScenarioUsers = 5; break; case 'EST-10C': maxScenarioUsers = 30; break; case 'EST-100C': maxScenarioUsers = 50; break; case 'EST-10A': maxScenarioUsers = 30; break; case 'EST-100A': maxScenarioUsers = 50; break; case 'EST-100D': maxScenarioUsers = 50; break; default: maxScenarioUsers = 0; } // 如果没有有效的许可证或型号不支持 if (!LICENSE_INFO.isValid || maxScenarioUsers === 0) { return res.status(403).json({ success: false, message: '无有效许可证或当前型号不支持场景化功能' }); } // 查询当前已开启场景化功能的用户数量(level=1或level=4) const [countResult] = await pool.query( 'SELECT COUNT(*) as count FROM users WHERE level = 1 OR level = 4' ); const currentCount = countResult[0].count; // 如果当前数量已达到最大值,拒绝请求 if (currentCount >= maxScenarioUsers) { return res.status(403).json({ success: false, message: `已达到最大场景化用户数量限制${maxScenarioUsers}位用户` }); } // 更新用户等级为1(开启场景化功能) const [updateResult] = await pool.query( 'UPDATE users SET level = ? WHERE student_id = ?', [1, student_id] ); if (updateResult.affectedRows === 0) { return res.status(404).json({ success: false, message: '未找到该用户' }); } return res.json({ success: true, message: '已开启场景化功能', new_level: 1 }); } catch (error) { console.error('切换场景化功能失败:', error); res.status(500).json({ success: false, message: '操作失败,请稍后再试', error: error.message }); } }); // --------------------------------------------------- // ---------------------宿主机网络配置---------------------------- // 网络配置相关 API const NETWORK_CONFIG_FILE = path.join(__dirname, 'network'); // 读取网络配置 async function readNetworkConfig() { try { const config = await fsPromises.readFile(NETWORK_CONFIG_FILE, 'utf8'); const configObj = {}; config.split('\n').forEach(line => { const [key, value] = line.split('=').map(part => part.trim()); if (key && value) { // 移除引号 configObj[key] = value.replace(/^"(.*)"$/, '$1'); } }); return configObj; } catch (error) { log(`读取网络配置失败: ${error.message}`); throw error; } } // 写入网络配置 async function writeNetworkConfig(config) { try { let configContent = ''; for (const [key, value] of Object.entries(config)) { // DNS 需要特殊处理,添加引号 if (key === 'DNS') { configContent += `${key}="${value}"\n`; } else { configContent += `${key}=${value}\n`; } } await fsPromises.writeFile(NETWORK_CONFIG_FILE, configContent); } catch (error) { log(`写入网络配置失败: ${error.message}`); throw error; } } // 获取网络配置 API app.get('/network-config', authenticateToken, async (req, res) => { try { const config = await readNetworkConfig(); res.json({ success: true, config }); } catch (error) { res.status(500).json({ success: false, error: '获取网络配置失败' }); } }); // 更新网络配置 API app.post('/network-config', authenticateToken, async (req, res) => { try { const { config } = req.body; // 基本验证 if (!config || typeof config !== 'object') { return res.status(400).json({ success: false, error: '无效的配置数据' }); } if (!config.BOOTPROTO) { return res.status(400).json({ success: false, error: '缺少必需字段: BOOTPROTO' }); } // 限定 BOOTPROTO 的取值 if (!['dhcp', 'static'].includes(config.BOOTPROTO)) { return res.status(400).json({ success: false, error: 'BOOTPROTO 仅支持 dhcp 或 static' }); } // 如果是静态 IP,验证额外字段 if (config.BOOTPROTO === 'static') { const staticFields = ['IPADDR', 'NETMASK', 'GATEWAY', 'DNS']; for (const field of staticFields) { if (!config[field]) { return res.status(400).json({ success: false, error: `静态 IP 配置缺少必需字段: ${field}` }); } } } const existingConfig = await readNetworkConfig(); const allowedUpdateKeys = ['BOOTPROTO', 'IPADDR', 'NETMASK', 'GATEWAY', 'DNS']; const updatedConfig = { ...existingConfig }; for (const key of allowedUpdateKeys) { if (config[key] !== undefined) { updatedConfig[key] = config[key]; } } if (config.BOOTPROTO === 'dhcp') { for (const key of ['IPADDR', 'NETMASK', 'GATEWAY', 'DNS']) { delete updatedConfig[key]; } } await writeNetworkConfig(updatedConfig); res.json({ success: true, message: '网络配置已更新' }); } catch (error) { res.status(500).json({ success: false, error: '更新网络配置失败' }); } }); // ------------------------------------------------------ // -------------------------API启动设置----------------- const PORT = process.env.PORT || 3000; // 如果作为主模块运行(独立启动),才开启监听;作为子服务被挂载时不监听端口 if (require.main === module) { const PORT = process.env.PORT || 3000; app.listen(PORT, () => { log(`Server running on port ${PORT}`); }); } // 导出 Express 应用,供主入口统一挂载 module.exports = app; // 在程序退出时关闭日志流 process.on('exit', () => { logStream.end(); }); // 捕获未捕获的异常并记录日志 process.on('uncaughtException', (error) => { log(`Uncaught Exception: ${error.message}`); process.exit(1); }); // 捕获未处理的 Promise 拒绝并记录日志 process.on('unhandledRejection', (reason, promise) => { log(`Unhandled Rejection at: ${promise}, reason: ${reason}`); });