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,34 @@
|
||||
// Server components call the API directly (container network); the browser uses same-origin /api.
|
||||
const base = typeof window === 'undefined' ? (process.env.API_INTERNAL_URL ?? 'http://127.0.0.1:3001') : '';
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
readonly status: number,
|
||||
path: string,
|
||||
) {
|
||||
super(`API ${status} for ${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiGet<T>(path: string): Promise<T> {
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetch(`${base}${path}`, { cache: 'no-store' });
|
||||
} catch {
|
||||
throw new ApiError(503, path);
|
||||
}
|
||||
if (!res.ok) throw new ApiError(res.status, path);
|
||||
return (await res.json()) as T;
|
||||
}
|
||||
|
||||
/** Like apiGet, but resolves to null for 404 (unknown player, board …). */
|
||||
export async function apiFind<T>(path: string): Promise<T | null> {
|
||||
try {
|
||||
return await apiGet<T>(path);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError && e.status === 404) return null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export const avatarUrl = (uuid: string) => `/api/avatar/${uuid}`;
|
||||
Reference in new issue
Block a user