From 74b1651eacc1c15d3fc9b869ecbe57e32ce8fadf Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Wed, 29 Apr 2026 00:53:38 +0200 Subject: [PATCH] fx-rates --- modules/fx-rates/bootstrap.php | 43 +++++ modules/fx-rates/module.json | 2 + modules/fx-rates/pages/index.php | 243 +++++++++++++++++++++++++++- modules/fx-rates/src/Api/Router.php | 20 +++ 4 files changed, 301 insertions(+), 7 deletions(-) diff --git a/modules/fx-rates/bootstrap.php b/modules/fx-rates/bootstrap.php index badfb0c..32ba41a 100644 --- a/modules/fx-rates/bootstrap.php +++ b/modules/fx-rates/bootstrap.php @@ -35,6 +35,15 @@ $mm->registerFunction($moduleName, 'settings', static function (): array { $timeout = max(2, (int) ($saved['timeout_sec'] ?? (getenv('FX_RATES_TIMEOUT') ?: getenv('MINING_CHECKER_FX_TIMEOUT') ?: 10))); $cacheTtl = max(60, (int) ($saved['cache_ttl_sec'] ?? (getenv('FX_RATES_CACHE_TTL') ?: getenv('MINING_CHECKER_FX_CACHE_TTL') ?: 21600))); + $preferredCurrencies = $saved['preferred_currencies'] ?? ['EUR', 'USD', 'DOGE']; + if (is_string($preferredCurrencies)) { + $preferredCurrencies = preg_split('/[\s,;]+/', $preferredCurrencies) ?: []; + } + $preferredCurrencies = array_values(array_unique(array_filter(array_map( + static fn (mixed $code): string => strtoupper(trim((string) $code)), + is_array($preferredCurrencies) ? $preferredCurrencies : [] + ), static fn (string $code): bool => $code !== ''))); + return [ 'provider' => $provider !== '' ? $provider : 'currencyapi', 'api_url' => $apiUrl, @@ -43,12 +52,46 @@ $mm->registerFunction($moduleName, 'settings', static function (): array { 'timeout_sec' => $timeout, 'cache_ttl_sec' => $cacheTtl, 'default_base_currency' => strtoupper(trim((string) ($saved['default_base_currency'] ?? 'EUR'))) ?: 'EUR', + 'display_base_currency' => strtoupper(trim((string) ($saved['display_base_currency'] ?? ($saved['default_base_currency'] ?? 'EUR')))) ?: 'EUR', + 'preferred_currencies' => $preferredCurrencies, 'daily_refresh_enabled' => array_key_exists('daily_refresh_enabled', $saved) ? (bool) $saved['daily_refresh_enabled'] : true, 'daily_refresh_hour' => max(0, min(23, (int) ($saved['daily_refresh_hour'] ?? 18))), 'schedule_timezone' => trim((string) ($saved['schedule_timezone'] ?? 'Europe/Berlin')) ?: 'Europe/Berlin', ]; }); +$mm->registerFunction($moduleName, 'save_runtime_settings', static function (array $payload): array { + $current = modules()->settings('fx-rates'); + $normalized = module_fn('fx-rates', 'settings'); + + if (array_key_exists('default_base_currency', $payload)) { + $normalized['default_base_currency'] = strtoupper(trim((string) $payload['default_base_currency'])) ?: $normalized['default_base_currency']; + } + if (array_key_exists('display_base_currency', $payload)) { + $normalized['display_base_currency'] = strtoupper(trim((string) $payload['display_base_currency'])) ?: $normalized['display_base_currency']; + } + if (array_key_exists('preferred_currencies', $payload)) { + $preferredCurrencies = $payload['preferred_currencies']; + if (is_string($preferredCurrencies)) { + $preferredCurrencies = preg_split('/[\s,;]+/', $preferredCurrencies) ?: []; + } + $normalized['preferred_currencies'] = array_values(array_unique(array_filter(array_map( + static fn (mixed $code): string => strtoupper(trim((string) $code)), + is_array($preferredCurrencies) ? $preferredCurrencies : [] + ), static fn (string $code): bool => $code !== ''))); + } + + $toSave = array_merge($current, [ + 'default_base_currency' => $normalized['default_base_currency'], + 'display_base_currency' => $normalized['display_base_currency'], + 'preferred_currencies' => $normalized['preferred_currencies'], + ]); + + modules()->saveSettings('fx-rates', $toSave); + + return module_fn('fx-rates', 'settings'); +}); + $mm->registerFunction($moduleName, 'pdo', function () use ($moduleName): PDO { $settings = modules()->settings($moduleName); $useSeparate = !empty($settings['use_separate_db']); diff --git a/modules/fx-rates/module.json b/modules/fx-rates/module.json index a6cf510..db563cc 100644 --- a/modules/fx-rates/module.json +++ b/modules/fx-rates/module.json @@ -20,6 +20,8 @@ { "name": "timeout_sec", "label": "Timeout (Sek.)", "type": "number", "required": false }, { "name": "cache_ttl_sec", "label": "Datei-Cache TTL (Sek.)", "type": "number", "required": false }, { "name": "default_base_currency", "label": "Standard-Basiswaehrung", "type": "text", "required": false, "help": "Wird fuer taegliche Abrufe und Snapshot-Abfragen verwendet." }, + { "name": "display_base_currency", "label": "Anzeige-Basiswaehrung", "type": "text", "required": false, "help": "Basis fuer die Anzeige der zuletzt gespeicherten Kurse im Modul." }, + { "name": "preferred_currencies", "label": "Bevorzugte Waehrungen", "type": "text", "required": false, "help": "Kommagetrennte Liste, z.B. EUR,USD,DOGE,BTC." }, { "name": "daily_refresh_enabled", "label": "Taeglichen Abruf aktivieren", "type": "checkbox", "required": false }, { "name": "daily_refresh_hour", "label": "Taegliche Abrufstunde", "type": "number", "required": false, "help": "Lokale Stunde fuer den Scheduler, standardmaessig 18." }, { "name": "schedule_timezone", "label": "Scheduler-Zeitzone", "type": "text", "required": false, "help": "z.B. Europe/Berlin" } diff --git a/modules/fx-rates/pages/index.php b/modules/fx-rates/pages/index.php index 9229532..44dc12a 100644 --- a/modules/fx-rates/pages/index.php +++ b/modules/fx-rates/pages/index.php @@ -6,14 +6,243 @@ require_once dirname(__DIR__) . '/bootstrap.php'; $settings = module_fn('fx-rates', 'settings'); $service = module_fn('fx-rates', 'service'); $latest = $service->latestStatus(); +$preferredCurrencies = is_array($settings['preferred_currencies'] ?? null) ? $settings['preferred_currencies'] : []; +$pageData = json_encode([ + 'settings' => $settings, + 'latest' => $latest, + 'preferred_currencies' => $preferredCurrencies, +], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?> 'Waehrungskurse']) ?> -
-

Waehrungskurse

-

Zentrale Quelle fuer FX-Snapshots, Zeitabfragen und manuelle Aktualisierung.

-

Standard-Basis:

-

Scheduler: taeglich um :00 ()

-

Letzter Abruf:

-

API: /api/fx-rates/v1/latest, /api/fx-rates/v1/rate, /api/fx-rates/v1/history, /api/fx-rates/v1/refresh

+
'> +
+
+
+
+

Waehrungskurse

+

Zentrale Quelle fuer FX-Snapshots, Zeitabfragen und manuelle Aktualisierung.

+
+
+ + +
+
+
+
Standard-Basis:
+
Anzeige-Basis:
+
Scheduler: taeglich um :00 ()
+
Letzter Abruf:
+
+
+
+ +
+

Anzeige und Waehrungen

+

Die Auswahl wird in den Modul-Settings gespeichert und steuert den Standardaufruf der letzten Kurse.

+
+ + +
+ +
+ +
+
+ +
+

Letzter Snapshot

+
+ + + + + + + + + + +
WaehrungKurs
Noch keine Daten geladen.
+
+

API: /api/fx-rates/v1/latest, /api/fx-rates/v1/rate, /api/fx-rates/v1/history, /api/fx-rates/v1/refresh

+
+
+ + diff --git a/modules/fx-rates/src/Api/Router.php b/modules/fx-rates/src/Api/Router.php index f81c798..d11e486 100644 --- a/modules/fx-rates/src/Api/Router.php +++ b/modules/fx-rates/src/Api/Router.php @@ -29,6 +29,10 @@ final class Router if ($path === 'v1/latest' && $method === 'GET') { $symbols = $this->parseCsv($_GET['symbols'] ?? null); $base = $this->stringOrNull($_GET['base'] ?? null); + if ($symbols === null) { + $settings = module_fn('fx-rates', 'settings'); + $symbols = is_array($settings['preferred_currencies'] ?? null) ? $settings['preferred_currencies'] : null; + } $snapshot = $this->service->snapshot($base, null, $symbols, null); $this->respond(['data' => $snapshot]); } @@ -65,6 +69,10 @@ final class Router $input = $this->input(); $base = $this->stringOrNull($input['base'] ?? null); $currencies = $this->parseCsv($input['currencies'] ?? null); + if ($currencies === null) { + $settings = module_fn('fx-rates', 'settings'); + $currencies = is_array($settings['preferred_currencies'] ?? null) ? $settings['preferred_currencies'] : null; + } $force = !empty($input['force']); $maxAgeHours = is_numeric($input['max_age_hours'] ?? null) ? (float) $input['max_age_hours'] : 24.0; @@ -84,6 +92,18 @@ final class Router $this->respond(['data' => $this->service->probeCurrencyCatalog()]); } + if ($path === 'v1/currencies-refresh' && $method === 'POST') { + $this->respond(['data' => $this->service->refreshCurrencyCatalog()], 201); + } + + if ($path === 'v1/settings' && $method === 'GET') { + $this->respond(['data' => module_fn('fx-rates', 'settings')]); + } + + if ($path === 'v1/settings' && $method === 'PUT') { + $this->respond(['data' => module_fn('fx-rates', 'save_runtime_settings', $this->input())]); + } + $this->respond(['error' => 'Unbekannter API-Pfad.'], 404); } catch (\Throwable $exception) { $this->respond([