| import express from 'express'; |
| import cors from 'cors'; |
| import helmet from 'helmet'; |
| import path from 'path'; |
| import { fileURLToPath } from 'url'; |
| import { keysRouter } from './routes/keys.js'; |
| import { modelsRouter } from './routes/models.js'; |
| import { proxyRouter, isRetryableError } from './routes/proxy.js'; |
| import { fallbackRouter } from './routes/fallback.js'; |
| import { analyticsRouter } from './routes/analytics.js'; |
| import { healthRouter } from './routes/health.js'; |
| import { settingsRouter } from './routes/settings.js'; |
| import { errorHandler } from './middleware/errorHandler.js'; |
| import { routeRequest, recordRateLimitHit, recordSuccess } from './services/router.js'; |
| import { recordRequest, recordTokens, setCooldown } from './services/ratelimit.js'; |
| import { getUnifiedApiKey } from './db/index.js'; |
|
|
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
|
|
| export function createApp() { |
| const app = express(); |
|
|
| app.use(helmet({ contentSecurityPolicy: false, hsts: false })); |
| app.use(cors()); |
| app.use(express.json({ limit: '1mb' })); |
|
|
| |
| const adminPassword = process.env.ADMIN_PASSWORD; |
| |
| if (adminPassword) { |
| |
| app.post('/api/login', (req, res) => { |
| const { password } = req.body; |
| if (password === adminPassword) { |
| res.json({ token: adminPassword }); |
| } else { |
| res.status(401).json({ error: 'Invalid password' }); |
| } |
| }); |
|
|
| app.use((req, res, next) => { |
| |
| if (req.path.startsWith('/v1/')) return next(); |
| if (req.path === '/api' && req.method === 'POST') return next(); |
| if (req.path === '/api/ping') return next(); |
| if (req.path === '/api/login') return next(); |
|
|
| |
| if (req.path.startsWith('/api/')) { |
| const authHeader = req.headers.authorization || ''; |
| const token = authHeader.replace(/^Bearer\s+/i, ''); |
|
|
| if (token === adminPassword) { |
| return next(); |
| } |
|
|
| return res.status(401).json({ error: 'Authentication required' }); |
| } |
|
|
| |
| return next(); |
| }); |
| } |
|
|
| |
| app.use('/api/keys', keysRouter); |
| app.use('/api/models', modelsRouter); |
| app.use('/api/fallback', fallbackRouter); |
| app.use('/api/analytics', analyticsRouter); |
| app.use('/api/health', healthRouter); |
| app.use('/api/settings', settingsRouter); |
|
|
| |
| app.post('/api', async (req, res) => { |
| const { prompt } = req.body; |
| if (!prompt || typeof prompt !== 'string') { |
| return res.status(400).json({ status: 'error', message: 'Missing prompt' }); |
| } |
|
|
| |
| const authHeader = req.headers.authorization; |
| const isLocal = req.ip === '127.0.0.1' || req.ip === '::1' || req.ip === '::ffff:127.0.0.1'; |
| if (authHeader && !isLocal) { |
| const token = authHeader.replace(/^Bearer\s+/i, ''); |
| const unifiedKey = getUnifiedApiKey(); |
| if (token !== unifiedKey) { |
| return res.status(401).json({ status: 'error', message: 'Invalid API key' }); |
| } |
| } |
|
|
| const messages = [{ role: 'user' as const, content: prompt }]; |
| const estimatedTokens = Math.ceil(prompt.length / 4) + 1000; |
| const skipKeys = new Set<string>(); |
| let lastError: any = null; |
|
|
| for (let attempt = 0; attempt < 20; attempt++) { |
| try { |
| const route = routeRequest(estimatedTokens, skipKeys.size > 0 ? skipKeys : undefined); |
| recordRequest(route.platform, route.modelId, route.keyId); |
|
|
| const result = await route.provider.chatCompletion( |
| route.apiKey, messages, route.modelId, |
| { temperature: 0.7 } |
| ); |
|
|
| const totalTokens = result.usage?.total_tokens ?? 0; |
| recordTokens(route.platform, route.modelId, route.keyId, totalTokens); |
| recordSuccess(route.modelDbId); |
|
|
| return res.json({ |
| status: 'success', |
| text: result.choices[0]?.message?.content || '' |
| }); |
| } catch (err: any) { |
| lastError = err; |
| if (isRetryableError(err)) { |
| |
| try { |
| const route = routeRequest(estimatedTokens, skipKeys.size > 0 ? skipKeys : undefined); |
| const skipId = `${route.platform}:${route.modelId}:${route.keyId}`; |
| skipKeys.add(skipId); |
| setCooldown(route.platform, route.modelId, route.keyId, 120_000); |
| recordRateLimitHit(route.modelDbId); |
| } catch { |
| |
| } |
| console.log(`[CustomAPI] ${err.message.slice(0, 60)}, falling back (attempt ${attempt + 1}/20)`); |
| continue; |
| } |
|
|
| |
| return res.status(500).json({ status: 'error', message: err.message }); |
| } |
| } |
|
|
| res.status(429).json({ status: 'error', message: `All models failed after retries. Last: ${lastError?.message}` }); |
| }); |
|
|
| |
| app.use('/v1', proxyRouter); |
|
|
| |
| app.get('/api/ping', (_req, res) => { |
| res.json({ status: 'ok', timestamp: new Date().toISOString() }); |
| }); |
|
|
| |
| app.use(errorHandler); |
|
|
| |
| const clientDist = path.resolve(__dirname, '../../client/dist'); |
| app.use(express.static(clientDist)); |
| |
| app.use((req, res, next) => { |
| if (req.path === '/api' || req.path.startsWith('/api/') || req.path.startsWith('/v1/')) { |
| next(); |
| return; |
| } |
| res.sendFile(path.join(clientDist, 'index.html')); |
| }); |
|
|
| return app; |
| } |
|
|