const express = require('express'); const { createProxyMiddleware, fixRequestBody } = require('http-proxy-middleware'); const fs = require('fs'); const path = require('path'); const app = express(); const proxyCache = {}; // 1. GLOBAL CORS INTERCEPTOR // This stops SillyTavern/JanitorAI preflight requests from reaching NIM and getting rejected. app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { return res.sendStatus(200); // Instantly approve all preflights } next(); }); // Parse incoming bodies (required to inject NIM's thinking parameters) app.use(express.json({ limit: '50mb' })); // Load Endpoints let endpoints = {}; try { endpoints = JSON.parse(fs.readFileSync(path.join(__dirname, 'endpoints.json'), 'utf8')); console.log("✔ Loaded Endpoints:", Object.keys(endpoints).join(', ')); } catch (e) { console.error("✘ Error: endpoints.json missing or invalid."); } app.use(express.static(path.join(__dirname, 'public'))); app.get('/api/endpoints', (req, res) => res.json(endpoints)); // 2. THE SMART PROXY app.use('/:nick', (req, res, next) => { const { nick } = req.params; // Ignore internal or invalid routes if (nick === 'api' || !endpoints[nick]) return next(); if (!proxyCache[nick]) { const target = endpoints[nick].url; console.log(`[Setup] Routing /${nick} -> ${target}`); // Logic to inject GLM 5.1 Thinking Parameters const handleProxyReq = (proxyReq, req, res) => { if (req.method === 'POST' && req.body) { if (nick === 'nim') { req.body.chat_template_kwargs = { enable_thinking: true, clear_thinking: false }; // Map unsupported roles if (req.body.messages && Array.isArray(req.body.messages)) { req.body.messages.forEach(msg => { if (msg.role === 'developer') msg.role = 'system'; }); } } // Safely repackage the modified JSON body fixRequestBody(proxyReq, req); } }; const handleError = (err, req, res) => { console.error(`[Error: ${nick}]`, err.message); if (!res.headersSent) { res.status(502).json({ error: "Provider Offline or Connection Timeout." }); } }; proxyCache[nick] = createProxyMiddleware({ target: target, changeOrigin: true, pathRewrite: { [`^/${nick}`]: '' }, // 3. DUAL-COMPATIBILITY HOOKS // Guarantees fixRequestBody fires regardless of the middleware version installed // --- Legacy v2 Syntax --- onProxyReq: handleProxyReq, onError: handleError, // --- Modern v3 Syntax --- on: { proxyReq: handleProxyReq, error: handleError } }); } return proxyCache[nick](req, res, next); }); const PORT = 7860; app.listen(PORT, '0.0.0.0', () => console.log(`🚀 Hub active on port ${PORT}`));