Files
broccolini-bot/settings-site/server.js
indifferentketchup a1cd67fd73 fix
2026-04-10 08:57:55 -05:00

104 lines
3.2 KiB
JavaScript

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: true,
sameSite: 'lax',
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}`);
});