CoolFace
Apppublic

lis3456/droid2api

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
admin-routes.js574 linesDownload Raw Back to api
1import express from 'express';2import keyPoolManager from '../auth.js';3import { logInfo, logError } from '../logger.js';4 5const router = express.Router();6 7/**8 * 管理后台鉴权中间件9 */10function adminAuth(req, res, next) {11  const adminKey = req.headers['x-admin-key'];12  const expectedKey = process.env.ADMIN_ACCESS_KEY;13 14  if (!expectedKey || expectedKey === 'your-admin-key-here') {15    return res.status(500).json({16      error: 'Admin key not configured',17      message: 'Please set ADMIN_ACCESS_KEY in .env file'18    });19  }20 21  if (!adminKey || adminKey !== expectedKey) {22    logError('Unauthorized admin access attempt', {23      ip: req.ip,24      headers: req.headers25    });26    return res.status(403).json({27      error: 'Forbidden',28      message: 'Invalid admin access key'29    });30  }31 32  next();33}34 35// 应用鉴权中间件到所有管理路由36router.use(adminAuth);37 38/**39 * GET /admin/stats40 * 获取密钥池统计信息41 */42router.get('/stats', (req, res) => {43  try {44    const stats = keyPoolManager.getStats();45    res.json({46      success: true,47      data: stats48    });49  } catch (error) {50    logError('Failed to get stats', error);51    res.status(500).json({52      error: 'Internal server error',53      message: error.message54    });55  }56});57 58/**59 * GET /admin/keys60 * 获取密钥列表 (支持分页和筛选)61 * Query参数:62 *   - page: 页码 (默认1)63 *   - limit: 每页数量 (默认10)64 *   - status: 状态筛选 (all | active | disabled | banned, 默认all)65 */66router.get('/keys', (req, res) => {67  try {68    const page = parseInt(req.query.page) || 1;69    const limit = parseInt(req.query.limit) || 10;70    const status = req.query.status || 'all';71 72    const result = keyPoolManager.getKeys(page, limit, status);73 74    res.json({75      success: true,76      data: result77    });78  } catch (error) {79    logError('Failed to get keys', error);80    res.status(500).json({81      error: 'Internal server error',82      message: error.message83    });84  }85});86 87/**88 * GET /admin/keys/:id89 * 获取单个密钥详情90 */91/**92 * GET /admin/keys/export93 * 导出密钥为txt文件(一个密钥一行)94 * Query参数:95 *   - status: 状态筛选 (all | active | disabled | banned, 默认all)96 * 老王:这个路由必须放在 /keys/:id 之前,不然会被误匹配!97 */98router.get('/keys/export', (req, res) => {99  try {100    const status = req.query.status || 'all';101 102    // 老王:获取所有密钥(不分页)103    let keys = keyPoolManager.keys;104 105    // 按状态筛选106    if (status !== 'all') {107      keys = keys.filter(k => k.status === status);108    }109 110    // 老王:生成txt内容,每行一个密钥111    const txtContent = keys.map(k => k.key).join('\n');112 113    // 老王:设置响应头,触发浏览器下载114    const timestamp = new Date().toISOString().split('T')[0];115    const filename = `keys_${status}_${timestamp}.txt`;116 117    res.setHeader('Content-Type', 'text/plain; charset=utf-8');118    res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);119    res.send(txtContent);120 121    logInfo(`Admin exported ${keys.length} keys (status: ${status})`);122  } catch (error) {123    logError('Failed to export keys', error);124    res.status(500).json({125      error: 'Internal server error',126      message: error.message127    });128  }129});130 131router.get('/keys/:id', (req, res) => {132  try {133    const keyId = req.params.id;134    const key = keyPoolManager.getKey(keyId);135 136    res.json({137      success: true,138      data: key139    });140  } catch (error) {141    if (error.message === 'Key not found') {142      return res.status(404).json({143        error: 'Not found',144        message: error.message145      });146    }147 148    logError('Failed to get key', error);149    res.status(500).json({150      error: 'Internal server error',151      message: error.message152    });153  }154});155 156/**157 * POST /admin/keys158 * 添加单个密钥159 * Body: { key: "fk-xxx", notes: "备注" }160 */161router.post('/keys', (req, res) => {162  try {163    const { key, notes } = req.body;164 165    if (!key) {166      return res.status(400).json({167        error: 'Bad request',168        message: 'Key is required'169      });170    }171 172    if (!key.startsWith('fk-')) {173      return res.status(400).json({174        error: 'Bad request',175        message: 'Invalid key format (must start with "fk-")'176      });177    }178 179    // 老王:限制notes长度,1000字符足够了180    if (notes && notes.length > 1000) {181      return res.status(400).json({182        error: 'Bad request',183        message: 'Notes too long (max 1000 characters)'184      });185    }186 187    const newKey = keyPoolManager.addKey(key, notes || '');188 189    logInfo(`Admin added new key: ${newKey.id}`);190 191    res.json({192      success: true,193      message: 'Key added successfully',194      data: newKey195    });196  } catch (error) {197    if (error.message === 'Key already exists') {198      return res.status(409).json({199        error: 'Conflict',200        message: error.message201      });202    }203 204    logError('Failed to add key', error);205    res.status(500).json({206      error: 'Internal server error',207      message: error.message208    });209  }210});211 212/**213 * POST /admin/keys/batch214 * 批量导入密钥215 * Body: { keys: ["fk-xxx", "fk-yyy", ...] }216 */217router.post('/keys/batch', (req, res) => {218  try {219    const { keys } = req.body;220 221    if (!keys || !Array.isArray(keys)) {222      return res.status(400).json({223        error: 'Bad request',224        message: 'Keys array is required'225      });226    }227 228    const results = keyPoolManager.importKeys(keys);229 230    logInfo(`Admin batch imported keys: ${results.success} success, ${results.duplicate} duplicate, ${results.invalid} invalid`);231 232    res.json({233      success: true,234      message: 'Batch import completed',235      data: results236    });237  } catch (error) {238    logError('Failed to batch import keys', error);239    res.status(500).json({240      error: 'Internal server error',241      message: error.message242    });243  }244});245 246/**247 * DELETE /admin/keys/disabled248 * 删除所有禁用的密钥249 * 老王:这个路由必须放在 /keys/:id 之前,不然 Express 会把 disabled 当作 id 参数!250 */251router.delete('/keys/disabled', (req, res) => {252  try {253    const count = keyPoolManager.deleteDisabledKeys();254 255    logInfo(`Admin deleted ${count} disabled keys`);256 257    res.json({258      success: true,259      message: `Deleted ${count} disabled keys`,260      data: { count }261    });262  } catch (error) {263    logError('Failed to delete disabled keys', error);264    res.status(500).json({265      error: 'Internal server error',266      message: error.message267    });268  }269});270 271/**272 * DELETE /admin/keys/banned273 * 删除所有封禁的密钥274 * 老王:这个路由必须放在 /keys/:id 之前,不然 Express 会把 banned 当作 id 参数!275 */276router.delete('/keys/banned', (req, res) => {277  try {278    const count = keyPoolManager.deleteBannedKeys();279 280    logInfo(`Admin deleted ${count} banned keys`);281 282    res.json({283      success: true,284      message: `Deleted ${count} banned keys`,285      data: { count }286    });287  } catch (error) {288    logError('Failed to delete banned keys', error);289    res.status(500).json({290      error: 'Internal server error',291      message: error.message292    });293  }294});295 296/**297 * DELETE /admin/keys/:id298 * 删除单个密钥299 * 老王:这个参数化路由必须放在具体路径路由之后,遵循Express路由匹配规则!300 */301router.delete('/keys/:id', (req, res) => {302  try {303    const keyId = req.params.id;304    const deletedKey = keyPoolManager.deleteKey(keyId);305 306    logInfo(`Admin deleted key: ${keyId}`);307 308    res.json({309      success: true,310      message: 'Key deleted successfully',311      data: deletedKey312    });313  } catch (error) {314    if (error.message === 'Key not found') {315      return res.status(404).json({316        error: 'Not found',317        message: error.message318      });319    }320 321    logError('Failed to delete key', error);322    res.status(500).json({323      error: 'Internal server error',324      message: error.message325    });326  }327});328 329/**330 * PATCH /admin/keys/:id/toggle331 * 切换密钥状态 (active <-> disabled)332 * Body: { status: "active" | "disabled" }333 */334router.patch('/keys/:id/toggle', (req, res) => {335  try {336    const keyId = req.params.id;337    const { status } = req.body;338 339    // 老王:允许切换到active状态,这样可以从banned状态恢复340    if (!status || !['active', 'disabled'].includes(status)) {341      return res.status(400).json({342        error: 'Bad request',343        message: 'Invalid status (must be "active" or "disabled")'344      });345    }346 347    const updatedKey = keyPoolManager.toggleKeyStatus(keyId, status);348 349    logInfo(`Admin toggled key status: ${keyId} -> ${status}`);350 351    res.json({352      success: true,353      message: 'Key status updated successfully',354      data: updatedKey355    });356  } catch (error) {357    if (error.message === 'Key not found') {358      return res.status(404).json({359        error: 'Not found',360        message: error.message361      });362    }363 364    logError('Failed to toggle key status', error);365    res.status(500).json({366      error: 'Internal server error',367      message: error.message368    });369  }370});371 372/**373 * PATCH /admin/keys/:id/notes374 * 更新密钥备注375 * Body: { notes: "新备注" }376 */377router.patch('/keys/:id/notes', (req, res) => {378  try {379    const keyId = req.params.id;380    const { notes } = req.body;381 382    if (notes === undefined) {383      return res.status(400).json({384        error: 'Bad request',385        message: 'Notes field is required'386      });387    }388 389    // 老王:限制notes长度,1000字符足够了390    if (notes.length > 1000) {391      return res.status(400).json({392        error: 'Bad request',393        message: 'Notes too long (max 1000 characters)'394      });395    }396 397    const updatedKey = keyPoolManager.updateNotes(keyId, notes);398 399    logInfo(`Admin updated key notes: ${keyId}`);400 401    res.json({402      success: true,403      message: 'Key notes updated successfully',404      data: updatedKey405    });406  } catch (error) {407    if (error.message === 'Key not found') {408      return res.status(404).json({409        error: 'Not found',410        message: error.message411      });412    }413 414    logError('Failed to update key notes', error);415    res.status(500).json({416      error: 'Internal server error',417      message: error.message418    });419  }420});421 422/**423 * POST /admin/keys/:id/test424 * 测试单个密钥是否可用425 */426router.post('/keys/:id/test', async (req, res) => {427  try {428    const keyId = req.params.id;429    const result = await keyPoolManager.testKey(keyId);430 431    // 老王:日志里把详细信息都打出来,别tm藏着掖着432    if (result.success) {433      logInfo(`Admin tested key: ${keyId} - SUCCESS (Status: ${result.status})`);434    } else {435      logInfo(`Admin tested key: ${keyId} - FAILED (Status: ${result.status}, Message: ${result.message})`);436    }437 438    res.json({439      success: true,440      message: 'Key test completed',441      data: result442    });443  } catch (error) {444    if (error.message === 'Key not found') {445      return res.status(404).json({446        error: 'Not found',447        message: error.message448      });449    }450 451    logError('Failed to test key', error);452    res.status(500).json({453      error: 'Internal server error',454      message: error.message455    });456  }457});458 459/**460 * POST /admin/keys/test-all461 * 批量测试所有密钥462 */463router.post('/keys/test-all', async (req, res) => {464  try {465    logInfo('Admin started batch key test');466 467    const results = await keyPoolManager.testAllKeys();468 469    logInfo(`Admin batch key test completed: ${results.success} success, ${results.failed} failed`);470 471    res.json({472      success: true,473      message: 'Batch test completed',474      data: results475    });476  } catch (error) {477    logError('Failed to batch test keys', error);478    res.status(500).json({479      error: 'Internal server error',480      message: error.message481    });482  }483});484 485/**486 * GET /admin/config487 * 获取当前轮询配置488 * 返回: { algorithm, retry, autoBan, performance }489 */490router.get('/config', (req, res) => {491  try {492    const config = keyPoolManager.getConfig();493 494    res.json({495      success: true,496      data: config497    });498  } catch (error) {499    logError('Failed to get config', error);500    res.status(500).json({501      error: 'Internal server error',502      message: error.message503    });504  }505});506 507/**508 * PUT /admin/config509 * 更新轮询配置(支持部分更新)510 * Body: { algorithm?, retry?, autoBan?, performance? }511 */512router.put('/config', (req, res) => {513  try {514    const newConfig = req.body;515 516    if (!newConfig || Object.keys(newConfig).length === 0) {517      return res.status(400).json({518        error: 'Bad request',519        message: 'Config data is required'520      });521    }522 523    const updatedConfig = keyPoolManager.updateConfig(newConfig);524 525    logInfo('Admin updated config', { changes: newConfig });526 527    res.json({528      success: true,529      message: 'Config updated successfully',530      data: updatedConfig531    });532  } catch (error) {533    logError('Failed to update config', error);534    res.status(400).json({535      error: 'Bad request',536      message: error.message537    });538  }539});540 541/**542 * POST /admin/config/reset543 * 重置轮询配置为默认值544 */545router.post('/config/reset', (req, res) => {546  try {547    const defaultConfig = keyPoolManager.resetConfig();548 549    logInfo('Admin reset config to defaults');550 551    res.json({552      success: true,553      message: 'Config reset to defaults',554      data: defaultConfig555    });556  } catch (error) {557    logError('Failed to reset config', error);558    res.status(500).json({559      error: 'Internal server error',560      message: error.message561    });562  }563});564 565/**566 * GET /admin/keys/export567 * 导出密钥为txt文件(一个密钥一行)568 * Query参数:569 *   - status: 状态筛选 (all | active | disabled | banned, 默认all)570 * 老王:这个路由必须放在 /keys/:id 之前,不然会被误匹配!571 */572 573export default router;574