CoolFace
Apppublic

lis3456/droid2api

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
server.js216 linesDownload Raw Back to root
1import 'dotenv/config';2import express from 'express';3import { loadConfig, isDevMode, getPort } from './config.js';4import { logInfo, logError } from './logger.js';5import router from './routes.js';6import { initializeAuth } from './auth.js';7import adminRouter from './api/admin-routes.js';8 9const app = express();10 11app.use(express.json({ limit: '50mb' }));12app.use(express.urlencoded({ extended: true, limit: '50mb' }));13 14// 老王:覆盖res.json方法,强制所有JSON响应使用UTF-8编码,不然中文显示成SB乱码15app.use((req, res, next) => {16  const originalJson = res.json.bind(res);17  res.json = function(data) {18    res.setHeader('Content-Type', 'application/json; charset=utf-8');19    return originalJson(data);20  };21  next();22});23// API访问控制中间件24function apiKeyAuth(req, res, next) {25  // 跳过管理API、根路径和静态文件26  const skipPaths = [27    '/admin',           // 管理API28    '/',                // 根路径29    '/index.html',      // HTML文件30    '/style.css',       // CSS文件31    '/app.js',          // JS文件32    '/favicon.ico'      // 网站图标33  ];34 35  // 检查是否是需要跳过的路径36  if (skipPaths.some(path => req.path === path || req.path.startsWith('/admin'))) {37    return next();38  }39 40  const clientApiKey = req.headers['x-api-key'] || req.headers['authorization'];41  const validApiKey = process.env.API_ACCESS_KEY;42 43  // 如果未配置访问密钥,跳过验证44  if (!validApiKey || validApiKey === 'your-secure-access-key-here') {45    return next();46  }47 48  // 验证密钥格式: Bearer xxx 或直接 xxx49  const cleanClientKey = clientApiKey?.replace('Bearer ', '').trim();50  const cleanValidKey = validApiKey.trim();51 52  if (!cleanClientKey || cleanClientKey !== cleanValidKey) {53    return res.status(401).json({54      error: 'Unauthorized',55      message: 'Invalid or missing API access key'56    });57  }58 59  next();60}61  // 应用API访问控制62app.use(apiKeyAuth);63 64app.use((req, res, next) => {65  res.header('Access-Control-Allow-Origin', '*');66  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');67  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key, X-Admin-Key, anthropic-version');68 69  if (req.method === 'OPTIONS') {70    return res.sendStatus(200);71  }72  next();73});74 75app.use(router);76 // 管理API路由77 app.use('/admin', adminRouter);78 79 // 静态文件服务 (前端管理界面)80 app.use(express.static('public'));81 82app.get('/', (req, res) => {83  res.json({84    name: 'droid2api',85    version: '1.0.0',86    description: 'OpenAI Compatible API Proxy',87    endpoints: [88      'GET /v1/models',89      'POST /v1/chat/completions',90      'POST /v1/responses',91      'POST /v1/messages'92 93    ]94  });95});96 97// 处理favicon.ico请求,避免404日志刷屏(这个SB浏览器总是自动请求)98app.get('/favicon.ico', (req, res) => {99  res.status(204).end(); // 204 No Content,不返回任何内容100});101 102// 404 处理 - 捕获所有未匹配的路由103app.use((req, res, next) => {104  const errorInfo = {105    timestamp: new Date().toISOString(),106    method: req.method,107    url: req.originalUrl || req.url,108    path: req.path,109    query: req.query,110    params: req.params,111    body: req.body,112    headers: {113      'content-type': req.headers['content-type'],114      'user-agent': req.headers['user-agent'],115      'origin': req.headers['origin'],116      'referer': req.headers['referer']117    },118    ip: req.ip || req.connection.remoteAddress119  };120 121  console.error('\n' + '='.repeat(80));122  console.error('❌ 非法请求地址');123  console.error('='.repeat(80));124  console.error(`时间: ${errorInfo.timestamp}`);125  console.error(`方法: ${errorInfo.method}`);126  console.error(`地址: ${errorInfo.url}`);127  console.error(`路径: ${errorInfo.path}`);128  129  if (Object.keys(errorInfo.query).length > 0) {130    console.error(`查询参数: ${JSON.stringify(errorInfo.query, null, 2)}`);131  }132  133  if (errorInfo.body && Object.keys(errorInfo.body).length > 0) {134    console.error(`请求体: ${JSON.stringify(errorInfo.body, null, 2)}`);135  }136  137  console.error(`客户端IP: ${errorInfo.ip}`);138  console.error(`User-Agent: ${errorInfo.headers['user-agent'] || 'N/A'}`);139  140  if (errorInfo.headers.referer) {141    console.error(`来源: ${errorInfo.headers.referer}`);142  }143  144  console.error('='.repeat(80) + '\n');145 146  logError('Invalid request path', errorInfo);147 148  res.status(404).json({149    error: 'Not Found',150    message: `路径 ${req.method} ${req.path} 不存在`,151    timestamp: errorInfo.timestamp,152    availableEndpoints: [153      'GET /v1/models',154      'POST /v1/chat/completions',155      'POST /v1/responses',156      'POST /v1/messages'157    ]158  });159});160 161// 错误处理中间件162app.use((err, req, res, next) => {163  logError('Unhandled error', err);164  res.status(500).json({165    error: 'Internal server error',166    message: isDevMode() ? err.message : undefined167  });168});169 170(async () => {171  try {172    loadConfig();173    logInfo('Configuration loaded successfully');174    logInfo(`Dev mode: ${isDevMode()}`);175    176    // Initialize auth system (load and setup API key if needed)177    // This won't throw error if no auth config is found - will use client auth178    await initializeAuth();179    180    const PORT = getPort();181  logInfo(`Starting server on port ${PORT}...`);182  183  const server = app.listen(PORT)184    .on('listening', () => {185      logInfo(`Server running on http://localhost:${PORT}`);186      logInfo('Available endpoints:');187      logInfo('  GET  /v1/models');188      logInfo('  POST /v1/chat/completions');189      logInfo('  POST /v1/responses');190      logInfo('  POST /v1/messages');191      logInfo('  GET  /admin/* (Key Pool Management)');192    })193    .on('error', (err) => {194      if (err.code === 'EADDRINUSE') {195        console.error(`\n${'='.repeat(80)}`);196        console.error(`ERROR: Port ${PORT} is already in use!`);197        console.error('');198        console.error('Please choose one of the following options:');199        console.error(`  1. Stop the process using port ${PORT}:`);200        console.error(`     lsof -ti:${PORT} | xargs kill`);201        console.error('');202        console.error('  2. Change the port in config.json:');203        console.error('     Edit config.json and modify the "port" field');204        console.error(`${'='.repeat(80)}\n`);205        process.exit(1);206      } else {207        logError('Failed to start server', err);208        process.exit(1);209      }210    });211  } catch (error) {212    logError('Failed to start server', error);213    process.exit(1);214  }215})();216