2023-09-28 07:23:33 +02:00
|
|
|
|
const express = require('express');
|
|
|
|
|
const router = express.Router();
|
|
|
|
|
const readline = require('readline');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
|
|
|
|
const rcon = require("../modules/rcon");
|
|
|
|
|
const is_authenticated = require("../modules/middleware");
|
|
|
|
|
|
|
|
|
|
const ALLOWED_STEAM_IDS = ['76561198154367261']
|
|
|
|
|
|
|
|
|
|
router.post('/api/setup-game', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
|
|
|
|
const team1 = req.body.team1;
|
|
|
|
|
const team2 = req.body.team2;
|
2024-10-29 20:25:01 +01:00
|
|
|
|
const map = req.body.map;
|
|
|
|
|
const game = req.body.game.toString();
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
if (team1.trim() != "") {
|
|
|
|
|
await rcon.execute_command(server_id, `mp_teamname_1 "${team1}"`);
|
|
|
|
|
}
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
if (team2.trim() != "") {
|
|
|
|
|
await rcon.execute_command(server_id, `mp_teamname_2 "${team2}"`);
|
|
|
|
|
}
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
2024-11-16 18:08:04 +01:00
|
|
|
|
if (game == "1") { // Competitive Short
|
|
|
|
|
await rcon.execute_command(server_id, `game_type 0`);
|
2024-10-17 22:44:27 +02:00
|
|
|
|
await rcon.execute_command(server_id, `game_mode 1`);
|
2024-11-16 18:18:30 +01:00
|
|
|
|
await rcon.execute_command(server_id, `sv_skirmish_id 0`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_competitive_16.cfg');
|
2024-11-16 18:08:04 +01:00
|
|
|
|
await rcon.execute_command(server_id, `mp_warmup_pausetimer 1`);
|
|
|
|
|
} else if (game == "2") { // Competitive Long
|
|
|
|
|
await rcon.execute_command(server_id, `game_type 0`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
await rcon.execute_command(server_id, `game_mode 1`);
|
2024-11-16 18:18:30 +01:00
|
|
|
|
await rcon.execute_command(server_id, `sv_skirmish_id 0`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_competitive_24.cfg');
|
2024-11-16 18:08:04 +01:00
|
|
|
|
await rcon.execute_command(server_id, `mp_warmup_pausetimer 1`);
|
|
|
|
|
} else if (game == "3") { // Casual Short
|
|
|
|
|
await rcon.execute_command(server_id, `game_type 0`);
|
2024-10-29 20:33:05 +01:00
|
|
|
|
await rcon.execute_command(server_id, `game_mode 0`);
|
2024-11-16 18:18:30 +01:00
|
|
|
|
await rcon.execute_command(server_id, `sv_skirmish_id 0`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_casual_16.cfg');
|
2024-11-16 18:08:04 +01:00
|
|
|
|
await rcon.execute_command(server_id, `mp_warmup_pausetimer 1`);
|
|
|
|
|
} else if (game == "4") { // Casual Long
|
|
|
|
|
await rcon.execute_command(server_id, `game_type 0`);
|
2024-10-29 20:33:05 +01:00
|
|
|
|
await rcon.execute_command(server_id, `game_mode 0`);
|
2024-11-16 18:18:30 +01:00
|
|
|
|
await rcon.execute_command(server_id, `sv_skirmish_id 0`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_casual_24.cfg');
|
2024-11-16 18:08:04 +01:00
|
|
|
|
await rcon.execute_command(server_id, `mp_warmup_pausetimer 1`);
|
|
|
|
|
} else if (game == "5") { // Wingman
|
|
|
|
|
await rcon.execute_command(server_id, `game_type 0`);
|
2024-10-17 22:44:27 +02:00
|
|
|
|
await rcon.execute_command(server_id, `game_mode 2`);
|
2024-11-16 18:18:30 +01:00
|
|
|
|
await rcon.execute_command(server_id, `sv_skirmish_id 0`);
|
2023-09-28 07:23:33 +02:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_wingman.cfg');
|
2024-11-16 18:08:04 +01:00
|
|
|
|
await rcon.execute_command(server_id, `mp_warmup_pausetimer 1`);
|
|
|
|
|
} else if (game == "6") { // Arms race
|
|
|
|
|
await rcon.execute_command(server_id, `game_type 1`);
|
|
|
|
|
await rcon.execute_command(server_id, `game_mode 0`);
|
2024-11-16 18:18:30 +01:00
|
|
|
|
await rcon.execute_command(server_id, `sv_skirmish_id 10`);
|
2024-11-16 18:08:04 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_arms_race.cfg');
|
2023-09-28 07:23:33 +02:00
|
|
|
|
}
|
2024-11-16 18:08:04 +01:00
|
|
|
|
|
2024-10-29 20:25:01 +01:00
|
|
|
|
await rcon.execute_command(server_id, `changelevel ${map}`);
|
2023-09-28 07:23:33 +02:00
|
|
|
|
|
|
|
|
|
// Adding 1 second delay in executing warmup.cfg to make it effective after map has been changed.
|
2024-10-29 20:33:05 +01:00
|
|
|
|
if (game == "1" || game == "3" || game == "5") {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/warmup_16.cfg');
|
|
|
|
|
}, 1000)
|
|
|
|
|
} else if (game == "2" || game == "4") {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/warmup_24.cfg');
|
|
|
|
|
}, 1000)
|
2024-11-16 18:27:47 +01:00
|
|
|
|
} else if (game == "6") {
|
|
|
|
|
setTimeout(() => {
|
2024-11-16 18:31:00 +01:00
|
|
|
|
rcon.execute_command(server_id, `mp_restartgame 30`);
|
2024-11-16 18:27:47 +01:00
|
|
|
|
}, 1000)
|
2024-10-29 20:33:05 +01:00
|
|
|
|
}
|
2023-09-28 07:23:33 +02:00
|
|
|
|
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Game Created!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.post('/api/restart', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2023-10-11 19:44:29 +02:00
|
|
|
|
await rcon.execute_command(server_id, `mp_restartgame 1`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Game restarted' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
router.post('/api/start-warmup', is_authenticated, async (req, res) => {
|
2023-09-28 07:23:33 +02:00
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/warmup.cfg');
|
2024-10-29 20:15:00 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/warmup_restart.cfg');
|
2023-09-28 07:23:33 +02:00
|
|
|
|
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Warmup started!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.post('/api/swap-team', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2023-10-11 19:44:29 +02:00
|
|
|
|
await rcon.execute_command(server_id, `mp_swapteams`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Teams Swapped!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.post('/api/go-live', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
2024-10-29 20:35:48 +01:00
|
|
|
|
const res_mode = await rcon.execute_command(server_id, `game_mode`);
|
|
|
|
|
const game_mode = res_mode.split("=")[1].trim().toString();
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
2024-10-29 20:35:48 +01:00
|
|
|
|
const res_type = await rcon.execute_command(server_id, `game_type`);
|
|
|
|
|
const game_type = res_type.split("=")[1].trim().toString();
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
2024-10-29 20:35:48 +01:00
|
|
|
|
const res_rounds = await rcon.execute_command(server_id, `mp_maxrounds`);
|
|
|
|
|
const maxrounds = res_rounds.split("=")[1].trim().toString();
|
2024-10-29 20:15:00 +01:00
|
|
|
|
|
|
|
|
|
if (game_mode == "1" && maxrounds == "16") {
|
|
|
|
|
console.log("Executing live_competitive_16.cfg")
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_competitive_16.cfg');
|
|
|
|
|
} else if (game_mode == "1" && maxrounds == "24") {
|
|
|
|
|
console.log("Executing live_competitive_24.cfg")
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_competitive_24.cfg');
|
|
|
|
|
} else if (game_mode == "0" && maxrounds == "16") {
|
|
|
|
|
console.log("Executing live_casual_16.cfg")
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_casual_16.cfg');
|
|
|
|
|
} else if (game_mode == "0" && maxrounds == "24") {
|
|
|
|
|
console.log("Executing live_casual_24.cfg")
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_casual_24.cfg');
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} else if (game_mode == "2") {
|
|
|
|
|
console.log("Executing live_wingman.cfg")
|
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_wingman.cfg');
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 20:15:00 +01:00
|
|
|
|
execute_cfg_on_server(server_id, './cfg/live_restart.cfg');
|
|
|
|
|
|
|
|
|
|
return res.status(200).json({ message: 'Match is live!!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// List Round Backups API
|
|
|
|
|
router.post('/api/list-backups', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2023-10-11 19:44:29 +02:00
|
|
|
|
const response = await rcon.execute_command(server_id, "mp_backup_restore_list_files");
|
2023-09-28 07:23:33 +02:00
|
|
|
|
console.log('Server response:', response);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: response });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error)
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Restore Round API
|
|
|
|
|
router.post('/api/restore-round', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
|
|
|
|
let round_number = req.body.round_number.toString()
|
|
|
|
|
if (round_number.length == 1) {
|
|
|
|
|
round_number = "0" + round_number;
|
|
|
|
|
}
|
|
|
|
|
console.log(`SENDING mp_backup_restore_load_file backup_round${round_number}.txt`)
|
2023-10-11 19:44:29 +02:00
|
|
|
|
await rcon.execute_command(server_id, `mp_backup_restore_load_file backup_round${round_number}.txt`);
|
|
|
|
|
await rcon.execute_command(server_id, `mp_pause_match`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Round Restored!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.post('/api/restore-latest-backup', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2023-10-11 19:44:29 +02:00
|
|
|
|
const response = await rcon.execute_command(server_id, `mp_backup_round_file_last`);
|
2023-09-28 07:23:33 +02:00
|
|
|
|
const last_round_file = response.split("=")[1].trim().toString();
|
|
|
|
|
if (last_round_file.includes('.txt')) {
|
2023-10-11 19:44:29 +02:00
|
|
|
|
await rcon.execute_command(server_id, `mp_backup_restore_load_file ${last_round_file}`);
|
|
|
|
|
await rcon.execute_command(server_id, `mp_pause_match`);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: `Latest Round Restored! (${last_round_file})` });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} else {
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'No latest backup found!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Pause Game API
|
|
|
|
|
router.post('/api/pause', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2023-10-11 19:44:29 +02:00
|
|
|
|
const response = await rcon.execute_command(server_id, 'mp_pause_match');
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Game paused' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Unpause Game API
|
|
|
|
|
router.post('/api/unpause', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
2023-10-11 19:44:29 +02:00
|
|
|
|
const response = await rcon.execute_command(server_id, 'mp_unpause_match');
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Game unpaused' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.post('/api/rcon', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
|
|
|
|
const command = req.body.command;
|
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
const response = await rcon.execute_command(server_id, command);
|
2023-09-28 07:23:33 +02:00
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
if (response == 200) {
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Command sent!' });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Command sent! Response received:\n' + response.toString() });
|
2023-09-28 07:23:33 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
router.post('/api/say-admin', is_authenticated, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const server_id = req.body.server_id;
|
|
|
|
|
const message = req.body.message;
|
|
|
|
|
const message_to_send = "say " + message;
|
|
|
|
|
await rcon.execute_command(server_id, message_to_send);
|
2024-10-29 20:15:00 +01:00
|
|
|
|
return res.status(200).json({ message: 'Message sent!' });
|
2023-10-11 19:44:29 +02:00
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2023-09-28 07:23:33 +02:00
|
|
|
|
function check_whitelisted_players() {
|
|
|
|
|
rcon.rcons[server_id].execute('status_json')
|
|
|
|
|
.then((response) => {
|
|
|
|
|
console.log(response)
|
|
|
|
|
const server_status = JSON.parse(response)
|
|
|
|
|
const players = server_status['server']['clients']
|
|
|
|
|
for (var i = 0; i < players.length; i++) {
|
|
|
|
|
let player = players[i]
|
|
|
|
|
if (!player.bot && player.steamid64.includes('7656') && !ALLOWED_STEAM_IDS.includes(player.steamid64)) {
|
|
|
|
|
console.log(`kick ${player.name}`)
|
|
|
|
|
rcon.rcons[server_id].execute(`kick ${player.name}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
})
|
|
|
|
|
.catch(console.error);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-04 09:29:23 +02:00
|
|
|
|
|
|
|
|
|
function splitByByteLength(data, length) {
|
|
|
|
|
const lines = data;
|
|
|
|
|
const exportedLines = [];
|
|
|
|
|
const lineEndChar = '; '
|
|
|
|
|
let index = 0;
|
|
|
|
|
|
|
|
|
|
for(let item = 0; item < lines.length; item++) {
|
|
|
|
|
if(typeof exportedLines[index] === "undefined") {
|
|
|
|
|
exportedLines[index] = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const lineFormatted = `${lines[item]}${lineEndChar}`;
|
|
|
|
|
const lineBytes = Buffer.byteLength(lineFormatted, 'utf8');
|
|
|
|
|
const bufferBytes = Buffer.byteLength(exportedLines[index], 'utf8');
|
|
|
|
|
|
|
|
|
|
if((bufferBytes + lineBytes) < length) {
|
|
|
|
|
exportedLines[index] += lineFormatted;
|
|
|
|
|
} else {
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return exportedLines;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
async function execute_cfg_on_server(server_id, cfg_path) {
|
2023-09-28 07:23:33 +02:00
|
|
|
|
|
2023-10-04 09:29:23 +02:00
|
|
|
|
fs.readFile(cfg_path, 'utf8', (err, data) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
throw err;
|
2023-09-28 07:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-04 09:29:23 +02:00
|
|
|
|
data = data.replace(/^\/\/.*$/m, '');
|
|
|
|
|
data = data.split("\n");
|
|
|
|
|
const new_data = [];
|
|
|
|
|
for (let i = 0; i < data.length; i += 1) {
|
|
|
|
|
const line = data[i].trim();
|
|
|
|
|
const segments = line.split(' ');
|
|
|
|
|
|
|
|
|
|
if(segments[0] === 'say' || segments.length == 1) {
|
|
|
|
|
new_data.push(line);
|
|
|
|
|
} else if (segments[0] !== '' && segments[0] !== '//') {
|
|
|
|
|
new_data.push(`${segments[0]} ${segments[1].split('\t')[0]}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const exported_lines = splitByByteLength(data, 512)
|
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
async function execute_next_item(item) {
|
2023-10-11 15:59:14 +02:00
|
|
|
|
try {
|
|
|
|
|
if (item < exported_lines.length) {
|
2023-10-11 19:44:29 +02:00
|
|
|
|
console.log("Executing on server:", exported_lines[item]);
|
|
|
|
|
await rcon.execute_command(server_id, exported_lines[item]);
|
2023-10-11 15:59:14 +02:00
|
|
|
|
|
2023-10-11 19:44:29 +02:00
|
|
|
|
// Wait for 100ms before moving to the next iteration
|
2023-10-11 15:59:14 +02:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
execute_next_item(item + 1);
|
2023-10-11 19:44:29 +02:00
|
|
|
|
}, 100);
|
2023-10-11 15:59:14 +02:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log("[execute_next_item] Error:", error)
|
2023-10-04 09:29:23 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
execute_next_item(0);
|
2023-09-28 07:23:33 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
router
|
2023-09-29 15:16:13 +02:00
|
|
|
|
};
|