Spaces:
Sleeping
Sleeping
| 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; | |