Download src/routes/admin.js from ASEM12345/anti_api: direct link, hf CLI and curl.
- Browser
- Download file 12.2 kB
-
https://huggingface.co/spaces/ASEM12345/anti_api/resolve/main/src/routes/admin.js
- Command line
-
hf download hf://spaces/ASEM12345/anti_api/src/routes/admin.js
-
curl -L -o admin.js https://huggingface.co/spaces/ASEM12345/anti_api/resolve/main/src/routes/admin.js
12.2 kB
| import express from 'express'; | |
| import { generateToken, authMiddleware } from '../auth/jwt.js'; | |
| import tokenManager from '../auth/token_manager.js'; | |
| import quotaManager from '../auth/quota_manager.js'; | |
| import oauthManager from '../auth/oauth_manager.js'; | |
| import config, { getConfigJson, saveConfigJson } from '../config/config.js'; | |
| import logger from '../utils/logger.js'; | |
| import { parseEnvFile, updateEnvFile } from '../utils/envParser.js'; | |
| import { reloadConfig } from '../utils/configReloader.js'; | |
| import { deepMerge } from '../utils/deepMerge.js'; | |
| import { getModelsWithQuotas } from '../api/client.js'; | |
| import { getEnvPath } from '../utils/paths.js'; | |
| import dotenv from 'dotenv'; | |
| const envPath = getEnvPath(); | |
| const router = express.Router(); | |
| // 登录速率限制 - 防止暴力破解 | |
| const loginAttempts = new Map(); // IP -> { count, lastAttempt, blockedUntil } | |
| const MAX_LOGIN_ATTEMPTS = 5; | |
| const BLOCK_DURATION = 5 * 60 * 1000; // 5分钟 | |
| const ATTEMPT_WINDOW = 15 * 60 * 1000; // 15分钟窗口 | |
| function getClientIP(req) { | |
| return req.headers['x-forwarded-for']?.split(',')[0]?.trim() || | |
| req.headers['x-real-ip'] || | |
| req.connection?.remoteAddress || | |
| req.ip || | |
| 'unknown'; | |
| } | |
| function checkLoginRateLimit(ip) { | |
| const now = Date.now(); | |
| const attempt = loginAttempts.get(ip); | |
| if (!attempt) return { allowed: true }; | |
| // 检查是否被封禁 | |
| if (attempt.blockedUntil && now < attempt.blockedUntil) { | |
| const remainingSeconds = Math.ceil((attempt.blockedUntil - now) / 1000); | |
| return { | |
| allowed: false, | |
| message: `登录尝试过多,请 ${remainingSeconds} 秒后重试`, | |
| remainingSeconds | |
| }; | |
| } | |
| // 清理过期的尝试记录 | |
| if (now - attempt.lastAttempt > ATTEMPT_WINDOW) { | |
| loginAttempts.delete(ip); | |
| return { allowed: true }; | |
| } | |
| return { allowed: true }; | |
| } | |
| function recordLoginAttempt(ip, success) { | |
| const now = Date.now(); | |
| if (success) { | |
| // 登录成功,清除记录 | |
| loginAttempts.delete(ip); | |
| return; | |
| } | |
| // 登录失败,记录尝试 | |
| const attempt = loginAttempts.get(ip) || { count: 0, lastAttempt: now }; | |
| attempt.count++; | |
| attempt.lastAttempt = now; | |
| // 超过最大尝试次数,封禁 | |
| if (attempt.count >= MAX_LOGIN_ATTEMPTS) { | |
| attempt.blockedUntil = now + BLOCK_DURATION; | |
| logger.warn(`IP ${ip} 因登录失败次数过多被暂时封禁`); | |
| } | |
| loginAttempts.set(ip, attempt); | |
| } | |
| // 登录接口 | |
| router.post('/login', (req, res) => { | |
| const clientIP = getClientIP(req); | |
| // 检查速率限制 | |
| const rateCheck = checkLoginRateLimit(clientIP); | |
| if (!rateCheck.allowed) { | |
| return res.status(429).json({ | |
| success: false, | |
| message: rateCheck.message, | |
| retryAfter: rateCheck.remainingSeconds | |
| }); | |
| } | |
| const { username, password } = req.body; | |
| // 验证输入 | |
| if (!username || !password || typeof username !== 'string' || typeof password !== 'string') { | |
| return res.status(400).json({ success: false, message: '用户名和密码必填' }); | |
| } | |
| // 限制输入长度防止 DoS | |
| if (username.length > 100 || password.length > 100) { | |
| return res.status(400).json({ success: false, message: '输入过长' }); | |
| } | |
| if (username === config.admin.username && password === config.admin.password) { | |
| recordLoginAttempt(clientIP, true); | |
| const token = generateToken({ username, role: 'admin' }); | |
| res.json({ success: true, token }); | |
| } else { | |
| recordLoginAttempt(clientIP, false); | |
| res.status(401).json({ success: false, message: '用户名或密码错误' }); | |
| } | |
| }); | |
| // Token管理API - 需要JWT认证 | |
| router.get('/tokens', authMiddleware, async (req, res) => { | |
| try { | |
| const tokens = await tokenManager.getTokenList(); | |
| res.json({ success: true, data: tokens }); | |
| } catch (error) { | |
| logger.error('获取Token列表失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| router.post('/tokens', authMiddleware, async (req, res) => { | |
| const { access_token, refresh_token, expires_in, timestamp, enable, projectId, email } = req.body; | |
| if (!access_token || !refresh_token) { | |
| return res.status(400).json({ success: false, message: 'access_token和refresh_token必填' }); | |
| } | |
| const tokenData = { access_token, refresh_token, expires_in }; | |
| if (timestamp) tokenData.timestamp = timestamp; | |
| if (enable !== undefined) tokenData.enable = enable; | |
| if (projectId) tokenData.projectId = projectId; | |
| if (email) tokenData.email = email; | |
| try { | |
| const result = await tokenManager.addToken(tokenData); | |
| res.json(result); | |
| } catch (error) { | |
| logger.error('添加Token失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| router.put('/tokens/:refreshToken', authMiddleware, async (req, res) => { | |
| const { refreshToken } = req.params; | |
| const updates = req.body; | |
| try { | |
| const result = await tokenManager.updateToken(refreshToken, updates); | |
| res.json(result); | |
| } catch (error) { | |
| logger.error('更新Token失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| router.delete('/tokens/:refreshToken', authMiddleware, async (req, res) => { | |
| const { refreshToken } = req.params; | |
| try { | |
| const result = await tokenManager.deleteToken(refreshToken); | |
| res.json(result); | |
| } catch (error) { | |
| logger.error('删除Token失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| router.post('/tokens/reload', authMiddleware, async (req, res) => { | |
| try { | |
| await tokenManager.reload(); | |
| res.json({ success: true, message: 'Token已热重载' }); | |
| } catch (error) { | |
| logger.error('热重载失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| // 刷新指定Token的access_token | |
| router.post('/tokens/:refreshToken/refresh', authMiddleware, async (req, res) => { | |
| const { refreshToken } = req.params; | |
| try { | |
| logger.info('正在刷新token...'); | |
| const tokens = await tokenManager.getTokenList(); | |
| const tokenData = tokens.find(t => t.refresh_token === refreshToken); | |
| if (!tokenData) { | |
| return res.status(404).json({ success: false, message: 'Token不存在' }); | |
| } | |
| // 调用 tokenManager 的刷新方法 | |
| const refreshedToken = await tokenManager.refreshToken(tokenData); | |
| res.json({ success: true, message: 'Token刷新成功', data: { expires_in: refreshedToken.expires_in, timestamp: refreshedToken.timestamp } }); | |
| } catch (error) { | |
| logger.error('刷新Token失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| router.post('/oauth/exchange', authMiddleware, async (req, res) => { | |
| const { code, port } = req.body; | |
| if (!code || !port) { | |
| return res.status(400).json({ success: false, message: 'code和port必填' }); | |
| } | |
| try { | |
| const account = await oauthManager.authenticate(code, port); | |
| const message = account.hasQuota | |
| ? 'Token添加成功' | |
| : 'Token添加成功(该账号无资格,已自动使用随机ProjectId)'; | |
| res.json({ success: true, data: account, message, fallbackMode: !account.hasQuota }); | |
| } catch (error) { | |
| logger.error('认证失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| // 获取配置 | |
| router.get('/config', authMiddleware, (req, res) => { | |
| try { | |
| const envData = parseEnvFile(envPath); | |
| const jsonData = getConfigJson(); | |
| res.json({ success: true, data: { env: envData, json: jsonData } }); | |
| } catch (error) { | |
| logger.error('读取配置失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| // 更新配置 | |
| router.put('/config', authMiddleware, (req, res) => { | |
| try { | |
| const { env: envUpdates, json: jsonUpdates } = req.body; | |
| if (envUpdates) updateEnvFile(envPath, envUpdates); | |
| if (jsonUpdates) saveConfigJson(deepMerge(getConfigJson(), jsonUpdates)); | |
| dotenv.config({ override: true }); | |
| reloadConfig(); | |
| logger.info('配置已更新并热重载'); | |
| res.json({ success: true, message: '配置已保存并生效(端口/HOST修改需重启)' }); | |
| } catch (error) { | |
| logger.error('更新配置失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| // 获取轮询策略配置 | |
| router.get('/rotation', authMiddleware, (req, res) => { | |
| try { | |
| const rotationConfig = tokenManager.getRotationConfig(); | |
| res.json({ success: true, data: rotationConfig }); | |
| } catch (error) { | |
| logger.error('获取轮询配置失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| // 更新轮询策略配置 | |
| router.put('/rotation', authMiddleware, (req, res) => { | |
| try { | |
| const { strategy, requestCount } = req.body; | |
| // 验证策略值 | |
| const validStrategies = ['round_robin', 'quota_exhausted', 'request_count']; | |
| if (strategy && !validStrategies.includes(strategy)) { | |
| return res.status(400).json({ | |
| success: false, | |
| message: `无效的策略,可选值: ${validStrategies.join(', ')}` | |
| }); | |
| } | |
| // 更新内存中的配置 | |
| tokenManager.updateRotationConfig(strategy, requestCount); | |
| // 保存到config.json | |
| const currentConfig = getConfigJson(); | |
| if (!currentConfig.rotation) currentConfig.rotation = {}; | |
| if (strategy) currentConfig.rotation.strategy = strategy; | |
| if (requestCount) currentConfig.rotation.requestCount = requestCount; | |
| saveConfigJson(currentConfig); | |
| // 重载配置到内存 | |
| reloadConfig(); | |
| logger.info(`轮询策略已更新: ${strategy || '未变'}, 请求次数: ${requestCount || '未变'}`); | |
| res.json({ success: true, message: '轮询策略已更新', data: tokenManager.getRotationConfig() }); | |
| } catch (error) { | |
| logger.error('更新轮询配置失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| // 获取指定Token的模型额度 | |
| router.get('/tokens/:refreshToken/quotas', authMiddleware, async (req, res) => { | |
| try { | |
| const { refreshToken } = req.params; | |
| const forceRefresh = req.query.refresh === 'true'; | |
| const tokens = await tokenManager.getTokenList(); | |
| let tokenData = tokens.find(t => t.refresh_token === refreshToken); | |
| if (!tokenData) { | |
| return res.status(404).json({ success: false, message: 'Token不存在' }); | |
| } | |
| // 检查token是否过期,如果过期则刷新 | |
| if (tokenManager.isExpired(tokenData)) { | |
| try { | |
| tokenData = await tokenManager.refreshToken(tokenData); | |
| } catch (error) { | |
| logger.error('刷新token失败:', error.message); | |
| // 使用 400 而不是 401,避免前端误认为 JWT 登录过期 | |
| return res.status(400).json({ success: false, message: 'Google Token已过期且刷新失败,请重新登录Google账号' }); | |
| } | |
| } | |
| // 先从缓存获取(除非强制刷新) | |
| let quotaData = forceRefresh ? null : quotaManager.getQuota(refreshToken); | |
| if (!quotaData) { | |
| // 缓存未命中或强制刷新,从API获取 | |
| const token = { access_token: tokenData.access_token, refresh_token: refreshToken }; | |
| const quotas = await getModelsWithQuotas(token); | |
| quotaManager.updateQuota(refreshToken, quotas); | |
| quotaData = { lastUpdated: Date.now(), models: quotas }; | |
| } | |
| // 转换时间为北京时间 | |
| const modelsWithBeijingTime = {}; | |
| Object.entries(quotaData.models).forEach(([modelId, quota]) => { | |
| modelsWithBeijingTime[modelId] = { | |
| remaining: quota.r, | |
| resetTime: quotaManager.convertToBeijingTime(quota.t), | |
| resetTimeRaw: quota.t | |
| }; | |
| }); | |
| res.json({ | |
| success: true, | |
| data: { | |
| lastUpdated: quotaData.lastUpdated, | |
| models: modelsWithBeijingTime | |
| } | |
| }); | |
| } catch (error) { | |
| logger.error('获取额度失败:', error.message); | |
| res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| export default router; |