Files
gameserver-status-api/server.js
2026-02-26 11:17:11 +01:00

156 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const http = require('http');
const fs = require('fs');
const path = require('path');
// ── GameDig v4+ benötigt destructured import ──────────────────────────────────
const { GameDig } = require('gamedig');
// ── Konfiguration ─────────────────────────────────────────────────────────────
const CONFIG_PATH = path.join('/home/container', 'config.json');
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 API_KEY = process.env.API_KEY || '';
// ── Config laden ──────────────────────────────────────────────────────────────
let servers = [];
try {
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
const cfg = JSON.parse(raw);
servers = cfg.servers || [];
console.log(`[INIT] config.json geladen ${servers.length} Server konfiguriert.`);
} catch (e) {
console.error(`[FEHLER] config.json konnte nicht geladen werden: ${e.message}`);
process.exit(1);
}
// ── Status-Cache ──────────────────────────────────────────────────────────────
let cache = {
updated: null,
interval_seconds: QUERY_INTERVAL / 1000,
servers: servers.map(s => ({
label: s.label,
type: s.type,
address: `${s.host}:${s.port}`,
image: s.image || '',
status: 'pending',
players: 0,
maxPlayers: 0,
map: '',
ping: 0
}))
};
// ── GameDig Abfrage ───────────────────────────────────────────────────────────
async function queryServer(srv) {
try {
const opts = {
type: srv.type,
host: srv.host,
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);
return {
status: 'online',
players: state.players ? state.players.length : 0,
maxPlayers: state.maxplayers || 0,
map: state.map || '',
ping: state.ping || 0
};
} catch (err) {
return {
status: 'offline',
error: err.message,
players: 0,
maxPlayers: 0,
map: '',
ping: 0
};
}
}
async function runQueries() {
console.log(`[QUERY] Starte Abfragen (${servers.length} Server)...`);
const start = Date.now();
const results = await Promise.all(servers.map(srv => queryServer(srv)));
results.forEach((res, i) => {
const srv = servers[i];
cache.servers[i] = {
label: srv.label,
type: srv.type,
address: `${srv.host}:${srv.port}`,
image: srv.image || '',
...res
};
const icon = res.status === 'online' ? '✓' : '✗';
const info = res.status === 'online'
? `${res.players}/${res.maxPlayers} Spieler, ${res.ping}ms`
: res.error || 'offline';
console.log(` ${icon} [${srv.label}] ${res.status} ${info}`);
});
cache.updated = new Date().toISOString();
console.log(`[QUERY] Abgeschlossen in ${Date.now() - start}ms.`);
}
// ── HTTP Server ───────────────────────────────────────────────────────────────
function checkAuth(req) {
if (!API_KEY) return true;
const headerKey = req.headers['x-api-key'];
const urlKey = new URL(req.url, `http://localhost`).searchParams.get('key');
return headerKey === API_KEY || urlKey === API_KEY;
}
const server = http.createServer((req, res) => {
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 === '/api/servers') {
if (!checkAuth(req)) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unauthorized' }));
return;
}
res.writeHead(200, {
'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, () => {
console.log('╔══════════════════════════════════════════╗');
console.log('║ GameDig Server Status API ║');
console.log('╠══════════════════════════════════════════╣');
console.log(`║ Port: ${String(PORT).padEnd(29)}`);
console.log(`║ Interval: ${String(QUERY_INTERVAL/1000+'s').padEnd(29)}`);
console.log(`║ API-Key: ${(API_KEY ? 'aktiv' : 'deaktiviert').padEnd(29)}`);
console.log('║ GET /api/servers ║');
console.log('║ GET /health ║');
console.log('╚══════════════════════════════════════════╝');
// Erste Abfrage sofort, dann im Intervall
runQueries();
setInterval(runQueries, QUERY_INTERVAL);
});