asdasd
This commit is contained in:
@@ -33,6 +33,8 @@ final class Router
|
|||||||
private ?SeedImporter $seedImporter = null;
|
private ?SeedImporter $seedImporter = null;
|
||||||
private ?SchemaManager $schemaManager = null;
|
private ?SchemaManager $schemaManager = null;
|
||||||
private ?FxService $fx = null;
|
private ?FxService $fx = null;
|
||||||
|
private ?array $fxRatesSettingsCache = null;
|
||||||
|
private ?array $currencyCatalogCache = null;
|
||||||
private DebugTrace $debug;
|
private DebugTrace $debug;
|
||||||
|
|
||||||
public function __construct(string $moduleBasePath)
|
public function __construct(string $moduleBasePath)
|
||||||
@@ -790,7 +792,11 @@ final class Router
|
|||||||
|
|
||||||
private function refreshCurrencies(): array
|
private function refreshCurrencies(): array
|
||||||
{
|
{
|
||||||
return $this->fx()->refreshCurrencyCatalog();
|
$result = $this->fx()->refreshCurrencyCatalog();
|
||||||
|
$synced = $this->syncLocalCurrencyCatalogFromFxRates(true);
|
||||||
|
return $result + [
|
||||||
|
'local_catalog_synced' => count($synced),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function probeCurrencies(): array
|
private function probeCurrencies(): array
|
||||||
@@ -1216,7 +1222,7 @@ final class Router
|
|||||||
'fx_max_age_hours' => 3,
|
'fx_max_age_hours' => 3,
|
||||||
'module_theme_mode' => 'inherit',
|
'module_theme_mode' => 'inherit',
|
||||||
'module_theme_accent' => 'teal',
|
'module_theme_accent' => 'teal',
|
||||||
'preferred_currencies' => ['DOGE', 'USD', 'EUR'],
|
'preferred_currencies' => $this->preferredCurrencies(),
|
||||||
];
|
];
|
||||||
if (!$this->isValidTimezone((string) ($base['display_timezone'] ?? ''))) {
|
if (!$this->isValidTimezone((string) ($base['display_timezone'] ?? ''))) {
|
||||||
$base['display_timezone'] = 'Europe/Berlin';
|
$base['display_timezone'] = 'Europe/Berlin';
|
||||||
@@ -1233,6 +1239,7 @@ final class Router
|
|||||||
|
|
||||||
$base['cost_plans'] = $this->costPlans($projectKey);
|
$base['cost_plans'] = $this->costPlans($projectKey);
|
||||||
$base['currencies'] = $this->currencies();
|
$base['currencies'] = $this->currencies();
|
||||||
|
$base['preferred_currencies'] = $this->preferredCurrencies($base['preferred_currencies'] ?? null);
|
||||||
$base['payouts'] = $this->payouts($projectKey);
|
$base['payouts'] = $this->payouts($projectKey);
|
||||||
$base['miner_offers'] = $this->minerOffers($projectKey);
|
$base['miner_offers'] = $this->minerOffers($projectKey);
|
||||||
$base['purchased_miners'] = $this->purchasedMiners($projectKey);
|
$base['purchased_miners'] = $this->purchasedMiners($projectKey);
|
||||||
@@ -1265,6 +1272,7 @@ final class Router
|
|||||||
$this->assertCurrencyType($settings['crypto_currency'], true, 'crypto_currency');
|
$this->assertCurrencyType($settings['crypto_currency'], true, 'crypto_currency');
|
||||||
|
|
||||||
$this->repository()->saveSettings($projectKey, $settings);
|
$this->repository()->saveSettings($projectKey, $settings);
|
||||||
|
$this->syncFxRatesPreferredCurrencies($settings['preferred_currencies']);
|
||||||
return $this->settings($projectKey);
|
return $this->settings($projectKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1455,17 +1463,18 @@ final class Router
|
|||||||
|
|
||||||
$knownCodes = array_map(
|
$knownCodes = array_map(
|
||||||
static fn (array $currency): string => strtoupper((string) ($currency['code'] ?? '')),
|
static fn (array $currency): string => strtoupper((string) ($currency['code'] ?? '')),
|
||||||
$this->repository()->listCurrencies()
|
$this->currencies()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (in_array($priceCurrency, $knownCodes, true)) {
|
if (in_array($priceCurrency, $knownCodes, true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->repository()->ensureCurrencyCode($priceCurrency, $priceCurrency);
|
$matched = $this->currencyCatalogEntry($priceCurrency);
|
||||||
|
$this->repository()->ensureCurrencyCode($priceCurrency, $matched['name'] ?? $priceCurrency);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->fx()->refreshCurrencyCatalog();
|
$this->refreshCurrencies();
|
||||||
} catch (\Throwable) {
|
} catch (\Throwable) {
|
||||||
// Measurement save must not fail because the external currency sync is unavailable.
|
// Measurement save must not fail because the external currency sync is unavailable.
|
||||||
}
|
}
|
||||||
@@ -1557,7 +1566,8 @@ final class Router
|
|||||||
|
|
||||||
private function currencies(): array
|
private function currencies(): array
|
||||||
{
|
{
|
||||||
return $this->repository()->listCurrencies();
|
$catalog = $this->syncLocalCurrencyCatalogFromFxRates();
|
||||||
|
return $catalog !== [] ? $catalog : $this->repository()->listCurrencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function currencyAliases(): array
|
private function currencyAliases(): array
|
||||||
@@ -1926,16 +1936,22 @@ final class Router
|
|||||||
return (string) $resolved['code'];
|
return (string) $resolved['code'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$catalogEntry = $this->currencyCatalogEntry($currency);
|
||||||
|
if (is_array($catalogEntry)) {
|
||||||
|
$this->repository()->ensureCurrencyCode($currency, (string) ($catalogEntry['name'] ?? $currency));
|
||||||
|
return $currency;
|
||||||
|
}
|
||||||
|
|
||||||
throw new ApiException(
|
throw new ApiException(
|
||||||
"Feld {$field} verweist auf keinen vorhandenen Waehrungsrecord.",
|
"Feld {$field} verweist auf keinen vorhandenen Waehrungsrecord.",
|
||||||
422,
|
422,
|
||||||
[
|
[
|
||||||
'field' => $field,
|
'field' => $field,
|
||||||
'missing_currency' => $currency,
|
'missing_currency' => $currency,
|
||||||
'hint' => 'Lege zuerst die Waehrung an oder hinterlege einen Alias auf einen bestehenden Waehrungsrecord.',
|
'hint' => 'Synchronisiere zuerst den Waehrungskatalog aus fx-rates oder hinterlege einen Alias auf einen bestehenden Waehrungsrecord.',
|
||||||
'available_currencies' => array_slice(array_map(
|
'available_currencies' => array_slice(array_map(
|
||||||
static fn (array $item): string => (string) ($item['code'] ?? ''),
|
static fn (array $item): string => (string) ($item['code'] ?? ''),
|
||||||
$this->repository()->listCurrencies()
|
$this->currencies()
|
||||||
), 0, 50),
|
), 0, 50),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -1951,6 +1967,7 @@ final class Router
|
|||||||
|
|
||||||
private function assertCurrencyType(string $code, bool $expectedCrypto, string $field): void
|
private function assertCurrencyType(string $code, bool $expectedCrypto, string $field): void
|
||||||
{
|
{
|
||||||
|
$this->ensureLocalCurrencyRecord($code);
|
||||||
$resolved = $this->repository()->resolveCurrencyCode($code);
|
$resolved = $this->repository()->resolveCurrencyCode($code);
|
||||||
$currency = is_array($resolved) ? ($resolved['currency'] ?? null) : null;
|
$currency = is_array($resolved) ? ($resolved['currency'] ?? null) : null;
|
||||||
$isCrypto = !empty($currency['is_crypto']);
|
$isCrypto = !empty($currency['is_crypto']);
|
||||||
@@ -2142,6 +2159,132 @@ final class Router
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function fxRatesSettings(): array
|
||||||
|
{
|
||||||
|
if ($this->fxRatesSettingsCache !== null) {
|
||||||
|
return $this->fxRatesSettingsCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modules()->isEnabled('fx-rates') || !modules()->hasFunction('fx-rates', 'settings')) {
|
||||||
|
return $this->fxRatesSettingsCache = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = module_fn('fx-rates', 'settings');
|
||||||
|
return $this->fxRatesSettingsCache = is_array($settings) ? $settings : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function preferredCurrencies(?array $fallback = null): array
|
||||||
|
{
|
||||||
|
$settings = $this->fxRatesSettings();
|
||||||
|
$preferred = $settings['preferred_currencies'] ?? $fallback ?? ['DOGE', 'USD', 'EUR'];
|
||||||
|
$normalized = [];
|
||||||
|
foreach (is_array($preferred) ? $preferred : [] as $code) {
|
||||||
|
$normalizedCode = strtoupper(trim((string) $code));
|
||||||
|
if ($normalizedCode !== '' && !in_array($normalizedCode, $normalized, true)) {
|
||||||
|
$normalized[] = $normalizedCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $normalized !== [] ? $normalized : ['DOGE', 'USD', 'EUR'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function currencyCatalog(): array
|
||||||
|
{
|
||||||
|
if ($this->currencyCatalogCache !== null) {
|
||||||
|
return $this->currencyCatalogCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $this->fxRatesSettings();
|
||||||
|
$catalog = [];
|
||||||
|
foreach (is_array($settings['currency_catalog'] ?? null) ? $settings['currency_catalog'] : [] as $entry) {
|
||||||
|
if (!is_array($entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$code = strtoupper(trim((string) ($entry['code'] ?? '')));
|
||||||
|
$name = trim((string) ($entry['name'] ?? ''));
|
||||||
|
if ($code === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$catalog[] = [
|
||||||
|
'code' => $code,
|
||||||
|
'name' => $name !== '' ? $name : $code,
|
||||||
|
'symbol' => $code,
|
||||||
|
'is_active' => 1,
|
||||||
|
'is_crypto' => $this->isCryptoCurrencyCode($code) ? 1 : 0,
|
||||||
|
'sort_order' => 1000,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->currencyCatalogCache = $catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function currencyCatalogEntry(string $code): ?array
|
||||||
|
{
|
||||||
|
$normalizedCode = strtoupper(trim($code));
|
||||||
|
if ($normalizedCode === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->currencyCatalog() as $entry) {
|
||||||
|
if ($normalizedCode === strtoupper((string) ($entry['code'] ?? ''))) {
|
||||||
|
return $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncLocalCurrencyCatalogFromFxRates(bool $forceRefresh = false): array
|
||||||
|
{
|
||||||
|
if ($forceRefresh) {
|
||||||
|
$this->fxRatesSettingsCache = null;
|
||||||
|
$this->currencyCatalogCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$catalog = $this->currencyCatalog();
|
||||||
|
if ($catalog === []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->repository()->saveCurrencies($catalog);
|
||||||
|
return $this->repository()->listCurrencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncFxRatesPreferredCurrencies(array $preferredCurrencies): void
|
||||||
|
{
|
||||||
|
if (!modules()->isEnabled('fx-rates') || !modules()->hasFunction('fx-rates', 'save_runtime_settings')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_fn('fx-rates', 'save_runtime_settings', [
|
||||||
|
'preferred_currencies' => $preferredCurrencies,
|
||||||
|
]);
|
||||||
|
$this->fxRatesSettingsCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ensureLocalCurrencyRecord(string $code): void
|
||||||
|
{
|
||||||
|
$resolved = $this->repository()->resolveCurrencyCode($code);
|
||||||
|
if ($resolved !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$catalogEntry = $this->currencyCatalogEntry($code);
|
||||||
|
if ($catalogEntry !== null) {
|
||||||
|
$this->repository()->saveCurrency($catalogEntry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->repository()->ensureCurrencyCode($code, $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isCryptoCurrencyCode(string $code): bool
|
||||||
|
{
|
||||||
|
return in_array(strtoupper(trim($code)), [
|
||||||
|
'ADA', 'ARB', 'AVAX', 'BNB', 'BTC', 'DAI', 'DOGE', 'DOT', 'ETH', 'LINK', 'LTC', 'MATIC', 'SOL', 'TRX', 'USDC', 'USDT', 'XRP', 'XMR'
|
||||||
|
], true);
|
||||||
|
}
|
||||||
|
|
||||||
private function resolveMeasurementFxFetchId(string $projectKey, array $payload, bool $allowRefresh, ?float $maxAgeHoursOverride = null): ?int
|
private function resolveMeasurementFxFetchId(string $projectKey, array $payload, bool $allowRefresh, ?float $maxAgeHoursOverride = null): ?int
|
||||||
{
|
{
|
||||||
$measuredAt = trim((string) ($payload['measured_at'] ?? ''));
|
$measuredAt = trim((string) ($payload['measured_at'] ?? ''));
|
||||||
|
|||||||
Reference in New Issue
Block a user