Freie Links je Server statt fester Felder
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

connect_url und mods_url waren zwei Spalten fuer zwei Zwecke — und beim
naechsten Zweck waere eine dritte dazugekommen. Jetzt gibt es eine Liste:
Beschriftung plus Adresse, so viele wie noetig. Regeln, TeamSpeak, Shop,
Bewerbung.

Als JSON-Spalte, nicht als eigene Tabelle: es haengt an genau einem Server,
wird immer komplett gelesen und immer komplett geschrieben. Gelesen wird es als
Liste, geschrieben darf es als Liste hereinkommen — better-sqlite3 kann kein
Array binden und wuerde sonst werfen. Kaputtes JSON ergibt eine leere Liste
statt eines Absturzes im Monitor.

Discord nimmt fuenf Knoepfe je Reihe; ab jetzt werden sie umgebrochen, bei
zwei Reihen ist Schluss. Die Grenze steht im Code und nicht im Fehler beim
Senden, den niemand sieht. Zu lange Beschriftungen werden auf 80 Zeichen
gekuerzt, halbe Zeilen fliegen raus statt als leerer Knopf zu erscheinen. Was
nicht http(s) ist — ts3server://, steam:// —, wird wie bisher ein Codeblock im
Embed zum Kopieren.

Dabei umbenannt: das Embed-Feld fuer einen nicht klickbaren Connect-Link hiess
"Connect", der Knopf daneben "Verbinden". Jetzt heisst beides gleich.

Geprueft: Umbruch bei zwoelf Links, Reihenfolge mit Connect und Mods vorn,
halbe Zeilen, ueberlange Beschriftung, nicht klickbare Schemata, fehlendes und
unbrauchbares links-Feld, dazu Speichern, Aendern und kaputtes JSON in der
Datenbank. Panel und /server-Seite im laufenden Frontend angesehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 13:39:10 +02:00
co-authored by Claude Opus 5
parent 5de172f862
commit b69cb6c5e4
6 changed files with 136 additions and 22 deletions
+33 -7
View File
@@ -927,6 +927,10 @@ db.exec(`
// Eigener Link für den Mod-Download. Vorher landete der beim Connect-Link,
// weil es kein anderes Feld gab — und dann führte "Connect" zu einer ZIP.
if (!cols.includes('mods_url')) db.exec(`ALTER TABLE gameservers ADD COLUMN mods_url TEXT NOT NULL DEFAULT ''`);
// Freie Links: Regeln, TeamSpeak, Shop, was auch immer. Als JSON-Liste,
// weil sonst für jeden neuen Zweck eine Spalte dazukäme — und genau so
// sind connect_url und mods_url entstanden.
if (!cols.includes('links')) db.exec(`ALTER TABLE gameservers ADD COLUMN links TEXT NOT NULL DEFAULT '[]'`);
}
// Die Auswahl im Panel bot mal Spiel-IDs an, die es bei gamedig gar nicht gibt.
// Wer eine davon gewählt hat, hatte einen Server, der dauerhaft offline stand,
@@ -940,13 +944,13 @@ db.exec(`
for (const [alt, neu] of Object.entries(echteIds)) um.run(neu, alt);
}
const insertGameserver = db.prepare(`
INSERT INTO gameservers (name, type, query_url, address, host, port, connect_url, image_url, token, mods_url)
VALUES (@name, @type, @query_url, @address, @host, @port, @connect_url, @image_url, @token, @mods_url)
INSERT INTO gameservers (name, type, query_url, address, host, port, connect_url, image_url, token, mods_url, links)
VALUES (@name, @type, @query_url, @address, @host, @port, @connect_url, @image_url, @token, @mods_url, @links)
`);
const updateGameserverStmt = db.prepare(`
UPDATE gameservers SET name = @name, type = @type, query_url = @query_url, address = @address,
host = @host, port = @port, connect_url = @connect_url, image_url = @image_url, token = @token,
mods_url = @mods_url
mods_url = @mods_url, links = @links
WHERE id = @id
`);
const setGameserverMessageStmt = db.prepare('UPDATE gameservers SET message_id = ? WHERE id = ?');
@@ -958,14 +962,36 @@ const deleteGameserverStmt = db.prepare('DELETE FROM gameservers WHERE id = ?');
// auffüllen ist billiger, als das an jeder Aufrufstelle nachzuziehen.
const gameserverFelder = {
name: '', type: 'fivem', query_url: '', address: '', host: '', port: null,
connect_url: '', image_url: '', token: '', mods_url: '',
connect_url: '', image_url: '', token: '', mods_url: '', links: '[]',
};
// Links dürfen als Liste hereinkommen — so gibt getGameserver sie ja auch
// heraus. better-sqlite3 kann kein Array binden und würde sonst werfen.
const vollstaendig = (s) => {
const voll = { ...gameserverFelder, ...s };
if (Array.isArray(voll.links)) voll.links = JSON.stringify(voll.links);
return voll;
};
const vollstaendig = (s) => ({ ...gameserverFelder, ...s });
export const createGameserver = (s) => insertGameserver.run(vollstaendig(s)).lastInsertRowid;
export const updateGameserver = (s) => updateGameserverStmt.run(vollstaendig(s)).changes > 0;
export const setGameserverMessage = (id, messageId) => setGameserverMessageStmt.run(messageId, id);
export const listGameservers = () => listGameserversStmt.all();
export const getGameserver = (id) => getGameserverStmt.get(id) ?? null;
/**
* Links kommen als JSON aus der Spalte und gehen als Liste weiter.
*
* Einmal hier statt an jeder Aufrufstelle: sonst parst jeder selbst, und der
* erste, der es vergisst, mappt über einen String. Kaputtes JSON ergibt eine
* leere Liste — ein Server ohne Links ist besser als ein Absturz im Monitor.
*/
function mitLinks(row) {
if (!row) return null;
let links = [];
try {
const roh = JSON.parse(row.links || '[]');
if (Array.isArray(roh)) links = roh.filter((l) => l?.url);
} catch { /* kaputt gespeichert — dann eben ohne */ }
return { ...row, links };
}
export const listGameservers = () => listGameserversStmt.all().map(mitLinks);
export const getGameserver = (id) => mitLinks(getGameserverStmt.get(id));
export const deleteGameserver = (id) => deleteGameserverStmt.run(id).changes > 0;
// Spielerzahl-Verlauf für die öffentliche /server-Seite (Sample alle 2 min, 7 Tage Retention)