diff --git a/intern/statistik/index.php b/intern/statistik/index.php index 65ff087..05b1622 100644 --- a/intern/statistik/index.php +++ b/intern/statistik/index.php @@ -37,6 +37,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username'], $_POST['p if ($userOk && $passwordOk) { session_regenerate_id(true); $_SESSION['authenticated'] = true; + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); header('Location: ./'); exit; } @@ -94,6 +95,51 @@ if (empty($_SESSION['authenticated'])): exit; endif; +if (empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); +$promoFlash = $_SESSION['promo_flash'] ?? null; +unset($_SESSION['promo_flash']); +$promoError = ''; +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['promo_action'])) { + $providedCsrf = (string) ($_POST['csrf_token'] ?? ''); + if (!hash_equals((string) $_SESSION['csrf_token'], $providedCsrf)) { + $promoError = 'Die Sitzung ist abgelaufen. Bitte lade die Seite neu und versuche es erneut.'; + } else { + $reward = (string) ($_POST['reward'] ?? ''); + $maxUses = filter_var($_POST['max_uses'] ?? '1', FILTER_VALIDATE_INT); + $note = trim((string) ($_POST['note'] ?? '')); + $noteLength = function_exists('mb_strlen') ? mb_strlen($note, 'UTF-8') : strlen($note); + $httpKey = trim((string) ($config['nakama_http_key'] ?? '')); + $rpcBase = preg_replace('~/private_stats$~', '/create_unlock_code', (string) ($config['nakama_rpc_url'] ?? '')); + if (!in_array($reward, ['all_towers', 'all_maps', 'all'], true)) { + $promoError = 'Bitte wähle einen gültigen Freischaltumfang.'; + } elseif ($maxUses === false || $maxUses < 1 || $maxUses > 50000) { + $promoError = 'Die Anzahl der Einlösungen muss zwischen 1 und 50.000 liegen.'; + } elseif ($noteLength > 160) { + $promoError = 'Die Bemerkung darf höchstens 160 Zeichen lang sein.'; + } elseif ($httpKey === '' || !is_string($rpcBase) || $rpcBase === (string) ($config['nakama_rpc_url'] ?? '')) { + $promoError = 'Die interne Codeverwaltung ist noch nicht mit Nakama verbunden.'; + } else { + $url = $rpcBase . '?http_key=' . rawurlencode($httpKey) . '&unwrap'; + $response = fetchJson($url, 'POST', json_encode(['reward' => $reward, 'max_uses' => $maxUses, 'note' => $note], JSON_THROW_ON_ERROR)); + if (is_array($response) && isset($response['payload']) && is_string($response['payload'])) { + $decodedPayload = json_decode($response['payload'], true); + if (is_array($decodedPayload)) $response = $decodedPayload; + } + if (is_array($response) && !empty($response['accepted']) && !empty($response['code'])) { + $_SESSION['promo_flash'] = [ + 'code' => (string) $response['code'], + 'reward' => (string) ($response['reward'] ?? $reward), + 'max_uses' => (int) ($response['max_uses'] ?? $maxUses), + 'note' => (string) ($response['note'] ?? $note), + ]; + header('Location: ./'); + exit; + } + $promoError = 'Der Server konnte keinen Code erstellen. Prüfe die Verbindung und versuche es erneut.'; + } + } +} + $releases = fetchJson((string) $config['gitea_releases_api']) ?? []; $downloadTotal = 0; $latestDownloads = 0; @@ -131,7 +177,48 @@ $players = (int) ($playerStats['total_players'] ?? 0); $today = gmdate('Y-m-d'); $activeToday = (int) (($playerStats['daily_active'] ?? [])[$today] ?? 0); $updatedAt = isset($playerStats['updated_at']) ? date('d.m.Y H:i', intdiv((int) $playerStats['updated_at'], 1000)) : 'Noch keine Daten'; + +$promoCodes = null; +$promoListError = ''; +if ($httpKey !== '') { + $listRpcUrl = preg_replace('~/private_stats$~', '/list_unlock_codes', (string) ($config['nakama_rpc_url'] ?? '')); + if (is_string($listRpcUrl) && $listRpcUrl !== (string) ($config['nakama_rpc_url'] ?? '')) { + $listUrl = $listRpcUrl . '?http_key=' . rawurlencode($httpKey) . '&unwrap'; + $listResponse = fetchJson($listUrl, 'POST', '{}'); + if (is_array($listResponse) && isset($listResponse['payload']) && is_string($listResponse['payload'])) { + $decodedPayload = json_decode($listResponse['payload'], true); + if (is_array($decodedPayload)) $listResponse = $decodedPayload; + } + if (is_array($listResponse) && isset($listResponse['codes']) && is_array($listResponse['codes'])) { + $promoCodes = $listResponse['codes']; + } else { + $promoListError = 'Die Code-Übersicht konnte gerade nicht geladen werden.'; + } + } else { + $promoListError = 'Die interne Codeverwaltung ist noch nicht mit Nakama verbunden.'; + } +} else { + $promoListError = 'Die interne Codeverwaltung ist noch nicht mit Nakama verbunden.'; +} ?> -Void Bastion – Interne Statistik

GERGAMES · NICHT ÖFFENTLICH

Void Bastion Statistik

Abmelden
Downloads insgesamt
Downloads aktuelles Release
Registrierte Piloten
Heute aktive Piloten
Windows-Downloads
Android-Downloads

Die Downloadwerte stammen direkt aus den Gitea-Release-Anhängen und werden alle fünf Minuten neu geladen.

Die private Nakama-Statistik ist noch nicht mit dem HTTP-Schlüssel verbunden.

Letzte Spieleraktualisierung:

Diese Seite ist nicht verlinkt, von Suchmaschinen ausgeschlossen und verlangt eine Anmeldung. Es werden ausschließlich zusammengefasste Werte dargestellt.

+ +Void Bastion – Interne Statistik

GERGAMES · NICHT ÖFFENTLICH

Void Bastion Statistik

Abmelden
+
Downloads insgesamt
Downloads aktuelles Release
Registrierte Piloten
Heute aktive Piloten
Windows-Downloads
Android-Downloads
+

Die Downloadwerte stammen direkt aus den Gitea-Release-Anhängen und werden alle fünf Minuten neu geladen.

Die private Nakama-Statistik ist noch nicht mit dem HTTP-Schlüssel verbunden.

Letzte Spieleraktualisierung:

Diese Seite ist nicht verlinkt, von Suchmaschinen ausgeschlossen und verlangt eine Anmeldung. Es werden ausschließlich zusammengefasste Werte dargestellt.

+

Freischaltcodes erstellen

Codes und Bemerkungen werden ausschließlich in der geschützten Serverablage verwaltet. Der Klartext ist nur in diesem angemeldeten Bereich sichtbar.

+
Code erstellt: · offen · maximal Einlösungen ·
+

+
+

Code-Übersicht

Noch keine Freischaltcodes vorhanden.

= $maxUses ? 'redeemed' : ($uses > 0 ? 'partial' : 'open'); +$statusLabel = $status === 'redeemed' ? 'Eingelöst' : ($status === 'partial' ? 'Teilweise eingelöst' : 'Offen'); +$createdAt = (int) ($promoCode['created_at'] ?? 0); +$createdLabel = $createdAt > 0 ? date('d.m.Y H:i', intdiv($createdAt, 1000)) : '—'; +$codeLabel = (string) ($promoCode['code'] ?? ''); +if ($codeLabel === '') $codeLabel = 'Alter Code – Klartext nicht gespeichert'; +?>
CodeFreischaltungStatusEinlösungenErstelltBemerkung
/
+