const express = require('express'); const { KEY_TYPE_ADMIN } = require('../database'); const { EVENT_TYPES } = require('../constants/eventTypes'); const router = express.Router(); const DEFAULT_ADMIN_BLOCKED_COMMANDS = new Set([ 'stop', 'deop', 'op', 'ban', 'ban-ip', 'banlist', 'pardon', 'pardon-ip', 'whitelist', 'kick', 'restart', 'reload', 'save-all', 'save-on', 'save-off' ]); function parseCommandList(value) { if (!value) { return []; } return value .split(',') .map((entry) => entry.trim().toLowerCase()) .filter(Boolean); } const adminAllowedCommands = new Set(parseCommandList(process.env.ADMIN_ALLOWED_COMMANDS)); const adminBlockedCommandsFromEnv = parseCommandList(process.env.ADMIN_BLOCKED_COMMANDS); const adminBlockedCommands = adminBlockedCommandsFromEnv.length ? new Set(adminBlockedCommandsFromEnv) : DEFAULT_ADMIN_BLOCKED_COMMANDS; function normalizeCommand(command) { const trimmed = command.trim(); if (!trimmed) { return null; } const token = trimmed.split(/\s+/)[0].toLowerCase(); const base = token.includes(':') ? token.split(':').pop() : token; return { token, base }; } function isAdminCommandAllowed(command) { const normalized = normalizeCommand(command); if (!normalized) { return false; } const { token, base } = normalized; if (adminAllowedCommands.size) { return adminAllowedCommands.has(token) || adminAllowedCommands.has(base); } return !(adminBlockedCommands.has(token) || adminBlockedCommands.has(base)); } function createServerRouter(db, manager, requireCommandKey) { router.get('/info', requireCommandKey, (req, res) => { try { const requestedServerId = req.apiKey.keyType === KEY_TYPE_ADMIN ? (req.query.server_id || req.apiKey.serverId) : req.apiKey.serverId; if (!requestedServerId) { return res.status(400).json({ detail: 'server_id is required for this key' }); } const serverInfo = { server_id: requestedServerId, status: 'running', online_players: 5, max_players: 20, version: '1.20.1', uptime: '2h 30m', tps: 19.8 }; res.json(serverInfo); } catch (error) { res.status(500).json({ detail: error.message }); } }); router.post('/command', requireCommandKey, (req, res) => { try { const { command, server_id: requestedServerId } = req.body; if (!command || typeof command !== 'string') { return res.status(400).json({ detail: 'Command is required' }); } const isAdmin = req.apiKey.keyType === KEY_TYPE_ADMIN; if (isAdmin && !isAdminCommandAllowed(command)) { return res.status(403).json({ detail: 'Command is not allowed for Admin Key' }); } const targetServerId = isAdmin ? (requestedServerId || req.apiKey.serverId) : req.apiKey.serverId; if (!targetServerId) { return res.status(400).json({ detail: 'server_id is required for this key' }); } console.log(`[Server Command] ${targetServerId}: ${command}`); const commandEvent = { event_type: EVENT_TYPES.SERVER_COMMAND, server_name: targetServerId, timestamp: new Date().toISOString(), data: { command: command, executed_by: req.apiKey.id, server_id: targetServerId } }; manager.broadcastToAll({ type: 'minecraft_event', event: commandEvent, source_key_id_prefix: req.apiKey.id.substring(0, 8) }); res.json({ message: 'Command sent successfully', command: command, server_id: targetServerId }); } catch (error) { res.status(500).json({ detail: error.message }); } }); return router; } module.exports = createServerRouter;