From b204da0319c88025aa6758b33b217b9197cab57a Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Tue, 23 Jun 2026 00:21:51 +0200 Subject: [PATCH] dasdsd --- modules/boersenchecker/api/chart_data.php | 236 +++++++++--------- .../boersenchecker/assets/boersenchecker.js | 9 +- 2 files changed, 131 insertions(+), 114 deletions(-) diff --git a/modules/boersenchecker/api/chart_data.php b/modules/boersenchecker/api/chart_data.php index 3f72a5db..614da0fd 100644 --- a/modules/boersenchecker/api/chart_data.php +++ b/modules/boersenchecker/api/chart_data.php @@ -8,132 +8,142 @@ use ModulesCore\ModuleHttp; session_start(); -ModuleHttp::requireDesktopAccess(dirname(__DIR__, 3), true); -require_auth(); - -$user = auth_user() ?? []; -$ownerSub = trim((string) ($user['sub'] ?? 'local')); - -$instrumentId = (int) ($_GET['instrument_id'] ?? 0); -if ($instrumentId <= 0) { - header('Content-Type: application/json; charset=utf-8'); - echo json_encode(['ok' => false, 'message' => 'instrument_id fehlt.'], JSON_UNESCAPED_UNICODE); - exit; -} - -$pdo = module_fn('boersenchecker', 'pdo'); -module_fn('boersenchecker', 'ensure_schema'); -$instrumentTable = module_fn('boersenchecker', 'table', 'instruments'); -$positionTable = module_fn('boersenchecker', 'table', 'positions'); -$quoteTable = module_fn('boersenchecker', 'table', 'quotes'); - -$stmt = $pdo->prepare( - 'SELECT i.id, i.name, i.symbol, i.isin, i.quote_currency - FROM ' . $instrumentTable . ' i - INNER JOIN ' . $positionTable . ' p ON p.instrument_id = i.id - WHERE i.id = :id AND p.owner_sub = :owner_sub - LIMIT 1' -); -$stmt->execute([ - 'id' => $instrumentId, - 'owner_sub' => $ownerSub, -]); -$instrument = $stmt->fetch(PDO::FETCH_ASSOC); - header('Content-Type: application/json; charset=utf-8'); -if (!is_array($instrument)) { - echo json_encode(['ok' => false, 'message' => 'Aktie nicht verfuegbar.'], JSON_UNESCAPED_UNICODE); - exit; -} -$quoteStmt = $pdo->prepare( - 'SELECT id, price, currency, quoted_at, source, created_at - FROM ' . $quoteTable . ' - WHERE instrument_id = :instrument_id - ORDER BY quoted_at ASC, created_at ASC, id ASC' -); -$quoteStmt->execute([ - 'instrument_id' => $instrumentId, -]); -$quotes = $quoteStmt->fetchAll(PDO::FETCH_ASSOC) ?: []; +try { + ModuleHttp::requireDesktopAccess(dirname(__DIR__, 3), true); + require_auth(); -if ($quotes === []) { - echo json_encode([ - 'ok' => false, - 'message' => 'Keine lokalen Kursdaten fuer diese Aktie vorhanden.', - ], JSON_UNESCAPED_UNICODE); - exit; -} + $user = auth_user() ?? []; + $ownerSub = trim((string) ($user['sub'] ?? 'local')); -$dailyMap = []; -foreach ($quotes as $quote) { - $localDate = trim((string) module_fn( - 'boersenchecker', - 'format_datetime_for_display', - (string) ($quote['quoted_at'] ?? ''), - (string) ($quote['source'] ?? ''), - 'Y-m-d' - )); - $localDateTime = trim((string) module_fn( - 'boersenchecker', - 'format_datetime_for_display', - (string) ($quote['quoted_at'] ?? ''), - (string) ($quote['source'] ?? ''), - 'Y-m-d H:i:s' - )); - if ($localDate === '' || !is_numeric($quote['price'] ?? null)) { - continue; + $instrumentId = (int) ($_GET['instrument_id'] ?? 0); + if ($instrumentId <= 0) { + echo json_encode(['ok' => false, 'message' => 'instrument_id fehlt.'], JSON_UNESCAPED_UNICODE); + exit; } - $point = [ - 'date' => $localDate, - 'close' => (float) $quote['price'], - 'currency' => strtoupper(trim((string) ($quote['currency'] ?? ''))), - 'quoted_at' => $localDateTime, - 'source' => (string) ($quote['source'] ?? ''), - ]; + $pdo = module_fn('boersenchecker', 'pdo'); + module_fn('boersenchecker', 'ensure_schema'); + $instrumentTable = module_fn('boersenchecker', 'table', 'instruments'); + $positionTable = module_fn('boersenchecker', 'table', 'positions'); + $quoteTable = module_fn('boersenchecker', 'table', 'quotes'); - if (!isset($dailyMap[$localDate]) || strcmp($localDateTime, (string) ($dailyMap[$localDate]['quoted_at'] ?? '')) >= 0) { - $dailyMap[$localDate] = $point; + $stmt = $pdo->prepare( + 'SELECT i.id, i.name, i.symbol, i.isin, i.quote_currency + FROM ' . $instrumentTable . ' i + INNER JOIN ' . $positionTable . ' p ON p.instrument_id = i.id + WHERE i.id = :id AND p.owner_sub = :owner_sub + LIMIT 1' + ); + $stmt->execute([ + 'id' => $instrumentId, + 'owner_sub' => $ownerSub, + ]); + $instrument = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!is_array($instrument)) { + echo json_encode(['ok' => false, 'message' => 'Aktie nicht verfuegbar.'], JSON_UNESCAPED_UNICODE); + exit; } -} -$daily = array_values($dailyMap); -usort($daily, static fn (array $left, array $right): int => strcmp((string) $left['date'], (string) $right['date'])); + $quoteStmt = $pdo->prepare( + 'SELECT id, price, currency, quoted_at, source, created_at + FROM ' . $quoteTable . ' + WHERE instrument_id = :instrument_id + ORDER BY quoted_at ASC, created_at ASC, id ASC' + ); + $quoteStmt->execute([ + 'instrument_id' => $instrumentId, + ]); + $quotes = $quoteStmt->fetchAll(PDO::FETCH_ASSOC) ?: []; -if ($daily === []) { - echo json_encode([ - 'ok' => false, - 'message' => 'Keine gueltigen lokalen Schlusskurse fuer diese Aktie vorhanden.', - ], JSON_UNESCAPED_UNICODE); - exit; -} + if ($quotes === []) { + echo json_encode([ + 'ok' => false, + 'message' => 'Keine lokalen Kursdaten fuer diese Aktie vorhanden.', + ], JSON_UNESCAPED_UNICODE); + exit; + } -$aggregate = static function (array $points, string $format): array { - $result = []; - $timezone = new DateTimeZone(nexus_display_timezone_name()); - foreach ($points as $point) { - $date = DateTimeImmutable::createFromFormat('Y-m-d', (string) ($point['date'] ?? ''), $timezone); - if (!$date instanceof DateTimeImmutable) { + $dailyMap = []; + foreach ($quotes as $quote) { + $localDate = trim((string) module_fn( + 'boersenchecker', + 'format_datetime_for_display', + (string) ($quote['quoted_at'] ?? ''), + (string) ($quote['source'] ?? ''), + 'Y-m-d' + )); + $localDateTime = trim((string) module_fn( + 'boersenchecker', + 'format_datetime_for_display', + (string) ($quote['quoted_at'] ?? ''), + (string) ($quote['source'] ?? ''), + 'Y-m-d H:i:s' + )); + if ($localDate === '' || !is_numeric($quote['price'] ?? null)) { continue; } - $bucket = $date->format($format); - $result[$bucket] = $point; + + $point = [ + 'date' => $localDate, + 'close' => (float) $quote['price'], + 'currency' => strtoupper(trim((string) ($quote['currency'] ?? ''))), + 'quoted_at' => $localDateTime, + 'source' => (string) ($quote['source'] ?? ''), + ]; + + if (!isset($dailyMap[$localDate]) || strcmp($localDateTime, (string) ($dailyMap[$localDate]['quoted_at'] ?? '')) >= 0) { + $dailyMap[$localDate] = $point; + } } - return array_values($result); -}; + $daily = array_values($dailyMap); + usort($daily, static fn (array $left, array $right): int => strcmp((string) $left['date'], (string) $right['date'])); -echo json_encode([ - 'ok' => true, - 'symbol' => strtoupper(trim((string) ($instrument['symbol'] ?? ''))), - 'isin' => strtoupper(trim((string) ($instrument['isin'] ?? ''))), - 'instrument_name' => (string) ($instrument['name'] ?? ''), - 'currency' => strtoupper(trim((string) ($instrument['quote_currency'] ?? ''))), - 'daily' => $daily, - 'weekly' => $aggregate($daily, 'o-W'), - 'monthly' => $aggregate($daily, 'Y-m'), - 'source' => 'database:quotes', - 'source_label' => 'Lokale Kurshistorie', -], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); -exit; + if ($daily === []) { + echo json_encode([ + 'ok' => false, + 'message' => 'Keine gueltigen lokalen Schlusskurse fuer diese Aktie vorhanden.', + ], JSON_UNESCAPED_UNICODE); + exit; + } + + $aggregate = static function (array $points, string $format): array { + $result = []; + $timezone = new DateTimeZone(nexus_display_timezone_name()); + foreach ($points as $point) { + $date = DateTimeImmutable::createFromFormat('Y-m-d', (string) ($point['date'] ?? ''), $timezone); + if (!$date instanceof DateTimeImmutable) { + continue; + } + $bucket = $date->format($format); + $result[$bucket] = $point; + } + + return array_values($result); + }; + + echo json_encode([ + 'ok' => true, + 'symbol' => strtoupper(trim((string) ($instrument['symbol'] ?? ''))), + 'isin' => strtoupper(trim((string) ($instrument['isin'] ?? ''))), + 'instrument_name' => (string) ($instrument['name'] ?? ''), + 'currency' => strtoupper(trim((string) ($instrument['quote_currency'] ?? ''))), + 'daily' => $daily, + 'weekly' => $aggregate($daily, 'o-W'), + 'monthly' => $aggregate($daily, 'Y-m'), + 'source' => 'database:quotes', + 'source_label' => 'Lokale Kurshistorie', + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + exit; +} catch (\Throwable $e) { + http_response_code(500); + echo json_encode([ + 'ok' => false, + 'message' => 'Chartdaten konnten nicht geladen werden.', + 'error' => $e->getMessage(), + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + exit; +} diff --git a/modules/boersenchecker/assets/boersenchecker.js b/modules/boersenchecker/assets/boersenchecker.js index 2e57edaf..2898b451 100644 --- a/modules/boersenchecker/assets/boersenchecker.js +++ b/modules/boersenchecker/assets/boersenchecker.js @@ -119,7 +119,14 @@ credentials: 'same-origin', headers: { Accept: 'application/json' } }); - const payload = await response.json(); + const responseText = await response.text(); + let payload = null; + try { + payload = JSON.parse(responseText); + } catch (_error) { + const preview = responseText.trim().slice(0, 160); + throw new Error(preview !== '' ? `Chart-API liefert kein JSON: ${preview}` : 'Chart-API liefert kein JSON.'); + } if (!payload.ok) { throw new Error(payload.message || 'Chartdaten konnten nicht geladen werden.'); }