server.js aktualisiert

This commit is contained in:
Cap
2026-02-26 12:42:58 +01:00
parent 2a4f66b783
commit 7de4960ca8

106
server.js
View File

@@ -1,19 +1,16 @@
'use strict'; import pkg from 'gamedig';
const http = require('http'); // pkg.query ist direkt verfügbar in gamedig v4
const fs = require('fs'); const GameDig = pkg;
const path = require('path');
// ── GameDig v4+ benötigt destructured import ────────────────────────────────── import http from 'http';
const { GameDig } = require('gamedig'); import fs from 'fs';
// ── Konfiguration ───────────────────────────────────────────────────────────── const CONFIG_PATH = '/home/container/config.json';
const CONFIG_PATH = path.join('/home/container', 'config.json');
const PORT = parseInt(process.env.SERVER_PORT || process.env.PORT || 21337, 10); const PORT = parseInt(process.env.SERVER_PORT || process.env.PORT || 21337, 10);
const QUERY_INTERVAL = Math.max(10, parseInt(process.env.QUERY_INTERVAL || '60', 10)) * 1000; const QUERY_INTERVAL = Math.max(10, parseInt(process.env.QUERY_INTERVAL || '60', 10)) * 1000;
const API_KEY = process.env.API_KEY || ''; const API_KEY = process.env.API_KEY || '';
// ── Config laden ──────────────────────────────────────────────────────────────
let servers = []; let servers = [];
try { try {
const raw = fs.readFileSync(CONFIG_PATH, 'utf8'); const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
@@ -25,118 +22,65 @@ try {
process.exit(1); process.exit(1);
} }
// ── Status-Cache ──────────────────────────────────────────────────────────────
let cache = { let cache = {
updated: null, updated: null,
interval_seconds: QUERY_INTERVAL / 1000, interval_seconds: QUERY_INTERVAL / 1000,
servers: servers.map(s => ({ servers: servers.map(s => ({
label: s.label, label: s.label, type: s.type,
type: s.type, address: `${s.host}:${s.port}`, image: s.image || '',
address: `${s.host}:${s.port}`, status: 'pending', players: 0, maxPlayers: 0, map: '', ping: 0
image: s.image || '',
status: 'pending',
players: 0,
maxPlayers: 0,
map: '',
ping: 0
})) }))
}; };
// ── GameDig Abfrage ───────────────────────────────────────────────────────────
async function queryServer(srv) { async function queryServer(srv) {
try { try {
const opts = { const opts = { type: srv.type, host: srv.host, port: srv.port };
type: srv.type, if (srv.type === 'dayz') opts.port = srv.queryPort || (srv.port + 24714);
host: srv.host, if (srv.queryPort) opts.port = srv.queryPort;
port: srv.port,
};
// DayZ: query port ist game port + 24714
if (srv.type === 'dayz' && !srv.queryPort) {
opts.port = srv.queryPort || (srv.port + 24714);
}
if (srv.queryPort) {
opts.port = srv.queryPort;
}
const state = await GameDig.query(opts); const state = await GameDig.query(opts);
return { return {
status: 'online', status: 'online',
players: state.players ? state.players.length : 0, players: Array.isArray(state.players) ? state.players.length : 0,
maxPlayers: state.maxplayers || 0, maxPlayers: state.maxplayers || 0,
map: state.map || '', map: state.map || '',
ping: state.ping || 0 ping: Math.round(state.ping || 0)
}; };
} catch (err) { } catch (err) {
return { return { status: 'offline', error: String(err.message || err).substring(0, 200), players: 0, maxPlayers: 0, map: '', ping: 0 };
status: 'offline',
error: err.message,
players: 0,
maxPlayers: 0,
map: '',
ping: 0
};
} }
} }
async function runQueries() { async function runQueries() {
console.log(`[QUERY] Starte Abfragen (${servers.length} Server)...`); console.log(`[QUERY] Starte Abfragen (${servers.length} Server)...`);
const start = Date.now(); const start = Date.now();
const results = await Promise.all(servers.map(srv => queryServer(srv))); const results = await Promise.all(servers.map(srv => queryServer(srv)));
results.forEach((res, i) => { results.forEach((res, i) => {
const srv = servers[i]; const srv = servers[i];
cache.servers[i] = { cache.servers[i] = { label: srv.label, type: srv.type, address: `${srv.host}:${srv.port}`, image: srv.image || '', ...res };
label: srv.label,
type: srv.type,
address: `${srv.host}:${srv.port}`,
image: srv.image || '',
...res
};
const icon = res.status === 'online' ? '✓' : '✗'; const icon = res.status === 'online' ? '✓' : '✗';
const info = res.status === 'online' const info = res.status === 'online' ? `${res.players}/${res.maxPlayers} Spieler, Map: ${res.map}, ${res.ping}ms` : `FEHLER: ${res.error}`;
? `${res.players}/${res.maxPlayers} Spieler, ${res.ping}ms`
: res.error || 'offline';
console.log(` ${icon} [${srv.label}] ${res.status} ${info}`); console.log(` ${icon} [${srv.label}] ${res.status} ${info}`);
}); });
cache.updated = new Date().toISOString(); cache.updated = new Date().toISOString();
console.log(`[QUERY] Abgeschlossen in ${Date.now() - start}ms.`); console.log(`[QUERY] Abgeschlossen in ${Date.now() - start}ms.`);
} }
// ── HTTP Server ───────────────────────────────────────────────────────────────
function checkAuth(req) { function checkAuth(req) {
if (!API_KEY) return true; if (!API_KEY) return true;
const headerKey = req.headers['x-api-key']; const headerKey = req.headers['x-api-key'];
const urlKey = new URL(req.url, `http://localhost`).searchParams.get('key'); const url = new URL(req.url, `http://localhost`);
return headerKey === API_KEY || urlKey === API_KEY; return headerKey === API_KEY || url.searchParams.get('key') === API_KEY;
} }
const server = http.createServer((req, res) => { const server = http.createServer((req, res) => {
const url = req.url.split('?')[0]; const url = req.url.split('?')[0];
if (url === '/health') { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({status:'ok',uptime:process.uptime()})); return; }
if (url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
return;
}
if (url === '/api/servers') { if (url === '/api/servers') {
if (!checkAuth(req)) { if (!checkAuth(req)) { res.writeHead(401, {'Content-Type':'application/json'}); res.end(JSON.stringify({error:'Unauthorized'})); return; }
res.writeHead(401, { 'Content-Type': 'application/json' }); res.writeHead(200, {'Content-Type':'application/json','Access-Control-Allow-Origin':'*'});
res.end(JSON.stringify({ error: 'Unauthorized' })); res.end(JSON.stringify(cache, null, 2)); return;
return;
} }
res.writeHead(200, { res.writeHead(404, {'Content-Type':'application/json'}); res.end(JSON.stringify({error:'Not found'}));
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
});
res.end(JSON.stringify(cache, null, 2));
return;
}
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}); });
server.listen(PORT, () => { server.listen(PORT, () => {
@@ -149,8 +93,6 @@ server.listen(PORT, () => {
console.log('║ GET /api/servers ║'); console.log('║ GET /api/servers ║');
console.log('║ GET /health ║'); console.log('║ GET /health ║');
console.log('╚══════════════════════════════════════════╝'); console.log('╚══════════════════════════════════════════╝');
// Erste Abfrage sofort, dann im Intervall
runQueries(); runQueries();
setInterval(runQueries, QUERY_INTERVAL); setInterval(runQueries, QUERY_INTERVAL);
}); });