const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const fs = require('fs'); const path = require('path'); const app = express(); const proxyCache = {}; // Memory cache for active proxies // Load Endpoints let endpoints = {}; try { endpoints = JSON.parse(fs.readFileSync(path.join(__dirname, 'endpoints.json'), 'utf8')); console.log("✔ Loaded:", Object.keys(endpoints).join(', ')); } catch (e) { console.error("✘ Error: endpoints.json missing or invalid."); } // 1. Static Frontend app.use(express.static(path.join(__dirname, 'public'))); // 2. Info Route app.get('/api/endpoints', (req, res) => res.json(endpoints)); // 3. The "Smart Proxy" Router app.use('/:nick', (req, res, next) => { const { nick } = req.params; // Ignore internal or invalid requests if (nick === 'api' || !endpoints[nick]) return next(); // Create the proxy if it doesn't exist in cache if (!proxyCache[nick]) { const target = endpoints[nick].url; console.log(`[Setup] Routing /${nick} -> ${target}`); proxyCache[nick] = createProxyMiddleware({ target: target, changeOrigin: true, pathRewrite: { [`^/${nick}`]: '' }, // Strips /gpt4 or /nim from the URL onProxyReq: (proxyReq) => { proxyReq.setHeader('host', new URL(target).host); }, onProxyRes: (proxyRes, req, res) => { // Global CORS fix for SillyTavern/Bots res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); }, onError: (err, req, res) => { console.error(`[Error: ${nick}]`, err.message); res.status(502).json({ error: "Provider Offline. Is your G4F Space paused?" }); } }); } return proxyCache[nick](req, res, next); }); const PORT = 7860; app.listen(PORT, '0.0.0.0', () => console.log(`🚀 Hub active on port ${PORT}`));