From 7c33d60f146b673cddfe1240f64978065a8160ab Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sun, 10 May 2026 23:58:51 +0200 Subject: [PATCH] adasd --- modules/mining-checker/bootstrap.php | 51 ++++++++------- modules/mining-checker/module.json | 11 ++++ .../src/Infrastructure/ConnectionFactory.php | 47 +++++++++++--- partials/landingpages/modules/setup.php | 65 ++++++++++++++++--- 4 files changed, 129 insertions(+), 45 deletions(-) diff --git a/modules/mining-checker/bootstrap.php b/modules/mining-checker/bootstrap.php index d9f5ce4..68599b2 100644 --- a/modules/mining-checker/bootstrap.php +++ b/modules/mining-checker/bootstrap.php @@ -134,24 +134,19 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr $repository->saveSettings($projectKey, $settings); - return module_fn($moduleName, 'runtime_settings'); + return module_fn('mining-checker', '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', + 'label' => 'Tabellen importieren', 'help' => 'Legt die Mining-Checker Tabellen an, wenn sie noch nicht vorhanden sind.', ], [ 'name' => 'upgrade_schema', - 'label' => 'Tabellen auf neuesten Stand bringen', + 'label' => 'Tabellen updaten', 'help' => 'Fuehrt fehlende Tabellen- und Spalten-Upgrades fuer den Mining-Checker aus.', ], [ @@ -162,6 +157,29 @@ $mm->registerFunction($moduleName, 'setup_actions', static function (): array { ]; }); +$mm->registerFunction($moduleName, 'setup_status', static function (): array { + $moduleBasePath = __DIR__; + $config = ModuleConfig::load($moduleBasePath); + $pdo = ConnectionFactory::make($config); + $schema = new SchemaManager($pdo, $config->tablePrefix(), $moduleBasePath); + $status = $schema->schemaStatus(); + + return [ + 'title' => 'Tabellenstatus', + 'type' => !empty($status['all_present']) && empty($status['pending_upgrades']) ? 'success' : 'hint', + 'text' => !empty($status['all_present']) + ? (empty($status['pending_upgrades']) + ? 'Alle Mining-Checker Tabellen sind vorhanden.' + : 'Alle Grundtabellen sind vorhanden, aber es gibt noch ausstehende Upgrades.') + : 'Das Mining-Checker Schema ist noch unvollstaendig.', + 'stats' => [ + ['label' => 'Vorhandene Tabellen', 'value' => (string) ((int) ($status['present_count'] ?? 0) . '/' . count((array) ($status['required_tables'] ?? [])))], + ['label' => 'Fehlende Tabellen', 'value' => !empty($status['missing_tables']) ? implode(', ', (array) $status['missing_tables']) : 'keine'], + ['label' => 'Ausstehende Upgrades', 'value' => !empty($status['pending_upgrades']) ? implode(', ', (array) $status['pending_upgrades']) : 'keine'], + ], + ]; +}); + $mm->registerFunction($moduleName, 'run_setup_action', static function (string $action): array { $moduleBasePath = __DIR__; $config = ModuleConfig::load($moduleBasePath); @@ -174,23 +192,6 @@ $mm->registerFunction($moduleName, 'run_setup_action', static function (string $ $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'] : []; diff --git a/modules/mining-checker/module.json b/modules/mining-checker/module.json index bf4f5a2..758dc0b 100644 --- a/modules/mining-checker/module.json +++ b/modules/mining-checker/module.json @@ -5,7 +5,18 @@ "description": "Erfassung, OCR-Auswertung und Analyse von DOGE-Mining-Messwerten als eingebettetes Modul.", "enabled_by_default": true, "setup": { + "sections": { + "database": true + }, "fields": [ + { "name": "use_separate_db", "label": "Datenbank-Modus", "type": "select", "required": false, "help": "Standard nutzt die Nexus-Datenbank. Custom nutzt eine eigene Datenbankverbindung.", "options": { "0": "Standard", "1": "Custom Datenbank" } }, + { "name": "db.driver", "label": "DB Driver", "type": "text", "required": false, "help": "z.B. pgsql oder mysql" }, + { "name": "db.host", "label": "DB Host", "type": "text", "required": false }, + { "name": "db.port", "label": "DB Port", "type": "number", "required": false }, + { "name": "db.dbname", "label": "DB Name", "type": "text", "required": false }, + { "name": "db.schema", "label": "DB Schema", "type": "text", "required": false }, + { "name": "db.user", "label": "DB User", "type": "text", "required": false }, + { "name": "db.password", "label": "DB Passwort", "type": "password", "required": false }, { "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": "report_currency", "label": "Standard-Berichtswaehrung", "type": "text", "required": false, "help": "Zielwaehrung fuer Kennzahlen und Berichte, z.B. EUR." } diff --git a/modules/mining-checker/src/Infrastructure/ConnectionFactory.php b/modules/mining-checker/src/Infrastructure/ConnectionFactory.php index 1413f79..cf28e6f 100644 --- a/modules/mining-checker/src/Infrastructure/ConnectionFactory.php +++ b/modules/mining-checker/src/Infrastructure/ConnectionFactory.php @@ -11,8 +11,19 @@ final class ConnectionFactory { public static function make(ModuleConfig $config): PDO { - if (!$config->useProjectDatabase()) { - throw new ApiException('Mining-Checker erwartet aktuell die Projekt-Datenbank. Eigene Modul-Datenbanken sind hier noch nicht implementiert.', 500); + $moduleSettings = modules()->settings('mining-checker'); + $useSeparateDb = self::usesSeparateDatabase($moduleSettings); + + if ($useSeparateDb) { + $dbConfig = is_array($moduleSettings['db'] ?? null) ? $moduleSettings['db'] : []; + if ($dbConfig === []) { + throw new ApiException('Custom-Datenbank ist aktiviert, aber nicht vollstaendig konfiguriert.', 500); + } + self::assertSupportedDriver($dbConfig); + if (method_exists(AppDatabase::class, 'connectFromConfig')) { + return AppDatabase::connectFromConfig($dbConfig); + } + return AppDatabase::createFromArray($dbConfig); } $dbConfig = app()->config()->dbConfig; @@ -20,14 +31,7 @@ final class ConnectionFactory throw new ApiException('Projekt-Datenbankkonfiguration fehlt in config/db_settings_basic.php.', 500); } - $driver = strtolower((string) ($dbConfig['driver'] ?? ($dbConfig['dsn'] ?? ''))); - if ($driver !== '' && !in_array($driver, ['mysql', 'pgsql'], true) && !str_starts_with($driver, 'mysql:') && !str_starts_with($driver, 'pgsql:' )) { - throw new ApiException( - 'Mining-Checker unterstuetzt aktuell MySQL/MariaDB und PostgreSQL. Stelle in config/db_settings_basic.php den Driver auf mysql oder pgsql.', - 500, - ['driver' => $dbConfig['driver'] ?? 'unknown'] - ); - } + self::assertSupportedDriver($dbConfig); if (method_exists(AppDatabase::class, 'connectFromConfig')) { return AppDatabase::connectFromConfig($dbConfig); @@ -35,4 +39,27 @@ final class ConnectionFactory return AppDatabase::createFromArray($dbConfig); } + + private static function usesSeparateDatabase(array $moduleSettings): bool + { + $raw = $moduleSettings['use_separate_db'] ?? false; + if (is_bool($raw)) { + return $raw; + } + + $normalized = strtolower(trim((string) $raw)); + return in_array($normalized, ['1', 'true', 'yes', 'on', 'custom'], true); + } + + private static function assertSupportedDriver(array $dbConfig): void + { + $driver = strtolower((string) ($dbConfig['driver'] ?? ($dbConfig['dsn'] ?? ''))); + if ($driver !== '' && !in_array($driver, ['mysql', 'pgsql'], true) && !str_starts_with($driver, 'mysql:') && !str_starts_with($driver, 'pgsql:')) { + throw new ApiException( + 'Mining-Checker unterstuetzt aktuell MySQL/MariaDB und PostgreSQL. Stelle den Driver auf mysql oder pgsql.', + 500, + ['driver' => $dbConfig['driver'] ?? 'unknown'] + ); + } + } } diff --git a/partials/landingpages/modules/setup.php b/partials/landingpages/modules/setup.php index 242a23d..d01e974 100644 --- a/partials/landingpages/modules/setup.php +++ b/partials/landingpages/modules/setup.php @@ -15,12 +15,8 @@ if (!$module) { return; } -$allowedSetupSections = ['general', 'access', 'cron', 'custom']; -if (!in_array($currentSection, $allowedSetupSections, true)) { - $currentSection = 'general'; -} - $fields = (array)($module['setup']['fields'] ?? []); +$setupSectionConfig = is_array($module['setup']['sections'] ?? null) ? $module['setup']['sections'] : []; $hasGlobalDebugField = false; foreach ($fields as $field) { if ((string)($field['name'] ?? '') === 'debug_enabled') { @@ -692,6 +688,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } $moduleStatusPanel = null; +if (modules()->hasFunction($moduleName, 'setup_status')) { + try { + $statusPanelPayload = module_fn($moduleName, 'setup_status'); + if (is_array($statusPanelPayload)) { + $moduleStatusPanel = $statusPanelPayload; + } + } catch (\Throwable $e) { + $moduleStatusPanel = [ + 'title' => 'Status', + 'type' => 'error', + 'text' => $e->getMessage(), + 'stats' => [], + ]; + } +} $activeDbGroup = $testGroup !== null && array_key_exists($testGroup, $dbGroups) ? $testGroup @@ -726,15 +737,31 @@ sort($knownGroups, SORT_NATURAL | SORT_FLAG_CASE); $knownUserValues = array_column($knownUsers, 'sub'); $manualUsers = array_values(array_filter($allowedUsers, fn (string $value): bool => !in_array($value, $knownUserValues, true))); $manualGroups = array_values(array_filter($allowedGroups, fn (string $value): bool => !in_array($value, $knownGroups, true))); +$hasDatabaseSection = array_key_exists('database', $setupSectionConfig) + ? !empty($setupSectionConfig['database']) + : $dbGroups !== []; $hasCustomSection = $customSetupFields !== [] || $setupActions !== [] || $isFxRatesSetup; +$showCustomDbConfig = !empty($current['use_separate_db']) && !in_array(strtolower(trim((string) ($current['use_separate_db'] ?? ''))), ['0', 'false', 'off', 'standard'], true); +$allowedSetupSections = ['general', 'access', 'cron']; +if ($hasDatabaseSection) { + $allowedSetupSections[] = 'database'; +} +if ($hasCustomSection) { + $allowedSetupSections[] = 'custom'; +} +if (!in_array($currentSection, $allowedSetupSections, true)) { + $currentSection = 'general'; +} $sectionUrls = [ 'general' => '/modules/setup/' . rawurlencode($moduleName) . '/general', + 'database' => '/modules/setup/' . rawurlencode($moduleName) . '/database', 'access' => '/modules/setup/' . rawurlencode($moduleName) . '/access', 'cron' => '/modules/setup/' . rawurlencode($moduleName) . '/cron', 'custom' => '/modules/setup/' . rawurlencode($moduleName) . '/custom', ]; $sectionTitles = [ 'general' => 'Allgemein', + 'database' => 'Datenbank', 'access' => 'Zugriffsrechte', 'cron' => 'Cron Einstellungen', 'custom' => 'Custom Settings', @@ -749,6 +776,9 @@ $GLOBALS['layout_header_context'] = 'Setup / ' . ($sectionTitles[$currentSection