Compare commits

..

4 Commits

Author SHA1 Message Date
xziino
e40bdbea1c config: auth_start_href() in index.php definiert (fehlende Funktion nach Merge)
Commit 8217f68 rief auth_start_href() in report-form.php und login.php
auf, ohne die Funktion zu definieren. Generiert auth/start.php?return=
mit dem aktuellen Request-URI als Return-Pfad.
In index.php statt config.php da config.php skip-worktree hat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 08:43:30 +02:00
Akurosia Kamo
4345fadc1c Merge remote-tracking branch 'origin/main' into akus_schabernack4 2026-05-24 08:22:40 +02:00
Akurosia Kamo
28c045fee7 add caching of logs 2026-05-24 08:22:27 +02:00
Akurosia Kamo
8217f68a0f fix login to keep url (again) 2026-05-24 08:02:32 +02:00
12 changed files with 139 additions and 19 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
.claude/
debug/
cached_logs/
fflogs-schema.json
config.php

View File

@ -1,13 +1,12 @@
<?php
ini_set('display_errors', '0');
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/cache.php';
session_start_safe();
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['error' => 'Method not allowed']); exit; }
if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; }
if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; }
$reportCode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['report_code'] ?? '');
$fightId = (int)($_POST['fight_id'] ?? 0);
@ -19,6 +18,16 @@ $translate = 'true';
if (!$reportCode || !$fightId || !$endTime) { http_response_code(400); echo json_encode(['error' => 'Missing params']); exit; }
$cacheParts = [$fightId, (int)$startTime, (int)$endTime];
$cached = read_cached_log('abilities', $reportCode, $language, $cacheParts);
if ($cached !== null) {
echo $cached;
exit;
}
if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; }
if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; }
$token = $_SESSION['access_token'];
function localized_graphql_uri(string $language): string {
@ -123,4 +132,12 @@ foreach (array_keys($seenIds) as $id) {
}
usort($abilities, fn($a, $b) => strcmp($a['name'], $b['name']));
echo json_encode(['abilities' => $abilities, 'players' => $players]);
$response = json_encode(['abilities' => $abilities, 'players' => $players]);
if ($response === false) {
http_response_code(500);
echo json_encode(['error' => 'Could not encode abilities response']);
exit;
}
write_cached_log('abilities', $reportCode, $language, $cacheParts, $response);
echo $response;

View File

@ -1,6 +1,7 @@
<?php
ini_set('display_errors', '0');
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/cache.php';
session_start_safe();
header('Content-Type: application/json');
@ -11,15 +12,6 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
if (empty($_SESSION['access_token'])) {
echo json_encode(['reauth' => true]);
exit;
}
if (($_SESSION['token_expires'] ?? 0) <= time()) {
echo json_encode(['reauth' => true]);
exit;
}
$reportCode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['report_code'] ?? '');
$fightId = (int)($_POST['fight_id'] ?? 0);
$startTime = (float)($_POST['start_time'] ?? 0);
@ -34,6 +26,22 @@ if (!$reportCode || !$fightId || !$endTime) {
exit;
}
$cacheParts = [$fightId, (int)$startTime, (int)$endTime];
$cached = read_cached_log('analysis', $reportCode, $language, $cacheParts);
if ($cached !== null) {
echo $cached;
exit;
}
if (empty($_SESSION['access_token'])) {
echo json_encode(['reauth' => true]);
exit;
}
if (($_SESSION['token_expires'] ?? 0) <= time()) {
echo json_encode(['reauth' => true]);
exit;
}
$token = $_SESSION['access_token'];
function localized_graphql_uri(string $language): string {
@ -514,9 +522,17 @@ foreach ($clusters as $group) {
}
usort($aoeEvents, fn($a, $b) => $a['timestamp'] <=> $b['timestamp']);
echo json_encode([
$response = json_encode([
'players' => array_values($players),
'aoe_events' => $aoeEvents,
'fight_start' => (int)$startTime,
'mitigation_names' => $mitigationNames,
]);
if ($response === false) {
http_response_code(500);
echo json_encode(['error' => 'Could not encode analysis response']);
exit;
}
write_cached_log('analysis', $reportCode, $language, $cacheParts, $response);
echo $response;

51
api/cache.php Normal file
View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
const CACHED_LOG_DIR = __DIR__ . '/../cached_logs';
function cache_language(string $language): string {
$language = strtolower(trim($language));
return in_array($language, ['en', 'de', 'fr', 'jp'], true) ? $language : 'en';
}
function cache_report_code(string $reportCode): string {
return preg_replace('/[^a-zA-Z0-9]/', '', $reportCode);
}
function cache_key_part(string|int|float|null $value): string {
$value = (string)($value ?? '');
$value = preg_replace('/[^a-zA-Z0-9._-]/', '_', $value);
return trim($value, '_') ?: 'all';
}
function cached_log_path(string $kind, string $reportCode, string $language, array $parts = []): string {
$reportCode = cache_report_code($reportCode);
$language = cache_language($language);
$kind = cache_key_part($kind);
$safeParts = array_map('cache_key_part', $parts);
$suffix = $safeParts ? '_' . implode('_', $safeParts) : '';
return CACHED_LOG_DIR . '/' . $reportCode . '/' . $language . '/' . $kind . $suffix . '.json';
}
function read_cached_log(string $kind, string $reportCode, string $language, array $parts = []): ?string {
$path = cached_log_path($kind, $reportCode, $language, $parts);
if (!is_file($path)) return null;
$body = file_get_contents($path);
if ($body === false || trim($body) === '') return null;
return $body;
}
function write_cached_log(string $kind, string $reportCode, string $language, array $parts, string $body): void {
$decoded = json_decode($body, true);
if (!is_array($decoded) || isset($decoded['error'], $decoded['errors'], $decoded['reauth'])) return;
$path = cached_log_path($kind, $reportCode, $language, $parts);
$dir = dirname($path);
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) return;
$tmp = $path . '.' . bin2hex(random_bytes(4)) . '.tmp';
if (file_put_contents($tmp, $body, LOCK_EX) === false) return;
rename($tmp, $path);
}

View File

@ -1,6 +1,7 @@
<?php
ini_set('display_errors', '0');
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/cache.php';
session_start_safe();
header('Content-Type: application/json');
@ -21,6 +22,12 @@ if (strlen($reportCode) < 1) {
exit;
}
$cached = read_cached_log('fight', $reportCode, $language);
if ($cached !== null) {
echo $cached;
exit;
}
if (empty($_SESSION['access_token'])) {
http_response_code(401);
echo json_encode(['error' => 'Not authenticated', 'reauth' => true]);
@ -91,6 +98,7 @@ curl_setopt_array($ch, [
$body = curl_exec($ch);
$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
http_response_code(502);
@ -112,5 +120,8 @@ if ($httpStatus === 401) {
}
http_response_code($httpStatus === 200 ? 200 : $httpStatus);
if ($httpStatus === 200) {
write_cached_log('fight', $reportCode, $language, [], $body);
}
echo $body;
exit;

View File

@ -1,13 +1,12 @@
<?php
ini_set('display_errors', '0');
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/cache.php';
session_start_safe();
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['error' => 'Method not allowed']); exit; }
if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; }
if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; }
$reportCode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['report_code'] ?? '');
$fightId = (int)($_POST['fight_id'] ?? 0);
@ -20,6 +19,16 @@ if (!$reportCode || !$fightId || !$endTime) {
exit;
}
$cacheParts = [$fightId, (int)$startTime, (int)$endTime];
$cached = read_cached_log('players', $reportCode, 'en', $cacheParts);
if ($cached !== null) {
echo $cached;
exit;
}
if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; }
if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; }
$token = $_SESSION['access_token'];
function pl_gql(string $query): array {
@ -92,4 +101,12 @@ foreach ($result['data']['reportData']['report']['events']['data'] ?? [] as $ev)
}
}
echo json_encode(['players' => array_values($players)]);
$response = json_encode(['players' => array_values($players)]);
if ($response === false) {
http_response_code(500);
echo json_encode(['error' => 'Could not encode players response']);
exit;
}
write_cached_log('players', $reportCode, 'en', $cacheParts, $response);
echo $response;

View File

@ -8,7 +8,7 @@ $state = bin2hex(random_bytes(16));
$_SESSION['pkce_verifier'] = $verifier;
$_SESSION['oauth_state'] = $state;
$_SESSION['oauth_return'] = null;
$_SESSION['oauth_return'] = safe_return_path($_GET['return'] ?? null);
$params = http_build_query([
'response_type' => 'code',

View File

@ -2,6 +2,11 @@
require_once __DIR__ . '/config.php';
session_start_safe();
function auth_start_href(): string {
$return = $_SERVER['REQUEST_URI'] ?? '/';
return 'auth/start.php?return=' . urlencode($return);
}
$authenticated = !empty($_SESSION['access_token'])
&& ($_SESSION['token_expires'] ?? 0) > time();

View File

@ -1302,6 +1302,7 @@ function initTimeline(planId) {
const timestamp = found ? assignmentStartMs(found.mechanic, found.assignment) : 0;
const items = rows
.filter(row => jobCanUseAbility(row.job, block.dataset.ability))
.filter((row, idx, arr) => arr.findIndex(r => r.job === row.job) === idx)
.map(row => ({
label: `${row.job}${row.name ? ` · ${row.name}` : ''}`,
icon: MITIG_ICONS[block.dataset.ability] ?? '',

1
run_Server.bat Normal file
View File

@ -0,0 +1 @@
php -S localhost:8080

View File

@ -21,7 +21,7 @@
<?php endif; ?>
</p>
<a href="auth/start.php" class="btn btn-gold btn-login">
<a href="<?= htmlspecialchars(auth_start_href(), ENT_QUOTES) ?>" class="btn btn-gold btn-login">
<?= $tokenExpired ? 'Reconnect to FFLogs' : 'Connect to FFLogs' ?>
</a>

View File

@ -23,7 +23,7 @@
</select>
</div>
<button class="btn btn-gold" type="submit" style="align-self:flex-end">Fetch</button>
<a class="btn" href="auth/start.php" style="align-self:flex-end;text-decoration:none">Reconnect</a>
<a class="btn" href="<?= htmlspecialchars(auth_start_href(), ENT_QUOTES) ?>" style="align-self:flex-end;text-decoration:none">Reconnect</a>
</div>
</form>
</div>