From f46de880f4f942a21d57c288a9550f720b8c91fa Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Mon, 11 May 2026 02:03:32 +0200 Subject: [PATCH] sadsad --- MODULCREATION_ANWEISUNG.md | 7 +- PROJECT_CONTEXT.md | 157 +++++++++++++++++- modules/boersenchecker/bootstrap.php | 2 +- modules/boersenchecker/pages/chart_data.php | 2 +- .../src/Support/DashboardPage.php | 2 +- .../src/Support/InstrumentPage.php | 2 +- modules/fx-rates/bootstrap.php | 2 +- .../fx-rates/src/Domain/FxRatesService.php | 4 +- modules/mining-checker/bootstrap.php | 8 +- modules/mining-checker/src/Api/Router.php | 12 +- .../src/Infrastructure/MiningRepository.php | 2 +- 11 files changed, 178 insertions(+), 22 deletions(-) diff --git a/MODULCREATION_ANWEISUNG.md b/MODULCREATION_ANWEISUNG.md index 17fa1a8..c17089f 100644 --- a/MODULCREATION_ANWEISUNG.md +++ b/MODULCREATION_ANWEISUNG.md @@ -2,4 +2,9 @@ Bitte PROJECT_CONTEXT.md lesen und strikt einhalten. Wichtig: Modul-spezifischer Code/Assets ausschließlich unter /modules//, keine Änderungen an /public/assets/* für modul-spezifische Features. Staging/Live: /config/ im Repo wird nach /app//config kopiert. -Modul-Assets müssen über /module//asset geladen werden. \ No newline at end of file +Modul-Assets müssen über /module//asset geladen werden. +Setup-Regel: `Allgemein`, `Datenbank`, `Zugriffsrechte` und `Cron Einstellungen` kommen immer aus dem globalen Setup-System. +Nur `Custom Settings` darf modulspezifisch sein. +Für Zeitzonen und Debug nach Möglichkeit die globalen Helfer aus `src/App/functions.php` nutzen. +Global bereits im Setup vorhanden sind Navigation, Debug-Feld, DB-Logik, Zugriffsschutz, Cron-/Scheduler-Logik, Setup-Aktionen, Statusblöcke und Zeitzonen-Vererbung. +Ein neues Modul soll dafür nur noch `setup.fields`, optionale `scheduler_jobs` / `interval_tasks` und bei Bedarf `setup_actions` / `setup_status` liefern. diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 25d2f5f..3ff67a2 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -84,17 +84,168 @@ Modulspezifische Assets: - Mining-Checker: Seitenheader-Box, Submenü-Box, Bereichs-Box, Karten-Boxen, Karten-Boxen, Bereichs-Box - Modulverwaltung: Seitenheader-Box, Submenü-Box, danach Karten-Boxen -8) Sicherheits-/Netzwerk-Constraints +8) Globales Setup-System (VERBINDLICH) +- Modul-Setup wird zentral über `partials/landingpages/modules/setup.php` gerendert. +- Die Bereiche + - `Allgemein` + - `Datenbank` + - `Zugriffsrechte` + - `Cron Einstellungen` + muessen fuer alle Module aus dieser gemeinsamen Setup-Logik kommen. +- Nur `Custom Settings` darf modulspezifischen Inhalt enthalten. +- Modul-spezifische Sonderlayouts fuer die Bereiche `Allgemein`, `Datenbank`, `Zugriffsrechte` oder `Cron Einstellungen` sind nicht erlaubt. +- Wenn sich das Verhalten eines dieser Bereiche ändert, muss die Änderung zentral erfolgen, so dass sie automatisch fuer alle Module gilt. + +Was global im Setup bereits verfügbar ist: +- gemeinsame Setup-Navigation mit festen Unterseiten +- rechte Aktionsseite mit + - `Nexus Übersicht` + - `Zurück zum Modul` +- gemeinsames Speichern pro Bereich +- gemeinsames Rendering von + - Textfeldern + - Zahlenfeldern + - Checkboxen + - Selects + - Multiselects + - Textareas +- globale `Debug aktivieren`-Option pro Modul im Bereich `Allgemein` +- gemeinsame Datenbank-Logik mit + - `Eigene Modul-DB nutzen` + - Anzeige/Verbergen der Custom-DB-Felder + - optional mehreren DB-Gruppen wie `db.*` und `metadata_db.*` + - `Verbindung testen` + - `Standardwerte laden` +- gemeinsame Auth-/Zugriffslogik mit + - `Login erforderlich` + - erlaubten Benutzern + - erlaubten Gruppen + - bekannten Keycloak-Benutzern und -Gruppen +- gemeinsame Cron-/Scheduler-Logik mit + - Anzeige von Intervall-Tasks + - Anzeige von Cron-Jobs + - Bearbeiten von Cron-Einträgen im Modal + - Cron-Test direkt aus dem Setup + - Mehrfacheinträgen bei `mode = multi` + - Modul-Zeitzonen-Override fuer Crons + - Vererbung globaler Cron-Zeitzonen-Defaults +- gemeinsame Darstellung von Setup-Aktionen und Statusblöcken +- globale Zeitzonen-Datalist aus `nexus_timezones` + +Was ein Modul für das Setup nur noch liefern muss: +- `module.json` mit `setup.fields` +- optional `setup.sections.database` +- optional `interval_tasks` +- optional `scheduler_jobs` +- optional `auth` +- optional Bootstrap-Funktionen: + - `setup_actions` + - `run_setup_action` + - `setup_status` + - `runtime_settings` + - `save_runtime_settings` + +Was nicht mehr jedes Modul selbst bauen darf: +- eigene Setup-Seitenstruktur fuer `Allgemein`, `Datenbank`, `Zugriffsrechte`, `Cron Einstellungen` +- eigene DB-Toggle-Logik fuer Standard/Custom +- eigene Cron-Editor-Grundlogik +- eigene Debug-UI-Grundlogik +- eigene globale Zeitzonen-Defaults + +Setup-Navigation: +- Setup-Routen laufen zentral ueber: + - `/modules/setup//general` + - `/modules/setup//database` + - `/modules/setup//access` + - `/modules/setup//cron` + - `/modules/setup//custom` +- `Setup` gehoert in der Modulansicht in die rechte Aktionsseite der Submenü-Box. + +Steuerung per `module.json`: +- Ein Modul kann ueber `setup.sections.database: true|false` steuern, ob der Menüpunkt `Datenbank` angezeigt wird. +- Wenn `setup.sections.database` fehlt, kann die zentrale Setup-Logik den Punkt implizit aktivieren, sobald DB-Felder vorhanden sind. +- Modulfelder fuer `Allgemein`, `Datenbank`, `Cron` und `Custom Settings` werden zentral nach Feldnamen aufgeteilt: + - `debug_enabled` -> `Allgemein` + - `use_separate_db` und `db.*` / `metadata_db.*` -> `Datenbank` + - `schedule_timezone` -> `Cron Einstellungen` + - alle übrigen Setup-Felder -> `Custom Settings` + +Weitere anerkannte Setup-Bausteine: +- `interval_tasks` + - fuer automatische Aufgaben beim Modulaufruf +- `scheduler_jobs` + - fuer zentrale Cron-Jobs ueber den Nexus-Scheduler +- `auth` + - fuer den initialen Modulschutz +- `db_defaults` + - fuer vorbefuellte Standard-DB-Werte im Datenbankbereich +- `metadata_db_defaults` + - fuer weitere globale DB-Gruppen im Datenbankbereich + +Speicherregel: +- Beim Speichern eines Setup-Bereichs duerfen nur die in diesem Bereich sichtbaren Felder aktualisiert werden. +- Felder aus anderen Bereichen duerfen nicht mit `null`, `0` oder leeren Strings ueberschrieben werden. + +Datenbankbereich: +- `Eigene Modul-DB nutzen` ist der zentrale Standard-Schalter fuer Module mit optionaler eigener DB. +- Wenn der Schalter deaktiviert ist, duerfen keine Custom-DB-Eingabefelder sichtbar sein. +- Datenbankaktionen und Tabellenstatus gehoeren in den Menüpunkt `Datenbank`, nicht in `Custom Settings`. + +9) Globale Zeitzonen-Logik (VERBINDLICH) +- Globale Nexus-Einstellungen liegen unter `/settings`. +- Dort werden zentral gepflegt: + - `Anzeige-Zeitzone` + - `Standard-Zeitzone für Crons` +- `Anzeige-Zeitzone`: + - hat eine `Custom`-Checkbox + - ohne Custom wird die System-Zeitzone verwendet + - die aktive Zeitzone soll angezeigt werden +- `Standard-Zeitzone für Crons`: + - ist der globale Default fuer Modul-Crons + - Module duerfen diese Zeitzone im Setup uebersteuern + - einzelne Cron-Einträge duerfen sie ebenfalls uebersteuern + +Modul-Cron-Verhalten: +- Im Modul-Setup gibt es keinen nackten Pflicht-Default mehr, der immer direkt eingegeben werden muss. +- Stattdessen: + - Anzeige des aktuell wirksamen Modul-Cron-Defaults + - `Custom-Zeitzone verwenden` fuer Modul-Override + - einzelne Cron-Einträge koennen ebenfalls `Custom-Zeitzone verwenden` +- Ohne Override erbt ein Cron-Eintrag die Modul-Zeitzone, und die Modul-Zeitzone erbt den globalen Nexus-Cron-Default. + +Globale PHP-Helfer: +- Neue Module sollen fuer zentrale Zeit-/Debug-Defaults nach Moeglichkeit die globalen Funktionen aus `src/App/functions.php` nutzen: + - `nexus_settings()` + - `nexus_save_settings(array $settings)` + - `nexus_system_timezone_name()` + - `nexus_display_timezone_name()` + - `nexus_cron_timezone_name()` + - `module_debug_enabled(string $module)` + - `module_debug_push(string $module, array $entry)` + - `module_debug_clear(string $module)` + +Regel fuer neue Module: +- Keine Zeitzone wie `Europe/Berlin` hart im Modul als Standard erzwingen, wenn dafuer ein globaler Nexus-Default existiert. +- Fuer Anzeige-/Formatierungslogik nach Moeglichkeit `nexus_display_timezone_name()` nutzen. +- Fuer Cron-Fallbacks nach Moeglichkeit `nexus_cron_timezone_name()` nutzen. + +10) Globales Debug-System +- Das Debug-Popup ist eine globale Infrastruktur aus dem zentralen Layout. +- Aktivierung bleibt jedoch pro Modul ueber `debug_enabled` im Modul-Setup. +- Das Debug-Symbol darf nur sichtbar sein, wenn fuer das aktuelle Modul Debug aktiv ist. +- Module sollen keine eigene separate Debug-Oberflaeche bauen, wenn der globale Debug-Stream genutzt werden kann. + +11) Sicherheits-/Netzwerk-Constraints - Zugriff im Heimnetz (192.168.178.0/24) per Nginx begrenzt. - SSH-Hosts nur Heimnetz. -9) Pi Control Besonderheiten (konkret) +12) Pi Control Besonderheiten (konkret) - Worker/Jobs unter /tools/pi_control/ - Check-Updates & Cron nutzen die gleichen SSH-Routinen. - Host-Karten, Befehle und Konsole sind UI im Modul. - Update/Upgrade-Checks liefern Debug-Ausgaben, die als Tooltip oder Debugzeile angezeigt werden. -10) Zusammenfassung (kurz) +13) Zusammenfassung (kurz) Nexus ist modular, mit strikter Trennung zwischen globalem Layout und modulspezifischem Code. Staging/Live haben eigene /app//config-Strukturen; /config/ im Repo wird beim Deployment kopiert. Modul-Assets gehören ausschließlich in den Modul-Ordner und werden dort geladen. diff --git a/modules/boersenchecker/bootstrap.php b/modules/boersenchecker/bootstrap.php index abf321f..cea0bba 100644 --- a/modules/boersenchecker/bootstrap.php +++ b/modules/boersenchecker/bootstrap.php @@ -574,7 +574,7 @@ $mm->registerFunction($moduleName, 'format_datetime_for_display', static functio }); $mm->registerFunction($moduleName, 'local_now_input_value', static function (): string { - return (new \DateTimeImmutable('now', new \DateTimeZone('Europe/Berlin')))->format('Y-m-d\TH:i'); + return (new \DateTimeImmutable('now', new \DateTimeZone(nexus_display_timezone_name())))->format('Y-m-d\TH:i'); }); $mm->registerFunction($moduleName, 'alpha_vantage_extract_global_quote', static function (array $entry): ?array { diff --git a/modules/boersenchecker/pages/chart_data.php b/modules/boersenchecker/pages/chart_data.php index e7a6c71..3dbcf4e 100644 --- a/modules/boersenchecker/pages/chart_data.php +++ b/modules/boersenchecker/pages/chart_data.php @@ -103,7 +103,7 @@ if ($daily === []) { $aggregate = static function (array $points, string $format): array { $result = []; - $timezone = new DateTimeZone('Europe/Berlin'); + $timezone = new DateTimeZone(nexus_display_timezone_name()); foreach ($points as $point) { $date = DateTimeImmutable::createFromFormat('Y-m-d', (string) ($point['date'] ?? ''), $timezone); if (!$date instanceof DateTimeImmutable) { diff --git a/modules/boersenchecker/src/Support/DashboardPage.php b/modules/boersenchecker/src/Support/DashboardPage.php index 93b336f..325c3ec 100644 --- a/modules/boersenchecker/src/Support/DashboardPage.php +++ b/modules/boersenchecker/src/Support/DashboardPage.php @@ -840,7 +840,7 @@ final class DashboardPage private function normalizeDateTimeLocal(?string $value): string { - $timezone = new \DateTimeZone('Europe/Berlin'); + $timezone = new \DateTimeZone(nexus_display_timezone_name()); $value = trim((string) $value); if ($value === '') { return (new \DateTimeImmutable('now', $timezone))->format('Y-m-d H:i:s'); diff --git a/modules/boersenchecker/src/Support/InstrumentPage.php b/modules/boersenchecker/src/Support/InstrumentPage.php index 4c599ad..4a65ccb 100644 --- a/modules/boersenchecker/src/Support/InstrumentPage.php +++ b/modules/boersenchecker/src/Support/InstrumentPage.php @@ -298,7 +298,7 @@ final class InstrumentPage private function normalizeDateTimeLocal(?string $value): string { - $timezone = new \DateTimeZone('Europe/Berlin'); + $timezone = new \DateTimeZone(nexus_display_timezone_name()); $value = trim((string) $value); if ($value === '') { return (new \DateTimeImmutable('now', $timezone))->format('Y-m-d H:i:s'); diff --git a/modules/fx-rates/bootstrap.php b/modules/fx-rates/bootstrap.php index a16e237..ce1613f 100644 --- a/modules/fx-rates/bootstrap.php +++ b/modules/fx-rates/bootstrap.php @@ -68,7 +68,7 @@ $mm->registerFunction($moduleName, 'settings', static function (): array { 'preferred_currencies' => $preferredCurrencies, 'currency_catalog' => $currencyCatalog, 'currency_catalog_synced_at' => trim((string) ($saved['currency_catalog_synced_at'] ?? '')), - 'schedule_timezone' => trim((string) ($saved['schedule_timezone'] ?? 'Europe/Berlin')) ?: 'Europe/Berlin', + 'schedule_timezone' => trim((string) ($saved['schedule_timezone'] ?? nexus_cron_timezone_name())) ?: nexus_cron_timezone_name(), ]; }); diff --git a/modules/fx-rates/src/Domain/FxRatesService.php b/modules/fx-rates/src/Domain/FxRatesService.php index 1f87a10..76650bd 100644 --- a/modules/fx-rates/src/Domain/FxRatesService.php +++ b/modules/fx-rates/src/Domain/FxRatesService.php @@ -991,11 +991,11 @@ final class FxRatesService private function scheduleTimezone(): DateTimeZone { - $timezone = trim((string) ($this->settings['schedule_timezone'] ?? 'Europe/Berlin')); + $timezone = trim((string) ($this->settings['schedule_timezone'] ?? nexus_cron_timezone_name())); try { return new DateTimeZone($timezone); } catch (\Throwable) { - return new DateTimeZone('Europe/Berlin'); + return new DateTimeZone(nexus_cron_timezone_name()); } } diff --git a/modules/mining-checker/bootstrap.php b/modules/mining-checker/bootstrap.php index e636ae3..f4eadd9 100644 --- a/modules/mining-checker/bootstrap.php +++ b/modules/mining-checker/bootstrap.php @@ -34,9 +34,9 @@ $mm->registerFunction($moduleName, 'runtime_settings', static function (): array $repository = new MiningRepository($pdo, $config->tablePrefix(), null, $ownerSub); $settings = $repository->getSettings($projectKey) ?? []; - $displayTimezone = trim((string) ($settings['display_timezone'] ?? 'Europe/Berlin')); + $displayTimezone = trim((string) ($settings['display_timezone'] ?? nexus_display_timezone_name())); if ($displayTimezone === '') { - $displayTimezone = 'Europe/Berlin'; + $displayTimezone = nexus_display_timezone_name(); } $baselineMeasuredAt = trim((string) ($settings['baseline_measured_at'] ?? '')); @@ -72,9 +72,9 @@ $mm->registerFunction($moduleName, 'save_runtime_settings', static function (arr $repository->ensureProject($projectKey, strtoupper(str_replace('-', ' ', $projectKey))); $existing = $repository->getSettings($projectKey) ?? []; - $displayTimezone = trim((string) ($existing['display_timezone'] ?? 'Europe/Berlin')); + $displayTimezone = trim((string) ($existing['display_timezone'] ?? nexus_display_timezone_name())); if ($displayTimezone === '') { - $displayTimezone = 'Europe/Berlin'; + $displayTimezone = nexus_display_timezone_name(); } try { diff --git a/modules/mining-checker/src/Api/Router.php b/modules/mining-checker/src/Api/Router.php index 5d95a22..629d247 100644 --- a/modules/mining-checker/src/Api/Router.php +++ b/modules/mining-checker/src/Api/Router.php @@ -326,7 +326,7 @@ final class Router 'daily_cost_currency' => 'EUR', 'report_currency' => 'EUR', 'crypto_currency' => 'DOGE', - 'display_timezone' => 'Europe/Berlin', + 'display_timezone' => nexus_display_timezone_name(), 'fx_max_age_hours' => self::FX_FETCH_MAX_AGE_HOURS, 'module_theme_mode' => 'inherit', 'module_theme_accent' => 'teal', @@ -525,7 +525,7 @@ final class Router 'daily_cost_currency' => $backup['settings']['daily_cost_currency'], 'report_currency' => $backup['settings']['report_currency'] ?? 'EUR', 'crypto_currency' => $backup['settings']['crypto_currency'] ?? 'DOGE', - 'display_timezone' => $backup['settings']['display_timezone'] ?? 'Europe/Berlin', + 'display_timezone' => $backup['settings']['display_timezone'] ?? nexus_display_timezone_name(), 'fx_max_age_hours' => self::FX_FETCH_MAX_AGE_HOURS, 'module_theme_mode' => $backup['settings']['module_theme_mode'] ?? 'inherit', 'module_theme_accent' => $backup['settings']['module_theme_accent'] ?? 'teal', @@ -1158,13 +1158,13 @@ final class Router 'daily_cost_currency' => 'EUR', 'report_currency' => 'EUR', 'crypto_currency' => 'DOGE', - 'display_timezone' => 'Europe/Berlin', + 'display_timezone' => nexus_display_timezone_name(), 'module_theme_mode' => 'inherit', 'module_theme_accent' => 'teal', 'preferred_currencies' => $this->preferredCurrencies(), ]; if (!$this->isValidTimezone((string) ($base['display_timezone'] ?? ''))) { - $base['display_timezone'] = 'Europe/Berlin'; + $base['display_timezone'] = nexus_display_timezone_name(); } if (!in_array((string) ($base['module_theme_mode'] ?? ''), ['inherit', 'custom'], true)) { $base['module_theme_mode'] = 'inherit'; @@ -1187,7 +1187,7 @@ final class Router { $existingSettings = $this->repository()->getSettings($projectKey) ?? []; $displayTimezone = $this->requiredTimezone( - $input['display_timezone'] ?? ($existingSettings['display_timezone'] ?? 'Europe/Berlin'), + $input['display_timezone'] ?? ($existingSettings['display_timezone'] ?? nexus_display_timezone_name()), 'display_timezone' ); $settings = [ @@ -2069,7 +2069,7 @@ final class Router { $settings = $this->repository()->getSettings($projectKey); $timezone = is_array($settings) ? (string) ($settings['display_timezone'] ?? '') : ''; - return $this->isValidTimezone($timezone) ? $timezone : 'Europe/Berlin'; + return $this->isValidTimezone($timezone) ? $timezone : nexus_display_timezone_name(); } private function utcTimezone(): \DateTimeZone diff --git a/modules/mining-checker/src/Infrastructure/MiningRepository.php b/modules/mining-checker/src/Infrastructure/MiningRepository.php index a4267bd..78caae7 100644 --- a/modules/mining-checker/src/Infrastructure/MiningRepository.php +++ b/modules/mining-checker/src/Infrastructure/MiningRepository.php @@ -103,7 +103,7 @@ final class MiningRepository 'daily_cost_currency' => $settings['daily_cost_currency'], 'report_currency' => $settings['report_currency'] ?? 'EUR', 'crypto_currency' => $settings['crypto_currency'] ?? 'DOGE', - 'display_timezone' => $settings['display_timezone'] ?? 'Europe/Berlin', + 'display_timezone' => $settings['display_timezone'] ?? nexus_display_timezone_name(), 'fx_max_age_hours' => $settings['fx_max_age_hours'] ?? 3, 'module_theme_mode' => $settings['module_theme_mode'] ?? 'inherit', 'module_theme_accent' => $settings['module_theme_accent'] ?? 'teal',