feat: add ADMIN_PWD and ALLOWED_GUILD_IDS support
- Add ADMIN_PWD and ALLOWED_GUILD_IDS env vars to config - Extend PluginContext with adminPwd and allowedGuildIds - Add adminAuth and guildFilter middleware for plugins - Add /api/admin/login endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1669af1e91
commit
1df780fe60
3 changed files with 58 additions and 1 deletions
41
server/src/core/middleware.ts
Normal file
41
server/src/core/middleware.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import type { PluginContext } from './plugin.js';
|
||||
|
||||
/**
|
||||
* Admin authentication middleware.
|
||||
* Checks `x-admin-password` header against ADMIN_PWD env var.
|
||||
*/
|
||||
export function adminAuth(ctx: PluginContext) {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
if (!ctx.adminPwd) {
|
||||
res.status(503).json({ error: 'ADMIN_PWD not configured' });
|
||||
return;
|
||||
}
|
||||
const pwd = req.headers['x-admin-password'] as string | undefined;
|
||||
if (pwd !== ctx.adminPwd) {
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Guild filter middleware.
|
||||
* If ALLOWED_GUILD_IDS is set, only allows requests for those guilds.
|
||||
* Reads guildId from req.params.guildId or req.body.guildId or req.query.guildId.
|
||||
*/
|
||||
export function guildFilter(ctx: PluginContext) {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
if (ctx.allowedGuildIds.length === 0) { next(); return; }
|
||||
|
||||
const guildId = req.params.guildId ?? req.body?.guildId ?? req.query.guildId;
|
||||
if (!guildId) { next(); return; }
|
||||
|
||||
if (!ctx.allowedGuildIds.includes(String(guildId))) {
|
||||
res.status(403).json({ error: 'Guild not allowed' });
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
|
@ -25,6 +25,8 @@ export interface Plugin {
|
|||
export interface PluginContext {
|
||||
client: Client;
|
||||
dataDir: string;
|
||||
adminPwd: string;
|
||||
allowedGuildIds: string[];
|
||||
}
|
||||
|
||||
const loadedPlugins: Plugin[] = [];
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import radioPlugin from './plugins/radio/index.js';
|
|||
const PORT = Number(process.env.PORT ?? 8080);
|
||||
const DATA_DIR = process.env.DATA_DIR ?? '/data';
|
||||
const DISCORD_TOKEN = process.env.DISCORD_TOKEN ?? '';
|
||||
const ADMIN_PWD = process.env.ADMIN_PWD ?? '';
|
||||
const ALLOWED_GUILD_IDS = (process.env.ALLOWED_GUILD_IDS ?? '')
|
||||
.split(',').map(s => s.trim()).filter(Boolean);
|
||||
|
||||
// ── Persistence ──
|
||||
loadState();
|
||||
|
|
@ -20,7 +23,7 @@ app.use(express.json());
|
|||
app.use(express.static(path.join(import.meta.dirname ?? __dirname, '..', '..', 'web', 'dist')));
|
||||
|
||||
// ── Plugin Context ──
|
||||
const ctx: PluginContext = { client, dataDir: DATA_DIR };
|
||||
const ctx: PluginContext = { client, dataDir: DATA_DIR, adminPwd: ADMIN_PWD, allowedGuildIds: ALLOWED_GUILD_IDS };
|
||||
|
||||
// ── SSE Events ──
|
||||
app.get('/api/events', (_req, res) => {
|
||||
|
|
@ -60,6 +63,17 @@ app.get('/api/health', (_req, res) => {
|
|||
});
|
||||
});
|
||||
|
||||
// ── Admin Login ──
|
||||
app.post('/api/admin/login', (req, res) => {
|
||||
if (!ADMIN_PWD) { res.status(503).json({ error: 'ADMIN_PWD not configured' }); return; }
|
||||
const { password } = req.body ?? {};
|
||||
if (password === ADMIN_PWD) {
|
||||
res.json({ ok: true });
|
||||
} else {
|
||||
res.status(401).json({ error: 'Invalid password' });
|
||||
}
|
||||
});
|
||||
|
||||
// ── API: List plugins ──
|
||||
app.get('/api/plugins', (_req, res) => {
|
||||
res.json(getPlugins().map(p => ({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue