Files
d4rkbot/tools/panel-abgleich-pruefen.mjs
T
D4rkst3randClaude Opus 5 0982e8cd1b
Deploy / check (push) Canceled after 0s
Deploy / deploy (push) Canceled after 0s
feat: Server aus dem Panel uebernehmen -- und nur die auch wieder loeschen
Im d4rk_gameserver-Panel bekommt jeder Server einen Schalter "Im Discord
anzeigen". Das Panel schreibt dafuer NICHTS hierher: es setzt nur ein
Haekchen, und das reist in dem Statusbericht mit, den der Monitor ohnehin
alle 30 Sekunden abholt. Der Bot legt den Eintrag dann selbst an.

Der umgekehrte Weg -- Panel ruft unsere API -- braeuchte dort ein
Zeichen mit Schreibrecht. So bleibt es bei einem, das nur lesen kann.

ZWEI FALLEN, beide beim Bauen aufgefallen:

Der Abgleich lief zu spaet. In monitorTick stand `if (servers.length ===
0) return` VOR dem Panel-Abruf; beim allerersten Server haette der
Schalter also nichts getan, und zwar stillschweigend. Der Abruf steht
jetzt davor.

Loeschen braucht einen Besitzvermerk. Ohne den nimmt ein Haekchen im
Panel den handgepflegten ATM10 mit -- die einzige verfuegbare Grundlage
waere der Name, und beim ersten Server, der in beiden Werkzeugen gleich
heisst, waere der Eintrag samt Bild, Links und Zugangs-Code weg. Neue
Spalte `panel_name`, leer heisst "von Hand". Die UPDATE-Anweisung fasst
sie nicht an, damit ein Bearbeiten im Webinterface die Herkunft behaelt.

Und der Abgleich ueberschreibt nichts: angelegt und geloescht wird, mehr
nicht.

tools/panel-abgleich-pruefen.mjs prueft das auf einer EIGENEN Datenbank
in /tmp und bricht ab, wenn sie es nicht ist -- eine Probe, die loeschen
kann, darf nicht dort loeschen, wo es zaehlt. Alles gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 19:08:43 +02:00

108 lines
4.9 KiB
JavaScript

// Der Abgleich mit dem Panel -- vor allem, was er NICHT anfassen darf.
//
// Diese Probe braucht better-sqlite3 und läuft deshalb im Container:
//
// docker exec d4rkbot node tools/panel-abgleich-pruefen.mjs
//
// Sie arbeitet auf einer EIGENEN Datenbank in /tmp, nicht auf der echten --
// eine Probe, die löschen kann, darf nicht dort löschen, wo es zählt.
// VOR dem Import setzen: db.js loest den Pfad beim Laden auf.
const PROBE = '/tmp/abgleich-probe.sqlite';
process.env.DB_PATH = PROBE;
import { unlinkSync } from 'node:fs';
for (const e of ['', '-wal', '-shm']) {
try { unlinkSync(PROBE + e); } catch { /* gab es noch nicht */ }
}
const db = await import('../src/db.js');
const { abgleichen, taugt } = await import('../src/panel-abgleich.js');
let fehler = 0;
const zeig = (gut, was, dazu = '') => {
if (!gut) fehler++;
console.log(` ${gut ? 'ok ' : 'FEHL'} ${was}${dazu ? ' ' + dazu : ''}`);
};
// Die echte Datenbank waere hier fatal -- diese Probe LOESCHT Server. Also
// nachsehen, worauf sie wirklich zeigt, statt es zu glauben.
{
const datei = db.db.prepare('PRAGMA database_list').all()[0]?.file ?? '';
if (!datei.includes('abgleich-probe')) {
console.log(`ABBRUCH: laeuft auf ${datei}, nicht auf der Probedatenbank.`);
process.exit(1);
}
console.log(`(Probedatenbank: ${datei})`);
console.log('');
}
const panel = (o) => ({
name: 'p1', discord: true, gamedig: 'minecraft', adresse: '1.2.3.4', port: 26000, ...o,
});
console.log('=== Was überhaupt taugt');
zeig(taugt(panel()) === true, 'vollständiger Eintrag');
zeig(taugt(panel({ discord: false })) === false, 'ohne Häkchen -> nein');
zeig(taugt(panel({ gamedig: null })) === false, 'nicht abfragbares Spiel -> nein (sonst dauerhaft offline)');
zeig(taugt(panel({ adresse: '' })) === false, 'ohne Adresse für Spieler -> nein');
zeig(taugt(panel({ port: null })) === false, 'ohne Port -> nein');
zeig(taugt(undefined) === false, 'gar nichts -> nein');
console.log('');
console.log('=== Anlegen');
let a = abgleichen([panel()]);
zeig(a.angelegt.length === 1, 'ein Server mit Häkchen wird angelegt', a.angelegt.join());
const angelegt = db.listGameservers().find((s) => s.name === 'p1');
zeig(angelegt?.type === 'minecraft', 'mit der gamedig-Kennung, nicht der Vorlagen-Kennung', angelegt?.type);
zeig(angelegt?.host === '1.2.3.4' && Number(angelegt?.port) === 26000, 'Adresse und Port stehen drin');
zeig(angelegt?.address === '1.2.3.4:26000', 'und die Adresse zum Kopieren', angelegt?.address);
zeig(angelegt?.panel_name === 'p1', 'die Herkunft ist vermerkt -- daran hängt alles Weitere');
a = abgleichen([panel()]);
zeig(a.angelegt.length === 0, 'ein zweiter Durchlauf legt ihn NICHT noch einmal an');
console.log('');
console.log('=== Was der Abgleich NICHT anfassen darf');
// Von Hand angelegt: kein panel_name.
const handId = db.createGameserver({
name: 'ATM10', type: 'minecraft', host: '9.9.9.9', port: 25565,
address: '9.9.9.9:25565', image_url: 'https://bild', mods_url: 'https://mods',
links: [{ label: 'Regeln', url: 'https://regeln' }],
});
a = abgleichen([]); // Panel meldet gar nichts mehr
const hand = db.getGameserver(handId);
zeig(Boolean(hand), 'ein von Hand angelegter Server überlebt einen leeren Bericht');
zeig(hand?.image_url === 'https://bild', 'samt Bild');
zeig(hand?.links?.length === 1, 'samt Links');
zeig(a.entfernt.length === 1 && a.entfernt[0].name === 'p1', 'entfernt wird nur, was aus dem Panel kam', a.entfernt.map((e) => e.name).join());
zeig(!db.listGameservers().some((s) => s.name === 'p1'), 'und der ist wirklich weg');
console.log('');
console.log('=== Ein bestehender Eintrag wird nicht verdoppelt');
// Derselbe Server, den es von Hand schon gibt: gleiche Adresse, gleicher Port.
a = abgleichen([panel({ name: 'ATM10 im Panel', adresse: '9.9.9.9', port: 25565 })]);
zeig(a.angelegt.length === 0, 'gleiche Adresse und gleicher Port -> kein zweites Embed im Kanal');
console.log('');
console.log('=== Bearbeiten im Webinterface nimmt die Herkunft nicht');
abgleichen([panel()]);
const p1 = db.listGameservers().find((s) => s.name === 'p1');
db.updateGameserver({ ...p1, image_url: 'https://neu' });
const danach = db.getGameserver(p1.id);
zeig(danach.panel_name === 'p1', 'panel_name steht nach dem Speichern noch da', danach.panel_name);
zeig(danach.image_url === 'https://neu', 'und die Änderung ist drin');
// Und der nächste Abgleich darf sie nicht überschreiben.
abgleichen([panel()]);
zeig(db.getGameserver(p1.id).image_url === 'https://neu', 'der nächste Durchlauf plättet sie nicht');
console.log('');
console.log('=== Kaputte Berichte');
zeig(abgleichen(null).angelegt.length === 0, 'null -> nichts passiert');
zeig(abgleichen([]).entfernt.length >= 0, 'leere Liste -> kein Absturz');
zeig(abgleichen([{}]).angelegt.length === 0, 'Eintrag ohne Felder -> nichts angelegt');
console.log('');
console.log(fehler ? `${fehler} FEHLER` : 'alles grün');
process.exitCode = fehler ? 1 : 0;