initial import server-manager

This commit is contained in:
2026-07-22 00:12:44 +02:00
parent 8354d94cbc
commit 57d63f5bf4
13 changed files with 172 additions and 60 deletions
+6 -1
View File
@@ -1,7 +1,12 @@
node_modules/ node_modules/
dist/ dist/
build/ build/
.env
*.log *.log
.env
*-state.json
.vscode/ .vscode/
.idea/
-10
View File
@@ -1,10 +0,0 @@
{
"usedPorts": [
7777,
7778
],
"usedQueryPorts": [
27015,
27016
]
}
-20
View File
@@ -1,20 +0,0 @@
{
"servers": [
{
"id": "srv-ba25028b",
"name": "[EU] druecktieR | Crimson",
"wrapperId": "wrp-8adcbbf2",
"port": 7777,
"queryPort": 27015,
"created": 1784578015578
},
{
"id": "srv-d70d5c3a",
"name": "[EU] druecktieR | Azure",
"wrapperId": "wrp-8adcbbf2",
"port": 7778,
"queryPort": 27016,
"created": 1784653753332
}
]
}
+15 -8
View File
@@ -1,14 +1,21 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.QUERY_PORT_START = exports.GAME_PORT_START = exports.DEFAULT_DESIRED_SERVERS = exports.LIFECYCLE_CRASH_TIMEOUT = exports.LIFECYCLE_SCALE_COOLDOWN = exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = exports.LIFECYCLE_MIN_UPTIME = exports.LIFECYCLE_STARTUP_GRACE_PERIOD = exports.LIFECYCLE_EMPTY_TIMEOUT = exports.LIFECYCLE_CHECK_INTERVAL = exports.RABBITMQ_URL = void 0; 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"; exports.RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74";
exports.LIFECYCLE_CHECK_INTERVAL = 10000; // Lifecycle
exports.LIFECYCLE_EMPTY_TIMEOUT = 300000; exports.LIFECYCLE_CHECK_INTERVAL = 10000; // Alle 10 Sekunden prüfen
exports.LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; // Scale Down
exports.LIFECYCLE_MIN_UPTIME = 300000; exports.LIFECYCLE_EMPTY_TIMEOUT = 300000; // 5 Minuten leer
exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; exports.LIFECYCLE_MIN_UPTIME = 600000; // 10 Minuten Mindestlaufzeit
exports.LIFECYCLE_SCALE_COOLDOWN = 60000; exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; // Unter 30% Gesamtauslastung darf abgebaut werden
exports.LIFECYCLE_CRASH_TIMEOUT = 30000; // 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; exports.DEFAULT_DESIRED_SERVERS = 1;
// Ports
exports.GAME_PORT_START = 7777; exports.GAME_PORT_START = 7777;
exports.QUERY_PORT_START = 27015; exports.QUERY_PORT_START = 27015;
+18 -7
View File
@@ -1,13 +1,24 @@
export const RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74"; export const RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74";
export const LIFECYCLE_CHECK_INTERVAL = 10000; // Lifecycle
export const LIFECYCLE_EMPTY_TIMEOUT = 300000; export const LIFECYCLE_CHECK_INTERVAL = 10000; // Alle 10 Sekunden prüfen
export const LIFECYCLE_STARTUP_GRACE_PERIOD = 60000;
export const LIFECYCLE_MIN_UPTIME = 300000; // Scale Down
export const LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; export const LIFECYCLE_EMPTY_TIMEOUT = 300000; // 5 Minuten leer
export const LIFECYCLE_SCALE_COOLDOWN = 60000; export const LIFECYCLE_MIN_UPTIME = 600000; // 10 Minuten Mindestlaufzeit
export const LIFECYCLE_CRASH_TIMEOUT = 30000; export const LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; // Unter 30% Gesamtauslastung darf abgebaut werden
// Scale Up
export const LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; // 1 Minute Schutz nach Start
export const LIFECYCLE_SCALE_COOLDOWN = 120000; // 2 Minuten zwischen Scale-Up Aktionen
// Crash Detection
export const LIFECYCLE_CRASH_TIMEOUT = 120000; // 2 Minuten ohne Status -> Crash
export const LIFECYCLE_CRASH_RETENTION = 300000; // 5 Minuten sichtbar nach Crash
// Default
export const DEFAULT_DESIRED_SERVERS = 1; export const DEFAULT_DESIRED_SERVERS = 1;
// Ports
export const GAME_PORT_START = 7777; export const GAME_PORT_START = 7777;
export const QUERY_PORT_START = 27015; export const QUERY_PORT_START = 27015;
+32 -3
View File
@@ -51,6 +51,19 @@ class ServerLifecycle {
logger.info("Server RUNNING:", server.name, "PID:", server.pid); logger.info("Server RUNNING:", server.name, "PID:", server.pid);
} }
break; 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": case "RUNNING":
/* /*
Crash Detection Crash Detection
@@ -61,6 +74,8 @@ class ServerLifecycle {
config_1.LIFECYCLE_CRASH_TIMEOUT) { config_1.LIFECYCLE_CRASH_TIMEOUT) {
server.status = server.status =
"CRASHED"; "CRASHED";
server.crashedAt =
Date.now();
this.serverRegistry.update(server); this.serverRegistry.update(server);
logger.error("Server abgestürzt:", server.name); logger.error("Server abgestürzt:", server.name);
break; break;
@@ -92,9 +107,22 @@ class ServerLifecycle {
} }
break; break;
case "CRASHED": case "CRASHED":
if (!server.crashedAt) {
server.crashedAt =
Date.now();
this.serverRegistry.update(server);
logger.error("Server abgestürzt erkannt:", server.name); logger.error("Server abgestürzt erkannt:", server.name);
break; 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 AKTIVE SERVER
@@ -102,7 +130,8 @@ class ServerLifecycle {
const activeServers = this.serverRegistry const activeServers = this.serverRegistry
.getAll() .getAll()
.filter(server => server.status === "RUNNING" || .filter(server => server.status === "RUNNING" ||
server.status === "STARTING"); server.status === "STARTING" ||
server.status === "RECOVERING");
/* /*
SLOT AUSLASTUNG SLOT AUSLASTUNG
*/ */
@@ -166,14 +195,14 @@ class ServerLifecycle {
const activeCount = this.serverRegistry const activeCount = this.serverRegistry
.getAll() .getAll()
.filter(s => s.status === "RUNNING" || .filter(s => s.status === "RUNNING" ||
s.status === "STARTING") s.status === "STARTING" ||
s.status === "RECOVERING")
.length; .length;
if (activeCount <= if (activeCount <=
this.desiredServers) { this.desiredServers) {
break; break;
} }
logger.info("Stoppe leeren Server:", server.name); logger.info("Stoppe leeren Server:", server.name);
logger.info("Stoppe leeren Server:", server.name);
this.serverManager.stopServer(server.id); this.serverManager.stopServer(server.id);
} }
} }
+38
View File
@@ -9,6 +9,7 @@ import {
LIFECYCLE_SCALE_DOWN_THRESHOLD, LIFECYCLE_SCALE_DOWN_THRESHOLD,
LIFECYCLE_SCALE_COOLDOWN, LIFECYCLE_SCALE_COOLDOWN,
LIFECYCLE_CRASH_TIMEOUT, LIFECYCLE_CRASH_TIMEOUT,
LIFECYCLE_CRASH_RETENTION,
DEFAULT_DESIRED_SERVERS DEFAULT_DESIRED_SERVERS
} from "./config"; } from "./config";
@@ -178,6 +179,9 @@ export class ServerLifecycle {
server.status = server.status =
"CRASHED"; "CRASHED";
server.crashedAt =
Date.now();
this.serverRegistry.update( this.serverRegistry.update(
server server
@@ -218,6 +222,9 @@ export class ServerLifecycle {
server.status = server.status =
"CRASHED"; "CRASHED";
server.crashedAt =
Date.now();
this.serverRegistry.update( this.serverRegistry.update(
server server
@@ -317,12 +324,43 @@ export class ServerLifecycle {
case "CRASHED": case "CRASHED":
if(!server.crashedAt){
server.crashedAt =
Date.now();
this.serverRegistry.update(
server
);
logger.error( logger.error(
"Server abgestürzt erkannt:", "Server abgestürzt erkannt:",
server.name server.name
); );
break;
}
if(
Date.now() -
server.crashedAt
>
LIFECYCLE_CRASH_RETENTION
){
logger.info(
"Entferne abgestürzten Server nach Grace-Zeit:",
server.name
);
this.serverRegistry.remove(
server.id
);
}
break; break;
+16 -4
View File
@@ -21,7 +21,7 @@ class ServerRegistry {
port: serverData.port, port: serverData.port,
queryPort: serverData.queryPort, queryPort: serverData.queryPort,
created: serverData.created, created: serverData.created,
status: "STARTING", status: "RECOVERING",
players: 0, players: 0,
maxPlayers: 0, maxPlayers: 0,
lastSeen: Date.now() lastSeen: Date.now()
@@ -96,9 +96,14 @@ class ServerRegistry {
logger.debug("Server cleanly stopped:", server.name); logger.debug("Server cleanly stopped:", server.name);
this.remove(server.id); this.remove(server.id);
} }
else if (server.status === "RUNNING") { else if (server.status === "RUNNING" ||
server.status === "RECOVERING") {
server.status = server.status =
"CRASHED"; "CRASHED";
if (!server.crashedAt) {
server.crashedAt =
Date.now();
}
this.servers.set(server.id, server); this.servers.set(server.id, server);
logger.warn("Server crashed:", server.name); logger.warn("Server crashed:", server.name);
} }
@@ -117,7 +122,9 @@ class ServerRegistry {
server.lastSeen = server.lastSeen =
Date.now(); Date.now();
const oldStatus = server.status; const oldStatus = server.status;
if (srv.alive) { if (srv.alive && server.status !== "STOPPING") {
server.crashedAt =
undefined;
if (!server.startedAt) { if (!server.startedAt) {
server.startedAt = server.startedAt =
srv.started; srv.started;
@@ -125,9 +132,14 @@ class ServerRegistry {
server.status = server.status =
"RUNNING"; "RUNNING";
} }
else if (server.status === "RUNNING") { else if (server.status === "RUNNING" ||
server.status === "RECOVERING") {
server.status = server.status =
"CRASHED"; "CRASHED";
if (!server.crashedAt) {
server.crashedAt =
Date.now();
}
} }
if (oldStatus !== server.status) { if (oldStatus !== server.status) {
logger.debug("Status change:", server.name, oldStatus, "->", server.status); logger.debug("Status change:", server.name, oldStatus, "->", server.status);
+19
View File
@@ -42,6 +42,8 @@ export interface ServerInfo {
startedAt?:number; startedAt?:number;
crashedAt?:number;
lastSeen:number; lastSeen:number;
@@ -357,6 +359,13 @@ export class ServerRegistry {
server.status = server.status =
"CRASHED"; "CRASHED";
if(!server.crashedAt){
server.crashedAt =
Date.now();
}
this.servers.set( this.servers.set(
server.id, server.id,
@@ -416,6 +425,9 @@ export class ServerRegistry {
if(srv.alive && server.status !== "STOPPING"){ if(srv.alive && server.status !== "STOPPING"){
server.crashedAt =
undefined;
if(!server.startedAt){ if(!server.startedAt){
server.startedAt = server.startedAt =
@@ -435,6 +447,13 @@ export class ServerRegistry {
server.status = server.status =
"CRASHED"; "CRASHED";
if(!server.crashedAt){
server.crashedAt =
Date.now();
}
} }
+6
View File
@@ -35,6 +35,8 @@ class ServerStatusManager {
return; return;
server.status = server.status =
"RUNNING"; "RUNNING";
server.crashedAt =
undefined;
if (!server.startedAt) { if (!server.startedAt) {
server.startedAt = server.startedAt =
Date.now(); Date.now();
@@ -48,6 +50,10 @@ class ServerStatusManager {
return; return;
server.status = server.status =
"CRASHED"; "CRASHED";
if (!server.crashedAt) {
server.crashedAt =
Date.now();
}
this.registry.update(server); this.registry.update(server);
logger.info("Server stopped:", data.serverId); logger.info("Server stopped:", data.serverId);
} }
+10
View File
@@ -160,6 +160,9 @@ export class ServerStatusManager {
server.status = server.status =
"RUNNING"; "RUNNING";
server.crashedAt =
undefined;
if(!server.startedAt){ if(!server.startedAt){
@@ -221,6 +224,13 @@ export class ServerStatusManager {
server.status = server.status =
"CRASHED"; "CRASHED";
if(!server.crashedAt){
server.crashedAt =
Date.now();
}
+2
View File
@@ -76,6 +76,7 @@ class WebApi {
name: server.name, name: server.name,
status: server.status, status: server.status,
wrapperId: server.wrapperId, wrapperId: server.wrapperId,
pid: server.pid,
port: server.port, port: server.port,
queryPort: server.queryPort, queryPort: server.queryPort,
players: server.players, players: server.players,
@@ -95,6 +96,7 @@ class WebApi {
name: server.name, name: server.name,
status: server.status, status: server.status,
wrapperId: server.wrapperId, wrapperId: server.wrapperId,
pid: server.pid,
port: server.port, port: server.port,
queryPort: server.queryPort, queryPort: server.queryPort,
players: server.players, players: server.players,
+4 -1
View File
@@ -6,5 +6,8 @@
"wrapper", "wrapper",
"shared", "shared",
"dashboard" "dashboard"
] ],
"allowScripts": {
"esbuild@0.21.5": true
}
} }