270 lines
12 KiB
PHP
270 lines
12 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use App\ModuleConfigException;
|
|
use Modules\FxRates\Domain\FxRatesService;
|
|
use Modules\FxRates\Infrastructure\FxRatesRepository;
|
|
|
|
spl_autoload_register(static function (string $class): void {
|
|
$prefix = 'Modules\\FxRates\\';
|
|
if (!str_starts_with($class, $prefix)) {
|
|
return;
|
|
}
|
|
|
|
$file = __DIR__ . '/src/' . str_replace('\\', '/', substr($class, strlen($prefix))) . '.php';
|
|
if (is_file($file)) {
|
|
require_once $file;
|
|
}
|
|
});
|
|
|
|
$moduleName = 'fx-rates';
|
|
$mm = isset($modules) && $modules instanceof App\ModuleManager ? $modules : modules();
|
|
|
|
$mm->registerFunction($moduleName, 'table', static function (string $name): string {
|
|
$prefix = 'fxrate_';
|
|
$sanitized = preg_replace('/[^a-zA-Z0-9_]/', '', $name);
|
|
return $prefix . $sanitized;
|
|
});
|
|
|
|
$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')));
|
|
$apiVersion = strtolower(trim((string) ($saved['api_version'] ?? 'v2')));
|
|
$apiUrl = rtrim((string) ($saved['api_url'] ?? (getenv('FX_RATES_API_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)));
|
|
|
|
$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 !== '')));
|
|
|
|
$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_version' => in_array($apiVersion, ['v2', 'v3'], true) ? $apiVersion : 'v2',
|
|
'api_url' => $apiUrl,
|
|
'api_key' => $apiKey,
|
|
'timeout_sec' => $timeout,
|
|
'refresh_max_age_minutes' => max(1, (int) ($saved['refresh_max_age_minutes'] ?? 60)),
|
|
'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'] ?? '')),
|
|
'schedule_timezone' => trim((string) ($saved['schedule_timezone'] ?? nexus_cron_timezone_name())) ?: nexus_cron_timezone_name(),
|
|
];
|
|
});
|
|
|
|
$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 !== '')));
|
|
}
|
|
|
|
$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'],
|
|
'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']);
|
|
|
|
if ($useSeparate) {
|
|
$module = modules()->get($moduleName);
|
|
$fallback = $module['db_defaults'] ?? [];
|
|
return modules()->modulePdo($moduleName, $fallback);
|
|
}
|
|
|
|
$base = app()->basePdo();
|
|
if ($base instanceof PDO) {
|
|
return $base;
|
|
}
|
|
|
|
throw new ModuleConfigException(
|
|
$moduleName,
|
|
'Base-DB ist deaktiviert. Bitte Base-DB aktivieren oder eine eigene Modul-DB konfigurieren.'
|
|
);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'repository', static function (): FxRatesRepository {
|
|
return new FxRatesRepository(module_fn('fx-rates', 'pdo'), 'fxrate_');
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'ensure_schema', static function (): void {
|
|
module_fn('fx-rates', 'repository')->ensureSchema();
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'service', static function (): FxRatesService {
|
|
module_fn('fx-rates', 'ensure_schema');
|
|
return new FxRatesService(
|
|
module_fn('fx-rates', 'repository'),
|
|
module_fn('fx-rates', 'settings')
|
|
);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'setup_actions', static function (): array {
|
|
return [
|
|
[
|
|
'name' => 'sync_currency_catalog',
|
|
'label' => 'Waehrungskatalog synchronisieren',
|
|
'help' => 'Laedt die verfuegbaren Waehrungen einmalig aus dem konfigurierten FX-Provider.',
|
|
],
|
|
];
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'run_setup_action', static function (string $action): array {
|
|
return match ($action) {
|
|
'sync_currency_catalog' => (static function (): array {
|
|
$result = module_fn('fx-rates', 'service')->refreshCurrencyCatalog();
|
|
$current = modules()->settings('fx-rates');
|
|
|
|
$catalog = [];
|
|
foreach (is_array($result['currencies'] ?? null) ? $result['currencies'] : [] as $item) {
|
|
if (!is_array($item)) {
|
|
continue;
|
|
}
|
|
$code = strtoupper(trim((string) ($item['code'] ?? '')));
|
|
$name = trim((string) ($item['name'] ?? ''));
|
|
if ($code === '' || $name === '') {
|
|
continue;
|
|
}
|
|
$catalog[$code] = ['code' => $code, 'name' => $name];
|
|
}
|
|
|
|
$latest = module_fn('fx-rates', 'service')->latestStatus();
|
|
if (is_array($latest) && !empty($latest['id'])) {
|
|
$snapshot = module_fn('fx-rates', 'snapshot', null, null, null, null);
|
|
$rates = is_array($snapshot['rates'] ?? null) ? $snapshot['rates'] : [];
|
|
foreach (array_keys($rates) as $code) {
|
|
$code = strtoupper(trim((string) $code));
|
|
if ($code !== '' && !isset($catalog[$code])) {
|
|
$catalog[$code] = ['code' => $code, 'name' => $code];
|
|
}
|
|
}
|
|
$baseCode = strtoupper(trim((string) ($snapshot['base_currency'] ?? '')));
|
|
if ($baseCode !== '' && !isset($catalog[$baseCode])) {
|
|
$catalog[$baseCode] = ['code' => $baseCode, 'name' => $baseCode];
|
|
}
|
|
}
|
|
|
|
foreach ([
|
|
(string) ($current['default_base_currency'] ?? ''),
|
|
(string) ($current['display_base_currency'] ?? ''),
|
|
...((is_array($current['preferred_currencies'] ?? null) ? $current['preferred_currencies'] : [])),
|
|
] as $code) {
|
|
$code = strtoupper(trim((string) $code));
|
|
if ($code !== '' && !isset($catalog[$code])) {
|
|
$catalog[$code] = ['code' => $code, 'name' => $code];
|
|
}
|
|
}
|
|
|
|
ksort($catalog);
|
|
$current['currency_catalog'] = array_values($catalog);
|
|
$current['currency_catalog_synced_at'] = gmdate('Y-m-d H:i:s');
|
|
modules()->saveSettings('fx-rates', $current);
|
|
return $result + [
|
|
'currencies' => array_values($catalog),
|
|
'synced_count' => count($catalog),
|
|
'message' => 'Waehrungskatalog synchronisiert. ' . count($catalog) . ' Waehrungen verarbeitet.',
|
|
];
|
|
})(),
|
|
default => throw new \RuntimeException('Unbekannte Setup-Aktion.'),
|
|
};
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'refresh_latest', static function (?array $currencies = null, ?string $baseCurrency = null): array {
|
|
return module_fn('fx-rates', 'service')->refreshLatestRates($currencies, $baseCurrency);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'ensure_fresh_latest_rates', static function (float $maxAgeHours = 24.0, ?string $baseCurrency = null, ?array $currencies = null): array {
|
|
return module_fn('fx-rates', 'service')->ensureFreshLatestRates($maxAgeHours, $baseCurrency, $currencies);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'rate', static function (string $fromCurrency, string $toCurrency, ?string $at = null, ?int $windowMinutes = null): ?array {
|
|
return module_fn('fx-rates', 'service')->findRate($fromCurrency, $toCurrency, $at, $windowMinutes);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'convert', static function (?float $amount, ?string $fromCurrency, ?string $toCurrency, ?string $at = null, ?int $windowMinutes = null): ?float {
|
|
return module_fn('fx-rates', 'service')->convert($amount, $fromCurrency, $toCurrency, $at, $windowMinutes);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'snapshot', static function (?string $baseCurrency = null, ?string $at = null, ?array $symbols = null, ?int $windowMinutes = null): ?array {
|
|
return module_fn('fx-rates', 'service')->snapshot($baseCurrency, $at, $symbols, $windowMinutes);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'recent_fetches', static function (int $limit = 20): array {
|
|
return module_fn('fx-rates', 'service')->recentFetches($limit);
|
|
});
|
|
|
|
$mm->registerFunction($moduleName, 'scheduled_refresh', static function (array $context = []): array {
|
|
$result = module_fn('fx-rates', 'service')->runScheduledRefresh($context);
|
|
if (function_exists('module_debug_push')) {
|
|
module_debug_push('fx-rates', [
|
|
'label' => 'Intervall-Aufgabe',
|
|
'type' => 'scheduler:run',
|
|
'task' => 'daily_refresh',
|
|
'context' => $context,
|
|
'message' => (string) ($result['message'] ?? ''),
|
|
]);
|
|
}
|
|
return $result;
|
|
});
|