const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const cors = require('cors'); const rateLimit = require('express-rate-limit'); const app = express(); const PORT = process.env.PORT || 8080; // CORS middleware app.use(cors({ origin: true, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['*'] })); // Handle preflight BEFORE proxy app.options('*', cors()); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok' }); }); // Rate limiting const limiter = rateLimit({ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 60_000, max: parseInt(process.env.RATE_LIMIT_MAX, 10) || 100, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many requests, please try again later.' }, }); app.use(limiter); // Require Authorization header app.use('/api/v1', (req, res, next) => { const auth = req.headers['authorization']; if (!auth || !auth.startsWith('Bearer ')) { return res.status(401).json({ error: 'Missing or invalid Authorization header.' }); } next(); }); // Request logging app.use('/api/v1', (req, res, next) => { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`); }); next(); }); // Proxy — only /api/v1/* traffic app.use('/api/v1', createProxyMiddleware({ target: 'https://openrouter.ai/api/v1', changeOrigin: true, pathRewrite: { '^/api/v1': '', }, onProxyReq: (proxyReq, req, res) => { if (!req.headers['http-referer']) { proxyReq.setHeader('HTTP-Referer', 'https://your-app-domain.com'); } }, onError: (err, req, res) => { console.error(`Proxy error: ${err.message}`); res.status(502).json({ error: 'Bad gateway — upstream request failed.' }); }, buffer: false, })); // 404 for everything else app.use((req, res) => { res.status(404).json({ error: 'Not found. Use /api/v1/* to access the OpenRouter API.' }); }); app.listen(PORT, () => { console.log(`CORS Proxy running on port ${PORT}`); });