diff --git a/manager/package.json b/manager/package.json index d955981..e4e6a54 100644 --- a/manager/package.json +++ b/manager/package.json @@ -1,11 +1,12 @@ { "name": "manager", "version": "1.0.0", - "description": "", - "main": "index.js", + "description": "Server Manager for dedicated game servers", + "main": "dist/index.js", "scripts": { - "start": "ts-node src/index.ts", - "build": "tsc -p tsconfig.json" + "dev": "ts-node src/index.ts", + "build": "tsc -p tsconfig.json", + "start": "node dist/index.js" }, "keywords": [], "author": "", @@ -20,4 +21,4 @@ "ts-node": "^10.9.2", "typescript": "^5.7.3" } -} +} \ No newline at end of file diff --git a/manager/src/command.js b/manager/src/command.js deleted file mode 100644 index 37d6097..0000000 --- a/manager/src/command.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CommandService = void 0; -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("CommandService"); -class CommandService { - constructor(channel) { - this.channel = channel; - } - async send(queue, command) { - await this.channel.sendToQueue(queue, Buffer.from(JSON.stringify(command))); - logger.debug("Command sent", command); - } -} -exports.CommandService = CommandService; diff --git a/manager/src/config.js b/manager/src/config.js deleted file mode 100644 index 321cded..0000000 --- a/manager/src/config.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.QUERY_PORT_START = exports.GAME_PORT_START = exports.DEFAULT_DESIRED_SERVERS = exports.LIFECYCLE_CRASH_RETENTION = exports.LIFECYCLE_CRASH_TIMEOUT = exports.LIFECYCLE_SCALE_COOLDOWN = exports.LIFECYCLE_STARTUP_GRACE_PERIOD = exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = exports.LIFECYCLE_MIN_UPTIME = exports.LIFECYCLE_EMPTY_TIMEOUT = exports.LIFECYCLE_CHECK_INTERVAL = exports.RABBITMQ_URL = void 0; -exports.RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74"; -// Lifecycle -exports.LIFECYCLE_CHECK_INTERVAL = 10000; // Alle 10 Sekunden prüfen -// Scale Down -exports.LIFECYCLE_EMPTY_TIMEOUT = 300000; // 5 Minuten leer -exports.LIFECYCLE_MIN_UPTIME = 600000; // 10 Minuten Mindestlaufzeit -exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; // Unter 30% Gesamtauslastung darf abgebaut werden -// Scale Up -exports.LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; // 1 Minute Schutz nach Start -exports.LIFECYCLE_SCALE_COOLDOWN = 120000; // 2 Minuten zwischen Scale-Up Aktionen -// Crash Detection -exports.LIFECYCLE_CRASH_TIMEOUT = 120000; // 2 Minuten ohne Status -> Crash -exports.LIFECYCLE_CRASH_RETENTION = 300000; // 5 Minuten sichtbar nach Crash -// Default -exports.DEFAULT_DESIRED_SERVERS = 1; -// Ports -exports.GAME_PORT_START = 7777; -exports.QUERY_PORT_START = 27015; diff --git a/manager/src/index.js b/manager/src/index.js deleted file mode 100644 index 962aa61..0000000 --- a/manager/src/index.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const rabbit_1 = require("./rabbit"); -const wrapperManager_1 = require("./wrapperManager"); -const wrapperRegistry_1 = require("./wrapperRegistry"); -const serverRegistry_1 = require("./serverRegistry"); -const serverManager_1 = require("./serverManager"); -const portManager_1 = require("./portManager"); -const serverStatusManager_1 = require("./serverStatusManager"); -const serverLifecycle_1 = require("./serverLifecycle"); -const logger_1 = require("./logger"); -const server_1 = require("./web/server"); -const logger = (0, logger_1.getLogger)("Index"); -const wrapperRegistry = new wrapperRegistry_1.WrapperRegistry(); -const serverRegistry = new serverRegistry_1.ServerRegistry(); -const portManager = new portManager_1.PortManager(); -async function main() { - const rabbit = new rabbit_1.Rabbit(); - await rabbit.connect(); - logger.info("RabbitMQ-Verbindung hergestellt"); - await portManager.init(); - logger.info("Persistenz initialisiert"); - await wrapperRegistry.init(); - logger.debug("WrapperRegistry initialisiert"); - await serverRegistry.init(); - logger.debug("ServerRegistry initialisiert"); - const wrapperManager = new wrapperManager_1.WrapperManager(rabbit, wrapperRegistry, serverRegistry); - await wrapperManager.start(); - logger.info("WrapperManager erfolgreich gestartet"); - const serverManager = new serverManager_1.ServerManager(rabbit, wrapperRegistry, serverRegistry, portManager); - const lifecycle = new serverLifecycle_1.ServerLifecycle(serverRegistry, serverManager); - lifecycle.start(); - logger.debug("ServerLifecycle gestartet"); - const statusManager = new serverStatusManager_1.ServerStatusManager(rabbit, serverRegistry); - await statusManager.start(); - const webServer = (0, server_1.createWebServer)({ - wrapperRegistry, - serverRegistry, - lifecycle, - rabbitMqStatus: "connected", - managerStatus: "running" - }, 3000); - logger.info("Manager gestartet"); - void webServer; -} -main(); diff --git a/manager/src/logger.js b/manager/src/logger.js deleted file mode 100644 index c408656..0000000 --- a/manager/src/logger.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getLogger = getLogger; -const LEVELS = { - DEBUG: 0, - INFO: 1, - WARN: 2, - ERROR: 3, -}; -const DEFAULT_LOG_LEVEL = "INFO"; -const configuredLevel = (process.env.LOG_LEVEL ?? "").toUpperCase(); -const currentLogLevel = configuredLevel in LEVELS ? configuredLevel : DEFAULT_LOG_LEVEL; -const currentLogLevelValue = LEVELS[currentLogLevel]; -function shouldLog(level) { - return LEVELS[level] >= currentLogLevelValue; -} -function formatMessage(level, component, message) { - return `${new Date().toISOString()} ${level} [${component}] ${message}`; -} -class Logger { - constructor(component) { - this.component = component; - } - debug(message, ...args) { - if (!shouldLog("DEBUG")) - return; - console.log(formatMessage("DEBUG", this.component, message), ...args); - } - info(message, ...args) { - if (!shouldLog("INFO")) - return; - console.log(formatMessage("INFO", this.component, message), ...args); - } - warn(message, ...args) { - if (!shouldLog("WARN")) - return; - console.warn(formatMessage("WARN", this.component, message), ...args); - } - error(message, ...args) { - if (!shouldLog("ERROR")) - return; - console.error(formatMessage("ERROR", this.component, message), ...args); - } -} -function getLogger(component) { - return new Logger(component); -} diff --git a/manager/src/nameGenerator.js b/manager/src/nameGenerator.js deleted file mode 100644 index b611e4b..0000000 --- a/manager/src/nameGenerator.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.generateServerName = generateServerName; -const locations = [ - "Crimson", - "Azure", - "Emerald", - "Amber", - "Sapphire", - "Ruby", - "Obsidian", - "Pearl", - "Onyx", - "Ivory", - "Copper", - "Platinum", - "Titanium", - "Neon", - "Shadow", - "Frost", - "Solar", - "Void", - "Storm", - "Ocean" -]; -function generateServerName(existingNames) { - for (const location of locations) { - const name = "[EU] druecktieR | " + location; - if (!existingNames.includes(name)) { - return name; - } - } - /* - Falls alle Namen vergeben sind - */ - return ("[EU] druecktieR | " + - Math.random() - .toString(36) - .substring(2, 6)); -} diff --git a/manager/src/persistence/jsonFileStore.js b/manager/src/persistence/jsonFileStore.js deleted file mode 100644 index 4e1dd8b..0000000 --- a/manager/src/persistence/jsonFileStore.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.JsonFileStore = void 0; -const fs_1 = require("fs"); -const logger_1 = require("../logger"); -const logger = (0, logger_1.getLogger)("JsonFileStore"); -class JsonFileStore { - constructor(filePath) { - this.filePath = filePath; - } - async load() { - try { - const fileContents = await fs_1.promises.readFile(this.filePath, "utf-8"); - return JSON.parse(fileContents); - } - catch (error) { - if (error.code === "ENOENT") { - return null; - } - logger.error(`Fehler beim Laden der Persistenzdatei ${this.filePath}:`, error); - throw error; - } - } - async save(data) { - try { - const fileContents = JSON.stringify(data, null, 4); - await fs_1.promises.writeFile(this.filePath, fileContents, "utf-8"); - } - catch (error) { - logger.error(`Fehler beim Speichern der Persistenzdatei ${this.filePath}:`, error); - throw error; - } - } -} -exports.JsonFileStore = JsonFileStore; diff --git a/manager/src/persistence/persistenceStore.js b/manager/src/persistence/persistenceStore.js deleted file mode 100644 index c8ad2e5..0000000 --- a/manager/src/persistence/persistenceStore.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/manager/src/portManager.js b/manager/src/portManager.js deleted file mode 100644 index 27288d0..0000000 --- a/manager/src/portManager.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PortManager = void 0; -const config_1 = require("./config"); -const jsonFileStore_1 = require("./persistence/jsonFileStore"); -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("PortManager"); -class PortManager { - constructor() { - this.usedPorts = new Set(); - this.usedQueryPorts = new Set(); - this.gamePortStart = config_1.GAME_PORT_START; - this.queryPortStart = config_1.QUERY_PORT_START; - this.persistence = new jsonFileStore_1.JsonFileStore("portManager-state.json"); - } - async init() { - const state = await this.persistence.load(); - if (!state) - return; - this.usedPorts = - new Set(state.usedPorts); - this.usedQueryPorts = - new Set(state.usedQueryPorts); - } - allocate() { - let port = this.gamePortStart; - while (this.usedPorts.has(port)) { - port++; - } - let queryPort = this.queryPortStart; - while (this.usedQueryPorts.has(queryPort)) { - queryPort++; - } - this.usedPorts.add(port); - this.usedQueryPorts.add(queryPort); - logger.debug(`Port erfolgreich reserviert: ${port}`); - logger.debug(`Query-Port erfolgreich reserviert: ${queryPort}`); - this.saveState(); - return { - port, - queryPort - }; - } - release(port, queryPort) { - const removedPort = this.usedPorts.delete(port); - const removedQueryPort = this.usedQueryPorts.delete(queryPort); - if (!removedPort || !removedQueryPort) { - logger.warn(`Unerwarteter Port-Zustand während der Freigabe: port=${port}, queryPort=${queryPort}`); - } - else { - logger.debug(`Port wieder freigegeben: ${port}`); - logger.debug(`Query-Port wieder freigegeben: ${queryPort}`); - } - this.saveState(); - } - saveState() { - const state = { - usedPorts: Array.from(this.usedPorts), - usedQueryPorts: Array.from(this.usedQueryPorts) - }; - this.persistence - .save(state) - .catch(error => { - logger.error("Fehler beim Speichern des PortManager-Zustands:", error); - }); - } -} -exports.PortManager = PortManager; diff --git a/manager/src/rabbit.js b/manager/src/rabbit.js deleted file mode 100644 index e3dbf23..0000000 --- a/manager/src/rabbit.js +++ /dev/null @@ -1,50 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Rabbit = void 0; -const amqp = __importStar(require("amqplib")); -const config_1 = require("./config"); -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("Rabbit"); -class Rabbit { - async connect() { - this.connection = - await amqp.connect(config_1.RABBITMQ_URL); - this.channel = - await this.connection.createChannel(); - logger.info("Connected to RabbitMQ"); - } -} -exports.Rabbit = Rabbit; diff --git a/manager/src/serverLifecycle.js b/manager/src/serverLifecycle.js deleted file mode 100644 index 2705264..0000000 --- a/manager/src/serverLifecycle.js +++ /dev/null @@ -1,225 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ServerLifecycle = void 0; -const logger_1 = require("./logger"); -const config_1 = require("./config"); -const logger = (0, logger_1.getLogger)("ServerLifecycle"); -class ServerLifecycle { - constructor(serverRegistry, serverManager, desiredServers = config_1.DEFAULT_DESIRED_SERVERS) { - this.serverRegistry = serverRegistry; - this.serverManager = serverManager; - this.desiredServers = desiredServers; - this.creatingServers = 0; - this.emptyTimeout = config_1.LIFECYCLE_EMPTY_TIMEOUT; - this.lastScaleUp = 0; - this.scaleCooldown = config_1.LIFECYCLE_SCALE_COOLDOWN; - } - start() { - logger.info("Server Lifecycle gestartet"); - setInterval(() => { - this.checkServers(); - }, config_1.LIFECYCLE_CHECK_INTERVAL); - } - setDesiredServers(count) { - logger.debug("Neue Soll Server Anzahl:", count); - this.desiredServers = - count; - } - /** - * Exposes the configured target for monitoring consumers. - * It deliberately does not influence lifecycle behaviour. - */ - getDesiredServers() { - return this.desiredServers; - } - checkServers() { - const servers = this.serverRegistry.getAll(); - /* - STATUS AKTUALISIEREN - */ - for (const server of servers) { - switch (server.status) { - case "STARTING": - if (server.pid) { - server.status = - "RUNNING"; - server.startedAt = - Date.now(); - server.emptySince = - undefined; - this.serverRegistry.update(server); - logger.info("Server RUNNING:", server.name, "PID:", server.pid); - } - break; - case "RECOVERING": - if (Date.now() - - server.lastSeen - > - config_1.LIFECYCLE_CRASH_TIMEOUT) { - server.status = - "CRASHED"; - server.crashedAt = - Date.now(); - this.serverRegistry.update(server); - logger.error("Server-Recovery fehlgeschlagen:", server.name); - } - break; - case "RUNNING": - /* - Crash Detection - */ - if (Date.now() - - server.lastSeen - > - config_1.LIFECYCLE_CRASH_TIMEOUT) { - server.status = - "CRASHED"; - server.crashedAt = - Date.now(); - this.serverRegistry.update(server); - logger.error("Server abgestürzt:", server.name); - break; - } - /* - Leere Server tracken - */ - if (server.players === 0) { - if (!server.emptySince) { - if (server.startedAt && - Date.now() - - server.startedAt < - config_1.LIFECYCLE_STARTUP_GRACE_PERIOD) { - break; - } - server.emptySince = - Date.now(); - this.serverRegistry.update(server); - logger.debug("Server leer:", server.name); - } - } - else { - if (server.emptySince) { - logger.debug("Server wieder aktiv:", server.name); - } - server.emptySince = - undefined; - this.serverRegistry.update(server); - } - break; - case "CRASHED": - if (!server.crashedAt) { - server.crashedAt = - Date.now(); - this.serverRegistry.update(server); - logger.error("Server abgestürzt erkannt:", server.name); - break; - } - if (Date.now() - - server.crashedAt - > - config_1.LIFECYCLE_CRASH_RETENTION) { - logger.info("Entferne abgestürzten Server nach Grace-Zeit:", server.name); - this.serverRegistry.remove(server.id); - } - break; - } - } - /* - AKTIVE SERVER - */ - const activeServers = this.serverRegistry - .getAll() - .filter(server => server.status === "RUNNING" || - server.status === "STARTING" || - server.status === "RECOVERING"); - /* - SLOT AUSLASTUNG - */ - const runningServers = activeServers.filter(server => server.status === "RUNNING"); - const totalPlayers = runningServers.reduce((sum, server) => sum + server.players, 0); - const totalSlots = runningServers.reduce((sum, server) => sum + server.maxPlayers, 0); - const usage = totalSlots > 0 - ? - totalPlayers / totalSlots - : - 0; - logger.debug("Kapazität:", `${totalPlayers}/${totalSlots}`, `${(usage * 100).toFixed(1)}%`); - /* - MINDEST SERVER ANZAHL - */ - const totalActive = activeServers.length + - this.creatingServers; - if (totalActive < - this.desiredServers) { - const missing = this.desiredServers - - totalActive; - this.startServers(missing); - } - /* - SCALE UP BEI 90% - */ - const shouldScale = runningServers.length > 0 && - this.creatingServers === 0 && - usage >= 0.90 && - Date.now() - - this.lastScaleUp - > - this.scaleCooldown; - if (shouldScale) { - logger.info("Auslastung hoch - starte zusätzlichen Server"); - this.lastScaleUp = - Date.now(); - this.startServers(1); - } - /* - LEERE SERVER ENTFERNEN - */ - const removableServers = activeServers - .filter(server => server.status === "RUNNING" && - server.players === 0 && - server.emptySince && - server.startedAt && - Date.now() - - server.startedAt - >= - config_1.LIFECYCLE_MIN_UPTIME && - Date.now() - - server.emptySince - > - this.emptyTimeout && - usage < - config_1.LIFECYCLE_SCALE_DOWN_THRESHOLD) - .sort((a, b) => (a.emptySince ?? 0) - - (b.emptySince ?? 0)); - for (const server of removableServers) { - const activeCount = this.serverRegistry - .getAll() - .filter(s => s.status === "RUNNING" || - s.status === "STARTING" || - s.status === "RECOVERING") - .length; - if (activeCount <= - this.desiredServers) { - break; - } - logger.info("Stoppe leeren Server:", server.name); - this.serverManager.stopServer(server.id); - } - } - startServers(count) { - logger.info("Starte Server:", count); - for (let i = 0; i < count; i++) { - this.creatingServers++; - this.serverManager - .createServer() - .then(() => { - this.creatingServers--; - }) - .catch(err => { - this.creatingServers--; - logger.error("Server Erstellung fehlgeschlagen:", err); - }); - } - } -} -exports.ServerLifecycle = ServerLifecycle; diff --git a/manager/src/serverLifecycleManager.js b/manager/src/serverLifecycleManager.js deleted file mode 100644 index 0201f01..0000000 --- a/manager/src/serverLifecycleManager.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ServerLifecycleManager = void 0; -class ServerLifecycleManager { - constructor(serverRegistry, rabbit) { - this.serverRegistry = serverRegistry; - this.rabbit = rabbit; - } - start() { - console.log("Server Lifecycle Manager gestartet"); - setInterval(() => { - this.checkServers(); - }, 5000); - } - checkServers() { - const servers = this.serverRegistry.getAll(); - for (const server of servers) { - switch (server.status) { - case "STARTING": - this.checkStarting(server); - break; - case "RUNNING": - this.checkRunning(server); - break; - case "CRASHED": - this.checkOffline(server); - break; - } - } - } - checkStarting(server) { - // kommt später - } - checkRunning(server) { - // kommt später - } - checkOffline(server) { - // kommt später - } -} -exports.ServerLifecycleManager = ServerLifecycleManager; diff --git a/manager/src/serverManager.js b/manager/src/serverManager.js deleted file mode 100644 index 3a40099..0000000 --- a/manager/src/serverManager.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ServerManager = void 0; -const logger_1 = require("./logger"); -const crypto_1 = require("crypto"); -const nameGenerator_1 = require("./nameGenerator"); -const logger = (0, logger_1.getLogger)("ServerManager"); -class ServerManager { - constructor(rabbit, wrapperRegistry, serverRegistry, portManager) { - this.rabbit = rabbit; - this.wrapperRegistry = wrapperRegistry; - this.serverRegistry = serverRegistry; - this.portManager = portManager; - } - async createServer() { - /* - freien Wrapper suchen - */ - const wrapper = this.findBestWrapper(); - if (!wrapper) { - logger.warn("Kein Wrapper verfügbar"); - return; - } - const serverId = "srv-" + - (0, crypto_1.randomUUID)() - .slice(0, 8); - const serverName = (0, nameGenerator_1.generateServerName)(this.serverRegistry - .getAll() - .map(server => server.name)); - /* - Ports reservieren - */ - const ports = this.portManager.allocate(); - const server = { - id: serverId, - name: serverName, - wrapperId: wrapper.id, - status: "STARTING", - port: ports.port, - queryPort: ports.queryPort, - players: 0, - maxPlayers: 21, - created: Date.now(), - lastSeen: Date.now() - }; - /* - Registry Eintrag - */ - this.serverRegistry.add(server); - /* - Command an Wrapper - */ - const command = { - type: "server.start", - serverId, - name: serverName, - port: ports.port, - queryPort: ports.queryPort - }; - const sent = this.rabbit.channel.sendToQueue(wrapper.commandQueue, Buffer.from(JSON.stringify(command))); - if (!sent) { - throw new Error("Server Start Command konnte nicht gesendet werden"); - } - logger.info("Starting server:", server.id); - } - async stopServer(serverId) { - const server = this.serverRegistry.get(serverId); - if (!server) { - logger.warn("Server not found:", serverId); - return; - } - const wrapper = this.wrapperRegistry.get(server.wrapperId); - if (!wrapper) { - logger.warn("Wrapper not found:", server.wrapperId); - return; - } - const command = { - type: "server.stop", - serverId - }; - this.rabbit.channel.sendToQueue(wrapper.commandQueue, Buffer.from(JSON.stringify(command))); - server.status = - "STOPPING"; - this.serverRegistry.update(server); - logger.info("Stopping server:", serverId); - } - findBestWrapper() { - const wrappers = this.wrapperRegistry.getAll() - .filter(w => w.online); - if (wrappers.length === 0) - return undefined; - /* - Einfachste Version: - - wenigste Server - */ - wrappers.sort((a, b) => a.servers.length - - b.servers.length); - return wrappers[0]; - } -} -exports.ServerManager = ServerManager; diff --git a/manager/src/serverRegistry.js b/manager/src/serverRegistry.js deleted file mode 100644 index c380884..0000000 --- a/manager/src/serverRegistry.js +++ /dev/null @@ -1,151 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ServerRegistry = void 0; -const logger_1 = require("./logger"); -const jsonFileStore_1 = require("./persistence/jsonFileStore"); -const logger = (0, logger_1.getLogger)("ServerRegistry"); -class ServerRegistry { - constructor() { - this.servers = new Map(); - this.persistence = new jsonFileStore_1.JsonFileStore("serverRegistry-state.json"); - } - async init() { - const state = await this.persistence.load(); - if (!state) - return; - for (const serverData of state.servers) { - this.servers.set(serverData.id, { - id: serverData.id, - name: serverData.name, - wrapperId: serverData.wrapperId, - port: serverData.port, - queryPort: serverData.queryPort, - created: serverData.created, - status: "RECOVERING", - players: 0, - maxPlayers: 0, - lastSeen: Date.now() - }); - } - } - toState() { - return { - servers: Array.from(this.servers.values()).map(server => ({ - id: server.id, - name: server.name, - wrapperId: server.wrapperId, - port: server.port, - queryPort: server.queryPort, - created: server.created - })) - }; - } - async saveState() { - await this.persistence.save(this.toState()); - } - persist() { - void this.saveState(); - } - add(server) { - this.servers.set(server.id, server); - logger.info("Server added:", server.id); - this.persist(); - } - get(id) { - return this.servers.get(id); - } - getAll() { - return Array.from(this.servers.values()); - } - update(server) { - this.servers.set(server.id, server); - this.persist(); - } - remove(id) { - this.servers.delete(id); - logger.info("Server removed:", id); - this.persist(); - } - list() { - logger.info("Server list output"); - const servers = this.getAll(); - if (servers.length === 0) { - logger.info("Keine Server vorhanden"); - return; - } - for (const server of servers) { - logger.info({ - id: server.id, - name: server.name, - wrapper: server.wrapperId, - status: server.status, - port: server.port, - queryPort: server.queryPort, - players: `${server.players}/${server.maxPlayers}` - }); - } - } - syncWrapperStatus(data) { - if (!data.servers) - return; - const runningServerIds = new Set(data.servers.map(srv => srv.id)); - for (const server of this.servers.values()) { - if (server.wrapperId === data.wrapperId && - !runningServerIds.has(server.id)) { - if (server.status === "STOPPING") { - logger.debug("Server cleanly stopped:", server.name); - this.remove(server.id); - } - else if (server.status === "RUNNING" || - server.status === "RECOVERING") { - server.status = - "CRASHED"; - if (!server.crashedAt) { - server.crashedAt = - Date.now(); - } - this.servers.set(server.id, server); - logger.warn("Server crashed:", server.name); - } - } - } - for (const srv of data.servers) { - const server = this.servers.get(srv.id); - if (!server) - continue; - server.pid = - srv.pid; - server.players = - srv.players ?? 0; - server.maxPlayers = - srv.maxPlayers ?? 0; - server.lastSeen = - Date.now(); - const oldStatus = server.status; - if (srv.alive && server.status !== "STOPPING") { - server.crashedAt = - undefined; - if (!server.startedAt) { - server.startedAt = - srv.started; - } - server.status = - "RUNNING"; - } - else if (server.status === "RUNNING" || - server.status === "RECOVERING") { - server.status = - "CRASHED"; - if (!server.crashedAt) { - server.crashedAt = - Date.now(); - } - } - if (oldStatus !== server.status) { - logger.debug("Status change:", server.name, oldStatus, "->", server.status); - } - this.servers.set(server.id, server); - } - } -} -exports.ServerRegistry = ServerRegistry; diff --git a/manager/src/serverStatusManager.js b/manager/src/serverStatusManager.js deleted file mode 100644 index 22961da..0000000 --- a/manager/src/serverStatusManager.js +++ /dev/null @@ -1,61 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ServerStatusManager = void 0; -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("ServerStatusManager"); -class ServerStatusManager { - constructor(rabbit, registry) { - this.rabbit = rabbit; - this.registry = registry; - } - async start() { - const queue = "server.status"; - await this.rabbit.channel.assertQueue(queue); - logger.info("Warte auf Server Status..."); - this.rabbit.channel.consume(queue, msg => { - if (!msg) - return; - const data = JSON.parse(msg.content.toString()); - switch (data.type) { - case "server.started": - this.serverStarted(data); - break; - case "server.stopped": - this.serverStopped(data); - break; - default: - logger.warn("Unbekannter Server Status:", data); - } - this.rabbit.channel.ack(msg); - }); - } - serverStarted(data) { - const server = this.registry.get(data.serverId); - if (!server) - return; - server.status = - "RUNNING"; - server.crashedAt = - undefined; - if (!server.startedAt) { - server.startedAt = - Date.now(); - } - this.registry.update(server); - logger.info("Server started:", data.serverId); - } - serverStopped(data) { - const server = this.registry.get(data.serverId); - if (!server) - return; - server.status = - "CRASHED"; - if (!server.crashedAt) { - server.crashedAt = - Date.now(); - } - this.registry.update(server); - logger.info("Server stopped:", data.serverId); - } -} -exports.ServerStatusManager = ServerStatusManager; diff --git a/manager/src/types.js b/manager/src/types.js deleted file mode 100644 index c8ad2e5..0000000 --- a/manager/src/types.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/manager/src/web/api.js b/manager/src/web/api.js deleted file mode 100644 index 1e8843c..0000000 --- a/manager/src/web/api.js +++ /dev/null @@ -1,113 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WebApi = void 0; -class WebApi { - constructor(deps) { - this.deps = deps; - } - getHealth() { - return { - status: this.deps.managerStatus ?? "running", - timestamp: new Date().toISOString() - }; - } - getDashboard() { - const wrappers = this.deps.wrapperRegistry.getAll(); - const servers = this.deps.serverRegistry.getAll(); - const runningServers = servers.filter(server => server.status === "RUNNING").length; - const totalPlayers = servers.reduce((sum, server) => sum + server.players, 0); - const totalCapacity = servers.reduce((sum, server) => sum + server.maxPlayers, 0); - const utilizationPercent = totalCapacity > 0 ? Math.round((totalPlayers / totalCapacity) * 100) : 0; - return { - managerStatus: this.deps.managerStatus ?? "running", - rabbitMqStatus: this.deps.rabbitMqStatus ?? "connected", - wrapperCount: wrappers.length, - onlineWrapperCount: wrappers.filter(wrapper => wrapper.online).length, - serverCount: servers.length, - runningServerCount: runningServers, - totalPlayers, - totalCapacity, - utilizationPercent, - desiredServers: this.getDesiredServers(), - activeServers: servers.filter(server => server.status === "RUNNING" || server.status === "STARTING").length - }; - } - getWrappers() { - return this.deps.wrapperRegistry.getAll().map(wrapper => ({ - id: wrapper.id, - hostname: wrapper.hostname, - online: wrapper.online, - cpu: wrapper.cpu, - ram: wrapper.ram, - ramFree: wrapper.ramFree, - lastHeartbeat: wrapper.lastHeartbeat, - serverCount: wrapper.servers.length - })); - } - getWrapper(id) { - const wrapper = this.deps.wrapperRegistry.get(id); - if (!wrapper) { - return null; - } - return { - id: wrapper.id, - hostname: wrapper.hostname, - online: wrapper.online, - cpu: wrapper.cpu, - ram: wrapper.ram, - ramFree: wrapper.ramFree, - lastHeartbeat: wrapper.lastHeartbeat, - serverCount: wrapper.servers.length, - servers: wrapper.servers.map(server => ({ - id: server.id, - name: server.name, - port: server.port, - queryPort: server.queryPort, - players: server.players, - maxPlayers: server.maxPlayers, - alive: server.alive, - started: server.started - })) - }; - } - getServers() { - return this.deps.serverRegistry.getAll().map(server => ({ - id: server.id, - name: server.name, - status: server.status, - wrapperId: server.wrapperId, - pid: server.pid, - port: server.port, - queryPort: server.queryPort, - players: server.players, - maxPlayers: server.maxPlayers, - created: server.created, - startedAt: server.startedAt, - emptySince: server.emptySince - })); - } - getServer(id) { - const server = this.deps.serverRegistry.get(id); - if (!server) { - return null; - } - return { - id: server.id, - name: server.name, - status: server.status, - wrapperId: server.wrapperId, - pid: server.pid, - port: server.port, - queryPort: server.queryPort, - players: server.players, - maxPlayers: server.maxPlayers, - created: server.created, - startedAt: server.startedAt, - emptySince: server.emptySince - }; - } - getDesiredServers() { - return this.deps.lifecycle.getDesiredServers(); - } -} -exports.WebApi = WebApi; diff --git a/manager/src/web/routes.js b/manager/src/web/routes.js deleted file mode 100644 index f8b1008..0000000 --- a/manager/src/web/routes.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerRoutes = registerRoutes; -const url_1 = require("url"); -function registerRoutes(server, api) { - server.on("request", (req, res) => { - const url = (0, url_1.parse)(req.url ?? "/", true); - const pathname = url.pathname ?? "/"; - if (pathname === "/api/health") { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(api.getHealth())); - return; - } - if (pathname === "/api/dashboard") { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(api.getDashboard())); - return; - } - if (pathname === "/api/wrappers") { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(api.getWrappers())); - return; - } - if (pathname.startsWith("/api/wrappers/")) { - const id = decodeURIComponent(pathname.split("/").pop() ?? ""); - const wrapper = api.getWrapper(id); - if (!wrapper) { - res.writeHead(404, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ error: "Wrapper not found" })); - return; - } - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(wrapper)); - return; - } - if (pathname === "/api/servers") { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(api.getServers())); - return; - } - if (pathname.startsWith("/api/servers/")) { - const id = decodeURIComponent(pathname.split("/").pop() ?? ""); - const server = api.getServer(id); - if (!server) { - res.writeHead(404, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ error: "Server not found" })); - return; - } - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(server)); - return; - } - res.writeHead(404, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ error: "Not found" })); - }); -} diff --git a/manager/src/web/server.js b/manager/src/web/server.js deleted file mode 100644 index 12d8165..0000000 --- a/manager/src/web/server.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createWebServer = createWebServer; -const http_1 = require("http"); -const routes_1 = require("./routes"); -const api_1 = require("./api"); -function createWebServer(deps, port = 3000) { - const api = new api_1.WebApi(deps); - const server = (0, http_1.createServer)(); - (0, routes_1.registerRoutes)(server, api); - server.listen(port, () => { - // no-op: startup is handled by the manager - }); - return server; -} diff --git a/manager/src/web/types.js b/manager/src/web/types.js deleted file mode 100644 index c8ad2e5..0000000 --- a/manager/src/web/types.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/manager/src/wrapperManager.js b/manager/src/wrapperManager.js deleted file mode 100644 index 40c4ac0..0000000 --- a/manager/src/wrapperManager.js +++ /dev/null @@ -1,167 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WrapperManager = void 0; -const logger_1 = require("./logger"); -const crypto_1 = require("crypto"); -const logger = (0, logger_1.getLogger)("WrapperManager"); -class WrapperManager { - constructor(rabbit, registry, serverRegistry) { - this.rabbit = rabbit; - this.registry = registry; - this.serverRegistry = serverRegistry; - } - async start() { - const queue = "wrapper.register"; - await this.rabbit.channel.assertQueue(queue); - logger.info("Warte auf Wrapper..."); - this.rabbit.channel.consume(queue, async (msg) => { - if (!msg) - return; - const data = JSON.parse(msg.content.toString()); - switch (data.type) { - case "wrapper.register": - await this.register(data); - break; - case "wrapper.heartbeat": - this.heartbeat(data); - break; - case "wrapper.status": - logger.debug("Status update received from wrapper-id", data.wrapperId); - this.registry.updateStatus(data); - this.serverRegistry.syncWrapperStatus(data); - break; - default: - logger.warn("Unbekannte Nachricht:", data); - } - this.rabbit.channel.ack(msg); - }); - // Offline Check - setInterval(() => { - this.registry.checkOffline(30000); - }, 5000); - } - async register(data) { - let id = data.wrapperId; - /* - Neue Wrapper bekommen eine ID - */ - if (!id) { - id = - "wrp-" + - (0, crypto_1.randomUUID)() - .slice(0, 8); - logger.debug("Neue Wrapper ID:", id); - } - const existing = this.registry.get(id); - /* - Wrapper war schon bekannt - */ - if (existing) { - existing.online = true; - existing.lastHeartbeat = - Date.now(); - existing.hostname = - data.hostname; - existing.cpu = - data.cpu; - existing.ram = - data.ram; - existing.ramFree = - data.ram; - // servers are updated via wrapper.status, not wrapper.register - this.registry.update(existing); - } - /* - Neuer Wrapper - */ - else { - const commandQueue = `wrapper.${id}.commands`; - await this.rabbit.channel.assertQueue(commandQueue); - const wrapper = { - id, - hostname: data.hostname, - commandQueue, - cpu: data.cpu, - ram: data.ram, - ramFree: data.ram, - servers: [], - online: true, - lastHeartbeat: Date.now() - }; - this.registry.add(wrapper); - } - /* - Antwort zurück an Wrapper - */ - const wrapper = this.registry.get(id); - if (data.replyQueue) { - this.rabbit.channel.sendToQueue(data.replyQueue, Buffer.from(JSON.stringify({ - type: "wrapper.registered", - wrapperId: id, - commandQueue: wrapper?.commandQueue - }))); - } - } - heartbeat(data) { - this.registry.updateHeartbeat(data.wrapperId); - logger.debug("Heartbeat:", data.wrapperId); - } - sendCommand(wrapperId, command) { - const wrapper = this.registry.get(wrapperId); - if (!wrapper) { - throw new Error("Wrapper nicht gefunden"); - } - this.rabbit.channel.sendToQueue(wrapper.commandQueue, Buffer.from(JSON.stringify(command))); - } - getWrappers() { - return this.registry.getAll(); - } - listWrappers() { - const wrappers = this.registry.getAll(); - logger.info("Wrapper Übersicht ausgegeben"); - if (wrappers.length === 0) { - logger.info("Keine Wrapper registriert"); - return; - } - for (const wrapper of wrappers) { - logger.info(` - -ID: -${wrapper.id} - - -Host: -${wrapper.hostname} - - -CPU: -${wrapper.cpu} Cores - - -RAM: -${wrapper.ram} MB - - -RAM frei: -${wrapper.ramFree} MB - - -Server: -${wrapper.servers.length} - - -Online: -${wrapper.online} - - -Heartbeat: -${new Date(wrapper.lastHeartbeat).toLocaleString()} - - ----------------------- - -`); - } - } -} -exports.WrapperManager = WrapperManager; diff --git a/manager/src/wrapperRegistry.js b/manager/src/wrapperRegistry.js deleted file mode 100644 index fe7ad0e..0000000 --- a/manager/src/wrapperRegistry.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WrapperRegistry = void 0; -const logger_1 = require("./logger"); -const jsonFileStore_1 = require("./persistence/jsonFileStore"); -const logger = (0, logger_1.getLogger)("WrapperRegistry"); -class WrapperRegistry { - constructor() { - this.wrappers = new Map(); - this.persistence = new jsonFileStore_1.JsonFileStore("wrapperRegistry-state.json"); - } - async init() { - const state = await this.persistence.load(); - if (!state) - return; - for (const wrapperData of state.wrappers) { - this.wrappers.set(wrapperData.id, { - id: wrapperData.id, - hostname: wrapperData.hostname, - commandQueue: wrapperData.commandQueue, - cpu: wrapperData.cpu, - ram: wrapperData.ram, - ramFree: wrapperData.ram, - online: false, - lastHeartbeat: 0, - servers: [] - }); - } - } - toState() { - return { - wrappers: Array.from(this.wrappers.values()).map(wrapper => ({ - id: wrapper.id, - hostname: wrapper.hostname, - commandQueue: wrapper.commandQueue, - cpu: wrapper.cpu, - ram: wrapper.ram - })) - }; - } - async saveState() { - await this.persistence.save(this.toState()); - } - add(wrapper) { - this.wrappers.set(wrapper.id, wrapper); - logger.info("Wrapper added:", wrapper.id); - void this.saveState(); - } - get(id) { - return this.wrappers.get(id); - } - getAll() { - return Array.from(this.wrappers.values()); - } - updateHeartbeat(id) { - const wrapper = this.wrappers.get(id); - if (!wrapper) - return; - wrapper.lastHeartbeat = - Date.now(); - wrapper.online = - true; - } - update(wrapper) { - this.wrappers.set(wrapper.id, wrapper); - void this.saveState(); - } - checkOffline(timeout) { - const now = Date.now(); - for (const wrapper of this.wrappers.values()) { - if (wrapper.online && - now - wrapper.lastHeartbeat > timeout) { - wrapper.online = false; - logger.warn("Wrapper offline:", wrapper.id); - } - } - } - updateStatus(data) { - const wrapper = this.wrappers.get(data.wrapperId); - if (!wrapper) - return; - wrapper.ramFree = - data.ramFree; - wrapper.servers = - data.servers; - wrapper.lastHeartbeat = - Date.now(); - wrapper.online = - true; - } -} -exports.WrapperRegistry = WrapperRegistry; diff --git a/manager/tsconfig.json b/manager/tsconfig.json index 511552f..cc1be15 100644 --- a/manager/tsconfig.json +++ b/manager/tsconfig.json @@ -8,6 +8,15 @@ "skipLibCheck": true, "types": [ "node" - ] - } + ], + "outDir": "./dist", + "rootDir": "./src" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] } \ No newline at end of file diff --git a/shared/package.json b/shared/package.json index 2f899d1..9f01b63 100644 --- a/shared/package.json +++ b/shared/package.json @@ -4,10 +4,15 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "build": "tsc -p tsconfig.json" + "build": "tsc -p tsconfig.json", + "clean": "rimraf dist" }, + "files": [ + "dist" + ], "devDependencies": { "@types/node": "^26.1.1", - "typescript": "^5.7.3" + "typescript": "^5.7.3", + "rimraf": "^6.0.1" } -} +} \ No newline at end of file diff --git a/shared/tsconfig.json b/shared/tsconfig.json index 957fd2e..f4d861f 100644 --- a/shared/tsconfig.json +++ b/shared/tsconfig.json @@ -4,8 +4,8 @@ "module": "CommonJS", "declaration": true, "declarationMap": true, - "outDir": "dist", - "rootDir": "src", + "outDir": "./dist", + "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, @@ -14,6 +14,10 @@ ] }, "include": [ - "src" + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" ] } \ No newline at end of file diff --git a/wrapper/package.json b/wrapper/package.json index c2737c5..2b4d040 100644 --- a/wrapper/package.json +++ b/wrapper/package.json @@ -1,11 +1,12 @@ { "name": "wrapper", "version": "1.0.0", - "description": "", - "main": "index.js", + "description": "Server Manager Wrapper", + "main": "dist/index.js", "scripts": { - "start": "ts-node src/index.ts", - "build": "tsc -p tsconfig.json" + "dev": "ts-node src/index.ts", + "build": "tsc -p tsconfig.json", + "start": "node dist/index.js" }, "keywords": [], "author": "", @@ -21,4 +22,4 @@ "ts-node": "^10.9.2", "typescript": "^5.7.3" } -} +} \ No newline at end of file diff --git a/wrapper/src/config.js b/wrapper/src/config.js deleted file mode 100644 index f651a69..0000000 --- a/wrapper/src/config.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SERVER_WORKING_DIRECTORY = exports.SERVER_SCRIPT_PATH = exports.WRAPPER_QUERY_INTERVAL = exports.WRAPPER_STATUS_INTERVAL = exports.WRAPPER_HEARTBEAT_INTERVAL = exports.RABBITMQ_URL = void 0; -exports.loadConfig = loadConfig; -exports.saveConfig = saveConfig; -const fs_1 = __importDefault(require("fs")); -const FILE = "./config.json"; -exports.RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74"; -exports.WRAPPER_HEARTBEAT_INTERVAL = 10000; -exports.WRAPPER_STATUS_INTERVAL = 10000; -exports.WRAPPER_QUERY_INTERVAL = 5000; -exports.SERVER_SCRIPT_PATH = "/home/heroes/HeroesOfValorServer.sh"; -exports.SERVER_WORKING_DIRECTORY = "/home/heroes"; -function loadConfig() { - if (!fs_1.default.existsSync(FILE)) { - return {}; - } - return JSON.parse(fs_1.default.readFileSync(FILE, "utf-8")); -} -function saveConfig(config) { - fs_1.default.writeFileSync(FILE, JSON.stringify(config, null, 4)); -} diff --git a/wrapper/src/index.js b/wrapper/src/index.js deleted file mode 100644 index 0d4a5a7..0000000 --- a/wrapper/src/index.js +++ /dev/null @@ -1,155 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const amqp = __importStar(require("amqplib")); -const os_1 = __importDefault(require("os")); -const config_1 = require("./config"); -const processManager_1 = require("./processManager"); -const statusReporter_1 = require("./statusReporter"); -const queryManager_1 = require("./queryManager"); -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("Wrapper"); -class Wrapper { - constructor() { - this.processManager = new processManager_1.ProcessManager(); - this.queryManager = new queryManager_1.QueryManager(this.processManager); - } - async start() { - const config = (0, config_1.loadConfig)(); - logger.info("Konfiguration geladen"); - if (config.wrapperId) { - this.id = - config.wrapperId; - logger.debug("Starte mit vorhandener ID:", this.id); - } - else { - logger.warn("Keine Wrapper ID vorhanden - neue Registrierung"); - } - this.connection = - await amqp.connect(config_1.RABBITMQ_URL); - this.channel = - await this.connection.createChannel(); - logger.info("RabbitMQ Verbindung hergestellt"); - this.queryManager.start(); - await this.register(); - } - async register() { - const reply = await this.channel.assertQueue("", { - exclusive: true - }); - const replyQueue = reply.queue; - await this.channel.consume(replyQueue, msg => { - if (!msg) - return; - const data = JSON.parse(msg.content.toString()); - logger.debug("Antwort vom Manager:", data.type); - if (data.type === "wrapper.registered") { - this.id = - data.wrapperId; - (0, config_1.saveConfig)({ - wrapperId: this.id - }); - logger.info("Registrierung beim Manager erfolgreich"); - logger.debug("Wrapper ID gespeichert:", this.id); - if (!this.id) { - logger.error("Keine Wrapper ID erhalten!"); - return; - } - this.listenCommands(data.commandQueue); - const reporter = new statusReporter_1.StatusReporter(this.channel, this.processManager, this.queryManager, this.id); - reporter.start(); - } - this.channel.ack(msg); - }); - const message = { - type: "wrapper.register", - // vorhandene ID mitsenden - wrapperId: this.id, - hostname: os_1.default.hostname(), - cpu: os_1.default.cpus().length, - ram: Math.round(os_1.default.totalmem() / 1024 / 1024), - replyQueue - }; - await this.channel.sendToQueue("wrapper.register", Buffer.from(JSON.stringify(message))); - logger.debug("Registrierung gesendet", this.id ?? "ohne ID"); - } - async listenCommands(queue) { - await this.channel.consume(queue, msg => { - if (!msg) - return; - const rawCommand = JSON.parse(msg.content.toString()); - const commandType = rawCommand.type; - switch (commandType) { - case "test": { - const command = rawCommand; - logger.debug("TEST COMMAND:", command.message); - break; - } - case "server.start": { - const command = rawCommand; - this.processManager.startServer(command); - break; - } - case "server.stop": { - const command = rawCommand; - this.processManager.stopServer(command.serverId); - break; - } - default: - logger.warn("Unbekannter Command:", commandType); - } - this.channel.ack(msg); - }); - logger.debug("Warte auf Commands:", queue); - this.heartbeat(); - } - heartbeat() { - logger.debug("Heartbeat gestartet"); - setInterval(() => { - if (!this.id) - return; - const heartbeat = { - type: "wrapper.heartbeat", - wrapperId: this.id - }; - this.channel.sendToQueue("wrapper.register", Buffer.from(JSON.stringify(heartbeat))); - logger.debug("Heartbeat gesendet"); - }, config_1.WRAPPER_HEARTBEAT_INTERVAL); - } -} -new Wrapper().start(); diff --git a/wrapper/src/logger.js b/wrapper/src/logger.js deleted file mode 100644 index c408656..0000000 --- a/wrapper/src/logger.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getLogger = getLogger; -const LEVELS = { - DEBUG: 0, - INFO: 1, - WARN: 2, - ERROR: 3, -}; -const DEFAULT_LOG_LEVEL = "INFO"; -const configuredLevel = (process.env.LOG_LEVEL ?? "").toUpperCase(); -const currentLogLevel = configuredLevel in LEVELS ? configuredLevel : DEFAULT_LOG_LEVEL; -const currentLogLevelValue = LEVELS[currentLogLevel]; -function shouldLog(level) { - return LEVELS[level] >= currentLogLevelValue; -} -function formatMessage(level, component, message) { - return `${new Date().toISOString()} ${level} [${component}] ${message}`; -} -class Logger { - constructor(component) { - this.component = component; - } - debug(message, ...args) { - if (!shouldLog("DEBUG")) - return; - console.log(formatMessage("DEBUG", this.component, message), ...args); - } - info(message, ...args) { - if (!shouldLog("INFO")) - return; - console.log(formatMessage("INFO", this.component, message), ...args); - } - warn(message, ...args) { - if (!shouldLog("WARN")) - return; - console.warn(formatMessage("WARN", this.component, message), ...args); - } - error(message, ...args) { - if (!shouldLog("ERROR")) - return; - console.error(formatMessage("ERROR", this.component, message), ...args); - } -} -function getLogger(component) { - return new Logger(component); -} diff --git a/wrapper/src/processManager.js b/wrapper/src/processManager.js deleted file mode 100644 index 2930c79..0000000 --- a/wrapper/src/processManager.js +++ /dev/null @@ -1,79 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ProcessManager = void 0; -const child_process_1 = require("child_process"); -const config_1 = require("./config"); -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("ProcessManager"); -class ProcessManager { - constructor() { - this.processes = new Map(); - } - startServer(data) { - logger.debug("Starte Game Server:", data.name); - const args = [ - `-Port=${data.port}`, - `-QueryPort=${data.queryPort}`, - `-ServerName=${data.name}` - ]; - const process = (0, child_process_1.spawn)(config_1.SERVER_SCRIPT_PATH, args, { - cwd: config_1.SERVER_WORKING_DIRECTORY, - stdio: "pipe", - detached: true - }); - process.stdout?.on("data", data => { - logger.debug(`[${data.name}]`, data.toString()); - }); - process.stderr?.on("data", data => { - logger.error("SERVER ERROR:", data.toString()); - }); - process.on("exit", code => { - const server = this.processes.get(data.serverId); - if (server) { - logger.info("Server beendet:", server.name, code); - this.processes.delete(data.serverId); - } - }); - this.processes.set(data.serverId, { - id: data.serverId, - name: data.name, - port: data.port, - queryPort: data.queryPort, - process, - started: Date.now(), - alive: true, - players: 0, - maxPlayers: 0 - }); - logger.info("Server gestartet:", { - id: data.serverId, - pid: process.pid - }); - } - stopServer(id) { - const server = this.processes.get(id); - if (!server) { - logger.warn("Server nicht gefunden:", id); - return; - } - logger.debug("Stoppe Server:", server.name); - process.kill(-server.process.pid, "SIGTERM"); - } - getServers() { - return Array.from(this.processes.values()); - } - getStatus() { - return Array.from(this.processes.values()).map(server => ({ - id: server.id, - name: server.name, - port: server.port, - queryPort: server.queryPort, - pid: server.process.pid, - alive: server.alive, - players: server.players, - maxPlayers: server.maxPlayers, - started: server.started - })); - } -} -exports.ProcessManager = ProcessManager; diff --git a/wrapper/src/queryManager.js b/wrapper/src/queryManager.js deleted file mode 100644 index da14b5e..0000000 --- a/wrapper/src/queryManager.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.QueryManager = void 0; -const steam_server_query_1 = require("steam-server-query"); -const config_1 = require("./config"); -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("QueryManager"); -class QueryManager { - constructor(processManager) { - this.processManager = processManager; - } - start() { - logger.debug("Query Manager gestartet"); - setInterval(() => { - this.updateQueries(); - }, config_1.WRAPPER_QUERY_INTERVAL); - } - async updateQueries() { - const servers = this.processManager.getServers(); - for (const server of servers) { - try { - const info = await (0, steam_server_query_1.queryGameServerInfo)(`127.0.0.1:${server.queryPort}`); - server.players = - info.players; - server.maxPlayers = - info.maxPlayers; - logger.debug("Query erfolgreich:", server.name, `${server.players}/${server.maxPlayers}`); - } - catch (err) { - logger.warn("Query fehlgeschlagen:", server.name); - } - } - } - getStatus() { - return this.processManager - .getServers() - .map(server => ({ - id: server.id, - players: server.players, - maxPlayers: server.maxPlayers - })); - } -} -exports.QueryManager = QueryManager; diff --git a/wrapper/src/statusReporter.js b/wrapper/src/statusReporter.js deleted file mode 100644 index f582189..0000000 --- a/wrapper/src/statusReporter.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StatusReporter = void 0; -const os_1 = __importDefault(require("os")); -const config_1 = require("./config"); -const logger_1 = require("./logger"); -const logger = (0, logger_1.getLogger)("StatusReporter"); -class StatusReporter { - constructor(channel, processManager, queryManager, wrapperId) { - this.channel = channel; - this.processManager = processManager; - this.queryManager = queryManager; - this.wrapperId = wrapperId; - } - start() { - logger.debug("Status Reporter gestartet"); - setInterval(() => { - this.sendStatus(); - }, config_1.WRAPPER_STATUS_INTERVAL); - } - sendStatus() { - const processes = this.processManager.getStatus(); - const queries = this.queryManager.getStatus(); - const servers = processes.map(server => { - const query = queries.find((q) => q.id === server.id); - return { - ...server, - players: query?.players ?? 0, - maxPlayers: query?.maxPlayers ?? 0 - }; - }); - const status = { - type: "wrapper.status", - wrapperId: this.wrapperId, - servers, - ramFree: Math.round(os_1.default.freemem() - / - 1024 - / - 1024) - }; - this.channel.sendToQueue("wrapper.register", Buffer.from(JSON.stringify(status))); - logger.debug("Status gesendet:", servers.length, "Server"); - } -} -exports.StatusReporter = StatusReporter; diff --git a/wrapper/tsconfig.json b/wrapper/tsconfig.json index 9621dbb..9e6833e 100644 --- a/wrapper/tsconfig.json +++ b/wrapper/tsconfig.json @@ -5,6 +5,18 @@ "moduleResolution": "node", "esModuleInterop": true, "strict": true, - "skipLibCheck": true - } -} + "skipLibCheck": true, + "outDir": "./dist", + "rootDir": "./src", + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file