feat: ATM10-Statistikportal
Statistikportal für den All-the-Mods-10-Server (Minecraft 1.21.1, NeoForge 21.1): - mod/: serverseitige NeoForge-Mod atmstats – Ereignisse, Live-Zähler, Servergesundheit, FTB-Dateien; Outbox auf der Festplatte, signierter gzip-Versand per HTTPS mit Backoff - backend/: Fastify + PostgreSQL – idempotenter Ingest, Projektionen, Parser für SNBT, FTB Quests/Teams, world/stats, world/advancements und latest.log; Ranglisten, Snapshots, Retention, öffentliche API, SSE-Live-Updates, Admin mit argon2/CSRF, SFTP-Worker - web/: Next.js-Dashboard – Übersicht, Ranglisten, Spielerprofile, ATM10, Server, Datenquellen, Verwaltung - shared/: Protokoll v1 und API-Typen; tools/: Simulator, Log-Anonymisierer, RCON-Client - deploy/: Docker Compose hinter Traefik, Backup; docs/: Architektur, Datenquellen, Betrieb Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
56d2078e6a
commit
e66f89f49e
214 files changed
+27077
No files matched your search
@@ -0,0 +1,59 @@
|
||||
// Verwaltungs-CLI:
|
||||
// cli admin:create <benutzer> Passwort aus ADMIN_PASSWORD oder interaktiv
|
||||
// cli token:create neues Ingest-Token (wird nur einmal angezeigt)
|
||||
// cli migrate Datenbankmigrationen ausführen
|
||||
import { createInterface } from 'node:readline/promises';
|
||||
import { createAdmin } from './auth/admin.ts';
|
||||
import { createPool } from './db/pool.ts';
|
||||
import { migrate } from './db/migrate.ts';
|
||||
import { createIngestToken } from './ingest/tokens.ts';
|
||||
|
||||
async function readPassword(): Promise<string> {
|
||||
if (process.env.ADMIN_PASSWORD) return process.env.ADMIN_PASSWORD;
|
||||
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
||||
// Hide typed characters.
|
||||
(rl as unknown as { _writeToOutput: (s: string) => void })._writeToOutput = (s: string) => {
|
||||
if (s.includes('Passwort')) process.stdout.write(s);
|
||||
};
|
||||
const pw = await rl.question('Passwort (min. 12 Zeichen): ');
|
||||
rl.close();
|
||||
process.stdout.write('\n');
|
||||
return pw;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [command, arg] = process.argv.slice(2);
|
||||
const url = process.env.DATABASE_URL;
|
||||
if (!url) throw new Error('DATABASE_URL is required');
|
||||
const pool = createPool(url, 2);
|
||||
try {
|
||||
await migrate(pool);
|
||||
switch (command) {
|
||||
case 'migrate':
|
||||
console.log('Migrationen ausgeführt.');
|
||||
break;
|
||||
case 'admin:create': {
|
||||
if (!arg) throw new Error('usage: admin:create <benutzer>');
|
||||
await createAdmin(pool, arg, await readPassword());
|
||||
console.log(`Admin "${arg}" angelegt bzw. Passwort aktualisiert.`);
|
||||
break;
|
||||
}
|
||||
case 'token:create': {
|
||||
const token = await createIngestToken(pool);
|
||||
console.log('Neues Ingest-Token (in config/atmstats-server.toml als token eintragen):');
|
||||
console.log(token);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.log('Befehle: migrate | admin:create <benutzer> | token:create');
|
||||
process.exitCode = 1;
|
||||
}
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error((err as Error).message);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user