From 0f4d5a98d46d6dbb5333b0aac3e2049927eff2f6 Mon Sep 17 00:00:00 2001 From: Akurosia Kamo Date: Wed, 27 May 2026 14:16:26 +0200 Subject: [PATCH] fflogs-ids > admin --- .env.example | 7 ++ admin.php | 199 +++++++++++++++++++++++++++++++++++++++++++ auth/callback.php | 1 + config.php | 93 ++++++++++++++++++++ css/layout.css | 19 ++++- templates/topbar.php | 13 ++- 6 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 admin.php diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b932e56 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +DEV_MODE=true +CLIENT_ID= +REDIRECT_URI=http://localhost:8080/auth/callback.php +AUTHORIZE_URI=https://www.fflogs.com/oauth/authorize +TOKEN_URI=https://www.fflogs.com/oauth/token +GRAPHQL_URI=https://www.fflogs.com/api/v2/user +ADMIN_USER_IDS= diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..5376a27 --- /dev/null +++ b/admin.php @@ -0,0 +1,199 @@ +&1'; + exec($cmd, $lines, $code); + return ['code' => $code, 'output' => trim(implode("\n", $lines))]; +} + +function git_output(array $args): string { + return git_run($args)['output']; +} + +function git_local_branches(): array { + $out = git_output(['branch', '--format=%(refname:short)']); + $branches = array_values(array_filter(array_map('trim', explode("\n", $out)), fn($v) => $v !== '')); + sort($branches, SORT_NATURAL | SORT_FLAG_CASE); + return $branches; +} + +$message = null; +$result = null; + +if ($allowed && $_SERVER['REQUEST_METHOD'] === 'POST') { + if (!hash_equals($csrf, $_POST['csrf'] ?? '')) { + http_response_code(400); + $message = 'Invalid CSRF token.'; + } else { + $action = $_POST['action'] ?? ''; + if ($action === 'fetch') { + $result = git_run(['fetch', '--prune']); + $message = 'git fetch --prune'; + } elseif ($action === 'pull') { + $result = git_run(['pull', '--ff-only']); + $message = 'git pull --ff-only'; + } elseif ($action === 'switch') { + $branch = trim((string)($_POST['branch'] ?? '')); + $branches = git_local_branches(); + if (!in_array($branch, $branches, true)) { + $result = ['code' => 1, 'output' => 'Unknown or non-local branch: ' . $branch]; + } else { + $result = git_run(['switch', $branch]); + $message = 'git switch ' . $branch; + } + } + } +} + +$currentBranch = ''; +$status = ''; +$branches = []; +$lastCommit = ''; +if ($allowed) { + $currentBranch = git_output(['rev-parse', '--abbrev-ref', 'HEAD']); + $status = git_output(['status', '-sb']); + $branches = git_local_branches(); + $lastCommit = git_output(['log', '-1', '--pretty=%h %s (%cr)']); +} +?> + + + + + + Admin · FFLogs Report Viewer + + + + + + +
+
+ +
+ Back +
+ +
+
+
+ + +
+
+
User nicht verfügbar
+

Ich konnte deine FFLogs User-ID nicht laden.

+
+
+ +
+
+
Kein Zugriff
+

+ Deine FFLogs User-ID ist . + Füge diese ID in deiner .env zu ADMIN_USER_IDS hinzu, um diese Seite freizuschalten. +

+
+
+ +
+
+
Git Admin
+
+ Branch:
+ Last commit: +
+
+ +
+
+
Aktionen
+
+ + + +
+ +
+ + +
+ + +
+ +
+
+ +
+
Status
+
+
+
+ + +
+
· Exit
+
+
+ +
+ +
+ + diff --git a/auth/callback.php b/auth/callback.php index b96897d..5dd9af0 100644 --- a/auth/callback.php +++ b/auth/callback.php @@ -67,6 +67,7 @@ if ($curlError || $status !== 200 || empty($data['access_token'])) { $_SESSION['access_token'] = $data['access_token']; $_SESSION['token_expires'] = time() + ($data['expires_in'] ?? 3600); +$_SESSION['fflogs_user'] = fetch_current_fflogs_user($data['access_token']); header('Location: ' . $returnPath); exit; diff --git a/config.php b/config.php index 29ee653..8fd386f 100644 --- a/config.php +++ b/config.php @@ -38,6 +38,11 @@ function env_bool(string $key, bool $default = false): bool { return in_array($value, ['1', 'true', 'yes', 'on'], true); } +function env_list(string $key): array { + $value = env_value($key, ''); + return array_values(array_filter(array_map('trim', explode(',', $value)), fn($v) => $v !== '')); +} + load_env_file(__DIR__ . '/.env'); define('DEV_MODE', env_bool('DEV_MODE')); @@ -46,6 +51,7 @@ define('REDIRECT_URI', env_value('REDIRECT_URI')); define('AUTHORIZE_URI', env_value('AUTHORIZE_URI')); define('TOKEN_URI', env_value('TOKEN_URI')); define('GRAPHQL_URI', env_value('GRAPHQL_URI')); +define('ADMIN_USER_IDS', env_list('ADMIN_USER_IDS')); function session_start_safe(): void { if (session_status() === PHP_SESSION_NONE) { @@ -94,3 +100,90 @@ function current_return_path(): string { function auth_start_href(?string $returnPath = null): string { return 'auth/start.php?return=' . rawurlencode($returnPath ?? current_return_path()); } + +function fflogs_graphql_user_query(string $query, string $token): array { + $payload = json_encode(['query' => $query]); + if ($payload === false) { + return ['error' => 'Could not encode GraphQL payload']; + } + + if (function_exists('curl_init')) { + $ch = curl_init(GRAPHQL_URI); + curl_setopt_array($ch, [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $payload, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $token, + ], + CURLOPT_SSL_VERIFYPEER => !DEV_MODE, + ]); + $body = curl_exec($ch); + $err = curl_error($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($err) return ['error' => $err, 'status' => $code]; + return ['body' => $body, 'status' => $code]; + } + + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'content' => $payload, + 'header' => implode("\r\n", [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $token, + ]), + 'timeout' => 30, + ], + 'ssl' => [ + 'verify_peer' => !DEV_MODE, + 'verify_peer_name' => !DEV_MODE, + ], + ]); + $body = file_get_contents(GRAPHQL_URI, false, $context); + if ($body === false) return ['error' => 'GraphQL request failed']; + return ['body' => $body, 'status' => 200]; +} + +function fetch_current_fflogs_user(string $token): ?array { + $result = fflogs_graphql_user_query('query { userData { currentUser { id name } } }', $token); + if (!empty($result['error'])) { + $_SESSION['fflogs_user_error'] = $result['error']; + return null; + } + + $data = json_decode((string)($result['body'] ?? ''), true); + $user = $data['data']['userData']['currentUser'] ?? null; + if (!is_array($user) || empty($user['id'])) { + $_SESSION['fflogs_user_error'] = $data['errors'][0]['message'] ?? 'Could not read current FFLogs user'; + return null; + } + + return [ + 'id' => (string)$user['id'], + 'name' => (string)($user['name'] ?? ''), + ]; +} + +function current_fflogs_user(bool $forceRefresh = false): ?array { + if (!$forceRefresh && !empty($_SESSION['fflogs_user']) && is_array($_SESSION['fflogs_user'])) { + return $_SESSION['fflogs_user']; + } + if (empty($_SESSION['access_token']) || (($_SESSION['token_expires'] ?? 0) <= time())) { + return null; + } + + $user = fetch_current_fflogs_user($_SESSION['access_token']); + if ($user !== null) { + $_SESSION['fflogs_user'] = $user; + unset($_SESSION['fflogs_user_error']); + } + return $user; +} + +function is_admin_user(): bool { + $user = current_fflogs_user(); + return $user !== null && in_array((string)$user['id'], ADMIN_USER_IDS, true); +} diff --git a/css/layout.css b/css/layout.css index 416ca74..866a347 100644 --- a/css/layout.css +++ b/css/layout.css @@ -100,12 +100,29 @@ /* ── Topbar user badge ───────────────────────────────────────────────────────── */ .topbar-user { - margin-left: auto; font-size: 13px; color: var(--t2); white-space: nowrap; } +.topbar-actions { + margin-left: auto; + display: flex; + align-items: center; + gap: 12px; +} + +.topbar-link { + color: var(--gold); + font-size: 13px; + text-decoration: none; + border: 1px solid var(--borderem); + border-radius: var(--r); + padding: 5px 10px; + background: var(--bg2); +} +.topbar-link:hover { background: var(--bg3); } + /* ── Login Overlay ───────────────────────────────────────────────────────────── */ #login-overlay { position: fixed; diff --git a/templates/topbar.php b/templates/topbar.php index f6376c4..b9ecde8 100644 --- a/templates/topbar.php +++ b/templates/topbar.php @@ -5,5 +5,16 @@ -
Token gültig bis:
+ +
+ + Admin + +
+ + # · + + Token gültig bis: +
+