File size: 4,325 Bytes
2bf7a2b
997f2d0
7a64905
997f2d0
2bf7a2b
 
997f2d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2bf7a2b
997f2d0
 
 
 
 
 
 
 
2bf7a2b
997f2d0
2bf7a2b
 
 
 
 
 
 
997f2d0
2bf7a2b
 
 
 
 
 
997f2d0
2bf7a2b
997f2d0
 
 
2bf7a2b
 
997f2d0
 
 
 
 
 
 
 
 
 
 
 
 
2bf7a2b
7a64905
997f2d0
2bf7a2b
 
 
 
997f2d0
2bf7a2b
 
997f2d0
2bf7a2b
 
 
 
 
997f2d0
 
2bf7a2b
 
997f2d0
2bf7a2b
 
 
 
 
 
 
 
 
997f2d0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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;