diff --git a/modules/mining-checker/bootstrap.php b/modules/mining-checker/bootstrap.php index 4e5dc8c..d9f5ce4 100644 --- a/modules/mining-checker/bootstrap.php +++ b/modules/mining-checker/bootstrap.php @@ -50,24 +50,10 @@ $mm->registerFunction($moduleName, 'runtime_settings', static function (): array } } - $preferredCurrencies = $settings['preferred_currencies'] ?? ['DOGE', 'USD', 'EUR']; - if (is_string($preferredCurrencies)) { - $preferredCurrencies = preg_split('/[\s,;]+/', $preferredCurrencies) ?: []; - } - $preferredCurrencies = array_values(array_filter(array_map( - static fn (mixed $value): string => strtoupper(trim((string) $value)), - is_array($preferredCurrencies) ? $preferredCurrencies : [] - ), static fn (string $value): bool => $value !== '')); - return [ 'baseline_measured_at' => $baselineMeasuredAt, 'baseline_coins_total' => isset($settings['baseline_coins_total']) ? (string) $settings['baseline_coins_total'] : '', - 'daily_cost_amount' => isset($settings['daily_cost_amount']) ? (string) $settings['daily_cost_amount'] : '', - 'daily_cost_currency' => strtoupper(trim((string) ($settings['daily_cost_currency'] ?? 'EUR'))) ?: 'EUR', 'report_currency' => strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR', - 'crypto_currency' => strtoupper(trim((string) ($settings['crypto_currency'] ?? 'DOGE'))) ?: 'DOGE', - 'display_timezone' => $displayTimezone, - 'preferred_currencies' => $preferredCurrencies, ]; }); @@ -86,7 +72,7 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr $repository->ensureProject($projectKey, strtoupper(str_replace('-', ' ', $projectKey))); $existing = $repository->getSettings($projectKey) ?? []; - $displayTimezone = trim((string) ($payload['display_timezone'] ?? ($existing['display_timezone'] ?? 'Europe/Berlin'))); + $displayTimezone = trim((string) ($existing['display_timezone'] ?? 'Europe/Berlin')); if ($displayTimezone === '') { $displayTimezone = 'Europe/Berlin'; } @@ -111,15 +97,11 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr } $baselineCoins = trim((string) ($payload['baseline_coins_total'] ?? ($existing['baseline_coins_total'] ?? ''))); - $dailyCostAmount = trim((string) ($payload['daily_cost_amount'] ?? ($existing['daily_cost_amount'] ?? ''))); if ($baselineCoins === '' || !is_numeric($baselineCoins)) { throw new RuntimeException('Baseline Coins muessen numerisch sein.'); } - if ($dailyCostAmount === '' || !is_numeric($dailyCostAmount)) { - throw new RuntimeException('Taegliche Kosten muessen numerisch sein.'); - } - $preferredCurrencies = $payload['preferred_currencies'] ?? ($existing['preferred_currencies'] ?? ['DOGE', 'USD', 'EUR']); + $preferredCurrencies = $existing['preferred_currencies'] ?? ['DOGE', 'USD', 'EUR']; if (is_string($preferredCurrencies)) { $preferredCurrencies = preg_split('/[\s,;]+/', $preferredCurrencies) ?: []; } @@ -131,10 +113,12 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr $settings = [ 'baseline_measured_at' => $baselineUtc, 'baseline_coins_total' => (float) $baselineCoins, - 'daily_cost_amount' => (float) $dailyCostAmount, - 'daily_cost_currency' => strtoupper(trim((string) ($payload['daily_cost_currency'] ?? ($existing['daily_cost_currency'] ?? 'EUR')))) ?: 'EUR', + 'daily_cost_amount' => isset($existing['daily_cost_amount']) && is_numeric((string) $existing['daily_cost_amount']) + ? (float) $existing['daily_cost_amount'] + : 0.0, + 'daily_cost_currency' => strtoupper(trim((string) ($existing['daily_cost_currency'] ?? 'EUR'))) ?: 'EUR', 'report_currency' => strtoupper(trim((string) ($payload['report_currency'] ?? ($existing['report_currency'] ?? 'EUR')))) ?: 'EUR', - 'crypto_currency' => strtoupper(trim((string) ($payload['crypto_currency'] ?? ($existing['crypto_currency'] ?? 'DOGE')))) ?: 'DOGE', + 'crypto_currency' => strtoupper(trim((string) ($existing['crypto_currency'] ?? 'DOGE'))) ?: 'DOGE', 'display_timezone' => $displayTimezone, 'fx_max_age_hours' => isset($existing['fx_max_age_hours']) && is_numeric((string) $existing['fx_max_age_hours']) ? (int) $existing['fx_max_age_hours'] @@ -150,15 +134,92 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr $repository->saveSettings($projectKey, $settings); - if (modules()->isEnabled('fx-rates') && modules()->hasFunction('fx-rates', 'save_runtime_settings')) { - module_fn('fx-rates', 'save_runtime_settings', [ - 'preferred_currencies' => $preferredCurrencies, - ]); - } - return module_fn($moduleName, 'runtime_settings'); }); +$mm->registerFunction($moduleName, 'setup_actions', static function (): array { + return [ + [ + 'name' => 'connection_test', + 'label' => 'DB-Verbindung testen', + 'help' => 'Prueft, ob die Projekt-Datenbank fuer den Mining-Checker erreichbar ist.', + ], + [ + 'name' => 'initialize_schema', + 'label' => 'Schema initialisieren', + 'help' => 'Legt die Mining-Checker Tabellen an, wenn sie noch nicht vorhanden sind.', + ], + [ + 'name' => 'upgrade_schema', + 'label' => 'Tabellen auf neuesten Stand bringen', + 'help' => 'Fuehrt fehlende Tabellen- und Spalten-Upgrades fuer den Mining-Checker aus.', + ], + [ + 'name' => 'seed_import', + 'label' => 'Seed-Daten importieren', + 'help' => 'Importiert die vordefinierten Startdaten fuer das Standardprojekt.', + ], + ]; +}); + +$mm->registerFunction($moduleName, 'run_setup_action', static function (string $action): array { + $moduleBasePath = __DIR__; + $config = ModuleConfig::load($moduleBasePath); + $projectKey = $config->defaultProjectKey(); + $user = auth_user() ?? []; + $ownerSub = trim((string) ($user['sub'] ?? '')) !== '' ? trim((string) ($user['sub'] ?? '')) : 'local'; + + $pdo = ConnectionFactory::make($config); + $schema = new SchemaManager($pdo, $config->tablePrefix(), $moduleBasePath); + $repository = new MiningRepository($pdo, $config->tablePrefix(), null, $ownerSub); + + return match ($action) { + 'connection_test' => (static function () use ($pdo, $config): array { + $driver = strtolower((string) $pdo->getAttribute(PDO::ATTR_DRIVER_NAME)); + $database = 'n/a'; + try { + $database = match ($driver) { + 'pgsql' => (string) ($pdo->query('SELECT current_database()')->fetchColumn() ?: 'n/a'), + 'mysql' => (string) ($pdo->query('SELECT DATABASE()')->fetchColumn() ?: 'n/a'), + default => 'n/a', + }; + } catch (\Throwable) { + $database = 'n/a'; + } + + return [ + 'message' => 'DB-Verbindung erfolgreich. Driver: ' . $driver . ', Datenbank: ' . $database . ', Tabellenpraefix: ' . $config->tablePrefix() . '.', + ]; + })(), + 'initialize_schema' => (static function () use ($schema): array { + $result = $schema->initializeSchema(false); + $after = is_array($result['after'] ?? null) ? $result['after'] : []; + return [ + 'message' => ($result['message'] ?? 'Schema initialisiert.') + . ' Vorhanden: ' . (int) ($after['present_count'] ?? 0) + . '/' . count((array) ($after['required_tables'] ?? [])) . '.', + ]; + })(), + 'upgrade_schema' => (static function () use ($schema): array { + $result = $schema->upgradeSchemaDirect(); + $applied = array_values(array_filter(array_map('strval', (array) ($result['upgraded'] ?? [])))); + return [ + 'message' => ($result['message'] ?? 'Schema-Upgrade ausgefuehrt.') + . ($applied !== [] ? ' Upgrades: ' . implode(', ', $applied) . '.' : ''), + ]; + })(), + 'seed_import' => (static function () use ($schema, $repository, $projectKey): array { + $schema->ensureSchema(); + $repository->ensureProject($projectKey, strtoupper(str_replace('-', ' ', $projectKey))); + $result = (new \Modules\MiningChecker\Domain\SeedImporter($repository))->import($projectKey); + return [ + 'message' => 'Seed-Daten importiert. Eingefuegt: ' . (int) ($result['inserted'] ?? 0) . '.', + ]; + })(), + default => throw new RuntimeException('Unbekannte Setup-Aktion.'), + }; +}); + $mm->registerFunction('mining-checker', 'sql_import_target', static function (): array { $moduleBasePath = __DIR__; $config = ModuleConfig::load($moduleBasePath); diff --git a/modules/mining-checker/module.json b/modules/mining-checker/module.json index 9f63417..bf4f5a2 100644 --- a/modules/mining-checker/module.json +++ b/modules/mining-checker/module.json @@ -8,12 +8,7 @@ "fields": [ { "name": "baseline_measured_at", "label": "Baseline Zeitpunkt", "type": "datetime-local", "required": false, "help": "Referenzzeitpunkt fuer die erste Baseline-Messung." }, { "name": "baseline_coins_total", "label": "Baseline Coins", "type": "number", "required": false, "help": "Coin-Bestand zum Baseline-Zeitpunkt." }, - { "name": "daily_cost_amount", "label": "Taegliche Kosten", "type": "number", "required": false, "help": "Taegliche Kosten fuer die Break-even-Berechnung." }, - { "name": "daily_cost_currency", "label": "Kostenwaehrung", "type": "text", "required": false, "help": "FIAT-Waehrung der taeglichen Kosten, z.B. EUR." }, - { "name": "report_currency", "label": "Standard-Berichtswaehrung", "type": "text", "required": false, "help": "Zielwaehrung fuer Kennzahlen und Berichte, z.B. EUR." }, - { "name": "crypto_currency", "label": "Standard-Krypto-Waehrung", "type": "text", "required": false, "help": "Aktuell geminte Hauptwaehrung, z.B. DOGE oder BTC." }, - { "name": "display_timezone", "label": "Anzeige-Zeitzone", "type": "text", "required": false, "help": "Zeitzone fuer lokale Datums- und Uhrzeitanzeige, z.B. Europe/Berlin." }, - { "name": "preferred_currencies", "label": "Bevorzugte Waehrungen", "type": "textarea", "required": false, "help": "Mehrere Codes mit Komma oder Zeilenumbruch trennen, z.B. DOGE, USD, EUR." } + { "name": "report_currency", "label": "Standard-Berichtswaehrung", "type": "text", "required": false, "help": "Zielwaehrung fuer Kennzahlen und Berichte, z.B. EUR." } ] }, "auth": {