diff --git a/modules/fx-rates/bootstrap.php b/modules/fx-rates/bootstrap.php index 5ec077f..78815b4 100644 --- a/modules/fx-rates/bootstrap.php +++ b/modules/fx-rates/bootstrap.php @@ -30,7 +30,6 @@ $mm->registerFunction($moduleName, 'settings', static function (): array { $saved = modules()->settings('fx-rates'); $provider = trim((string) ($saved['provider'] ?? (getenv('FX_RATES_PROVIDER') ?: getenv('MINING_CHECKER_FX_PROVIDER') ?: 'currencyapi'))); $apiUrl = rtrim((string) ($saved['api_url'] ?? (getenv('FX_RATES_API_URL') ?: getenv('MINING_CHECKER_FX_URL') ?: 'https://currencyapi.net')), '/'); - $currenciesUrl = rtrim((string) ($saved['currencies_url'] ?? (getenv('FX_RATES_CURRENCIES_URL') ?: getenv('MINING_CHECKER_FX_CURRENCIES_URL') ?: getenv('MINING_CHECKER_FX_URL') ?: 'https://currencyapi.net')), '/'); $apiKey = trim((string) ($saved['api_key'] ?? (getenv('FX_RATES_API_KEY') ?: getenv('MINING_CHECKER_FX_API_KEY') ?: ''))); $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))); @@ -44,16 +43,30 @@ $mm->registerFunction($moduleName, 'settings', static function (): array { is_array($preferredCurrencies) ? $preferredCurrencies : [] ), static fn (string $code): bool => $code !== ''))); + $currencyCatalog = $saved['currency_catalog'] ?? []; + $currencyCatalog = array_values(array_filter(array_map(static function (mixed $item): ?array { + if (!is_array($item)) { + return null; + } + $code = strtoupper(trim((string) ($item['code'] ?? ''))); + $name = trim((string) ($item['name'] ?? '')); + if ($code === '' || $name === '') { + return null; + } + return ['code' => $code, 'name' => $name]; + }, is_array($currencyCatalog) ? $currencyCatalog : []))); + return [ 'provider' => $provider !== '' ? $provider : 'currencyapi', 'api_url' => $apiUrl, - 'currencies_url' => $currenciesUrl, 'api_key' => $apiKey, '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, + 'currency_catalog' => $currencyCatalog, + 'currency_catalog_synced_at' => trim((string) ($saved['currency_catalog_synced_at'] ?? '')), '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', @@ -81,6 +94,25 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr ), static fn (string $code): bool => $code !== ''))); } + $catalogCodes = []; + foreach (($normalized['currency_catalog'] ?? []) as $currency) { + if (is_array($currency)) { + $code = strtoupper(trim((string) ($currency['code'] ?? ''))); + if ($code !== '') { + $catalogCodes[$code] = true; + } + } + } + if ($catalogCodes !== [] && !isset($catalogCodes[$normalized['display_base_currency']])) { + $normalized['display_base_currency'] = $normalized['default_base_currency']; + } + if ($catalogCodes !== []) { + $normalized['preferred_currencies'] = array_values(array_filter( + $normalized['preferred_currencies'], + static fn (string $code): bool => isset($catalogCodes[$code]) + )); + } + $toSave = array_merge($current, [ 'default_base_currency' => $normalized['default_base_currency'], 'display_base_currency' => $normalized['display_base_currency'], @@ -141,7 +173,14 @@ $mm->registerFunction($moduleName, 'setup_actions', static function (): array { $mm->registerFunction($moduleName, 'run_setup_action', static function (string $action): array { return match ($action) { - 'sync_currency_catalog' => module_fn('fx-rates', 'service')->refreshCurrencyCatalog(), + 'sync_currency_catalog' => (static function (): array { + $result = module_fn('fx-rates', 'service')->refreshCurrencyCatalog(); + $current = modules()->settings('fx-rates'); + $current['currency_catalog'] = array_values(is_array($result['currencies'] ?? null) ? $result['currencies'] : []); + $current['currency_catalog_synced_at'] = gmdate('Y-m-d H:i:s'); + modules()->saveSettings('fx-rates', $current); + return $result + ['message' => 'Waehrungskatalog synchronisiert. ' . (int) ($result['synced_count'] ?? 0) . ' Waehrungen verarbeitet.']; + })(), default => throw new \RuntimeException('Unbekannte Setup-Aktion.'), }; }); diff --git a/modules/fx-rates/module.json b/modules/fx-rates/module.json index 22b82da..7300396 100644 --- a/modules/fx-rates/module.json +++ b/modules/fx-rates/module.json @@ -1,6 +1,6 @@ { "title": "Waehrungskurse", - "version": "0.1.1", + "version": "0.1.2", "description": "Zentrales Modul fuer Waehrungskurse, Historie und API-Abrufe.", "enabled_by_default": true, "setup": { @@ -15,13 +15,12 @@ { "name": "db.password", "label": "DB Passwort", "type": "password", "required": false }, { "name": "provider", "label": "FX Provider", "type": "text", "required": false, "help": "Unterstuetzt legacy currencyapi.net und currencyapi.com v3." }, { "name": "api_url", "label": "FX API URL", "type": "text", "required": false, "help": "Nur die Basis-URL eintragen, z.B. https://api.currencyapi.com oder https://currencyapi.net." }, - { "name": "currencies_url", "label": "FX Currencies URL", "type": "text", "required": false, "help": "Nur die Basis-URL fuer den Waehrungskatalog, z.B. https://api.currencyapi.com." }, { "name": "api_key", "label": "FX API Key", "type": "password", "required": false }, { "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": "display_base_currency", "label": "Anzeige-Basiswaehrung", "type": "select", "required": false, "help": "Basis fuer die Anzeige der zuletzt gespeicherten Kurse im Modul." }, + { "name": "preferred_currencies", "label": "Bevorzugte Waehrungen", "type": "multiselect", "required": false, "help": "Auswahl aus dem synchronisierten Waehrungskatalog." }, { "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/partials/landingpages/modules/setup.php b/partials/landingpages/modules/setup.php index 4988fae..2e058aa 100644 --- a/partials/landingpages/modules/setup.php +++ b/partials/landingpages/modules/setup.php @@ -40,6 +40,7 @@ foreach ($fields as $field) { $fieldTypes[$fname] = (string)($field['type'] ?? 'text'); $fieldMeta[$fname] = $field; } +$isFxRatesSetup = $moduleName === 'fx-rates'; $current = modules()->settings($moduleName); $intervalTaskStatuses = modules()->intervalTaskStatuses($moduleName); $setupActions = modules()->hasFunction($moduleName, 'setup_actions') @@ -342,7 +343,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($type === 'checkbox') { $value = isset($_POST[$postKey]) ? '1' : '0'; } - if (is_array($value)) { + if (is_array($value) && $type === 'multiselect') { + $value = array_values(array_filter(array_map( + static fn (mixed $item): string => trim((string) $item), + $value + ), static fn (string $item): bool => $item !== '')); + } elseif (is_array($value)) { continue; } $value = is_string($value) ? trim($value) : $value; @@ -374,6 +380,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } else { try { $actionResult = module_fn($moduleName, 'run_setup_action', $postedSetupAction); + $current = modules()->settings($moduleName); $notice = 'Setup-Aktion ausgefuehrt.'; if (is_array($actionResult)) { if (isset($actionResult['message']) && is_string($actionResult['message']) && trim($actionResult['message']) !== '') { @@ -444,6 +451,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } else { modules()->saveSettings($moduleName, $current); + if ($isFxRatesSetup && modules()->hasFunction($moduleName, 'save_runtime_settings')) { + module_fn($moduleName, 'save_runtime_settings', $payload); + $current = modules()->settings($moduleName); + } if (empty($payload['debug_enabled'])) { module_debug_clear($moduleName); } @@ -474,6 +485,250 @@ $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups)