Files
minecraft-atm10-stats/web/src/lib/api.ts
T
lucashahmannandClaude Opus 5.5 e66f89f49e 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>
2026-10-13 09:33:25 +02:00

35 lines
1010 B
TypeScript

// 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}`;