'use strict'; const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const fetch = require('node-fetch'); const app = express(); const GATEWAY_TOKEN = process.env.GATEWAY_TOKEN || 'changeme'; const LLAMA_URL = 'http://127.0.0.1:8080'; const PORT = 7860; const START_TIME = Date.now(); let proxiedCount = 0; // ── Helpers ─────────────────────────────────────────────────────────────────── function requireAuth(req, res, next) { const auth = req.headers['authorization'] || ''; const token = auth.replace('Bearer ', '').trim(); if (token !== GATEWAY_TOKEN) return res.status(401).json({ error: 'Unauthorized' }); next(); } function formatUptime(ms) { const s = Math.floor(ms / 1000); const m = Math.floor(s / 60); const h = Math.floor(m / 60); const d = Math.floor(h / 24); if (d > 0) return d + 'd ' + (h % 24) + 'h'; if (h > 0) return h + 'h ' + (m % 60) + 'm'; if (m > 0) return m + 'm ' + (s % 60) + 's'; return s + 's'; } // ── /health ─────────────────────────────────────────────────────────────────── app.get('/health', async (req, res) => { try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); const resp = await fetch(`${LLAMA_URL}/v1/models`, { headers: { 'Authorization': `Bearer ${GATEWAY_TOKEN}` }, signal: controller.signal }); clearTimeout(timeout); if (resp.ok) return res.status(200).json({ status: 'ok', llm: 'ready' }); return res.status(503).json({ status: 'degraded', code: resp.status }); } catch (e) { return res.status(503).json({ status: 'unavailable', error: e.message }); } }); // ── /stats (consumed by the dashboard and external monitors) ────────────────── app.get('/stats', async (req, res) => { let llmOnline = false; let modelId = process.env.MODEL_FILENAME || 'Unknown'; try { const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(), 3000); const r = await fetch(`${LLAMA_URL}/v1/models`, { headers: { 'Authorization': `Bearer ${GATEWAY_TOKEN}` }, signal: ctrl.signal }); clearTimeout(t); if (r.ok) { llmOnline = true; const j = await r.json(); if (j.data && j.data[0] && j.data[0].id) modelId = j.data[0].id; } } catch (_) {} const bs = global.botState || {}; res.json({ llmOnline, modelId, contextLength: parseInt(process.env.CONTEXT_LENGTH || '2048'), threads: process.env.CPU_THREADS || 'auto', uptime: formatUptime(Date.now() - START_TIME), uptimeMs: Date.now() - START_TIME, proxiedCount, telegramConfigured: !!process.env.TELEGRAM_BOT_TOKEN, cfConfigured: !!(process.env.CLOUDFLARE_WORKERS_TOKEN && process.env.CLOUDFLARE_ACCOUNT_ID), proxyUrl: process.env.CLOUDFLARE_TELEGRAM_PROXY_URL || null, spaceUrl: process.env.SPACE_URL || null, backupEnabled: !!(process.env.HF_TOKEN && process.env.HF_BACKUP_DATASET), backupDataset: process.env.HF_BACKUP_DATASET || null, totalMessages: bs.totalMessages || 0, lastMessageAt: bs.lastMessageAt || null, activeChats: bs.activeChats || 0, }); }); // ── / (Dashboard) ───────────────────────────────────────────────────────────── const DASHBOARD_HTML = ` LLM Space
LIVE
Inference Engine
Checking…
llama.cpp
Port 8080 · Bearer protected
Model
Loading…
Uptime
Port 7860 · Express
Telegram
Checking…
Memory Sync
Checking…
Keep Alive
Checking…
`; app.get('/', (req, res) => { res.setHeader('Content-Type', 'text/html'); res.send(DASHBOARD_HTML); }); // ── /telegram (Webhook, intentionally open) ─────────────────────────────────── app.use('/telegram', express.json({ limit: '1mb' }), async (req, res) => { if (req.method !== 'POST') return res.status(405).send('Method Not Allowed'); if (!req.body || typeof req.body.update_id !== 'number') { return res.status(400).json({ ok: false, error: 'Invalid update payload' }); } try { const { handleTelegramUpdate } = require('./telegram-bot'); await handleTelegramUpdate(req.body); res.json({ ok: true }); } catch (e) { console.error('Webhook Endpoint Error:', e.message); res.status(200).json({ ok: false, error: 'Handled internal error' }); } } ); // ── /v1 (Authenticated LLM proxy) ──────────────────────────────────────────── app.use('/v1', requireAuth, (req, res, next) => { proxiedCount++; next(); }, createProxyMiddleware({ target: LLAMA_URL, changeOrigin: true, on: { error: (err, req, res) => { res.status(502).json({ error: 'LLM server unavailable' }); } } })); // ── Start ───────────────────────────────────────────────────────────────────── app.listen(PORT, '0.0.0.0', () => { console.log(`🌐 Gateway running on port ${PORT}`); });