require('dotenv').config({ path: process.env.ENV_FILE || '../.env' }); const express = require('express'); const session = require('express-session'); const path = require('path'); const fetch = require('node-fetch'); const app = express(); const PORT = parseInt(process.env.SETTINGS_PORT) || 12752; const INTERNAL_URL = process.env.INTERNAL_API_URL || `http://127.0.0.1:${process.env.INTERNAL_API_PORT || 12753}/internal`; const SECRET = process.env.INTERNAL_API_SECRET; const ADMIN_PASSWORD = process.env.SETTINGS_ADMIN_PASSWORD; app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static(path.join(__dirname, 'public'), { index: false })); app.use(session({ secret: SECRET || 'fallback-secret-change-me', resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: false, // set true if behind HTTPS proxy maxAge: 8 * 60 * 60 * 1000 // 8 hours } })); // Auth middleware function requireAuth(req, res, next) { if (req.session?.authed) return next(); res.redirect('/login'); } // Internal API proxy helper async function callBot(method, apiPath, body) { const res = await fetch(`${INTERNAL_URL}${apiPath}`, { method, headers: { 'Content-Type': 'application/json', 'x-internal-secret': SECRET }, body: body ? JSON.stringify(body) : undefined }); return res.json(); } // Routes app.get('/login', (req, res) => { if (req.session?.authed) return res.redirect('/'); res.sendFile(path.join(__dirname, 'public', 'login.html')); }); app.post('/login', (req, res) => { if (!ADMIN_PASSWORD) return res.status(503).json({ error: 'SETTINGS_ADMIN_PASSWORD not set' }); if (req.body.password === ADMIN_PASSWORD) { req.session.authed = true; return res.json({ ok: true }); } res.status(401).json({ error: 'Invalid password' }); }); app.post('/logout', (req, res) => { req.session.destroy(); res.redirect('/login'); }); app.get('/', requireAuth, (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); // Proxy to bot internal API app.get('/api/config', requireAuth, async (req, res) => { try { res.json(await callBot('GET', '/config')); } catch (e) { res.status(502).json({ error: 'Bot unreachable' }); } }); app.post('/api/config', requireAuth, async (req, res) => { try { res.json(await callBot('POST', '/config', req.body)); } catch (e) { res.status(502).json({ error: 'Bot unreachable' }); } }); app.get('/api/discord/guild', requireAuth, async (req, res) => { try { res.json(await callBot('GET', '/discord/guild')); } catch (e) { res.status(502).json({ error: 'Bot unreachable' }); } }); app.post('/api/restart', requireAuth, async (req, res) => { try { res.json(await callBot('POST', '/restart', req.body)); } catch (e) { res.status(502).json({ error: 'Bot unreachable' }); } }); app.get('/api/restart/status', requireAuth, async (req, res) => { try { res.json(await callBot('GET', '/restart/status')); } catch (e) { res.status(502).json({ error: 'Bot unreachable' }); } }); app.get('*', requireAuth, (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); app.listen(PORT, '0.0.0.0', () => { console.log(`[settings] running on port ${PORT}`); });