diff --git a/README.md b/README.md index cc6f67b..716eece 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,65 @@ # PHP_AdminTool_Projekt -Admin Tool Projekt für das Fach PHP ---- -Ich (@blaerf) habe mich mal hingesetzt und einen Windows Server 2025 als VM aufgesetzt. Darauf habe ich die Active Directory und IIS Rolle installiert. -Zudem habe ich alles so konfiguriert, dass wir PHP mit IIS nutzen können und per LDAPS auf das AD per PHP zugreifen können. +Dieses Repository enthält das gemeinsame PHP AdminTool, das auf IIS unter Windows betrieben wird und über LDAP/AD, SNMP und PowerShell administrative Aufgaben ermöglicht. Das Projekt wird von mehreren Entwicklern gepflegt und folgt einem klar definierten Workflow, um Qualität, Stabilität und Nachvollziehbarkeit sicherzustellen. -Ich habe mir auch noch Gedanken zum Git-Workflow gemacht und es nieder geschrieben. So können wir sauber und sicher arbeiten. -Eine Ordner Struktur habe ich mir auch überlegt und schon mal angelegt. Alle Infos habe ich soweit im Wiki zusammen getragen. -Meine Tests habe ich, wie im Git-workflow beschrieben, in ein eigenes branch (structure/first-structure) gepackt. +Ein zentraler Bestandteil der Arbeit mit diesem Repository ist der verbindliche Gitea-Workflow im Wiki. -Bitte auch das Wiki beachten und durchlesen! + +**Hinweis:** Der [Gitea-Workflow](https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/wiki) ist zwingend zu lesen und einzuhalten. Er legt fest, wie Branches erstellt, gemerged und getestet werden. --- -## Aufgaben: ## +## Überblick + +Das Projekt dient als webbasiertes Administrationswerkzeug und umfasst unter anderem: + +- LDAP/AD‑Anbindung (LDAPS) +- Benutzer- und Gruppenverwaltung +- SNMP‑Auswertungen +- PowerShell‑Integration +- Weboberfläche zur zentralen Administration + +Die Anwendung läuft in zwei Umgebungen: + +- Produktion (`main`): https://itfa.schraubenfuzzi.de +- Test (`develop`): https://test.itfa.schraubenfuzzi.de + +--- + +## Ordnerstruktur (Auszug) + +- `app/` – Anwendungscode +- `public/` – Webroot +- `config/` – Konfigurationen für LDAP, Systemvariablen usw. +- `scripts/` – Hilfs- und Bereitstellungsskripte +- `docs/` – interne Dokumente +- `.gitignore` – ausgeschlossene Dateien + +--- + +## Workflow + +Die Entwicklung folgt einem strengen, verbindlichen Ablauf: + +1. **Feature‑Branch erstellen** (`feature/name`) +2. Feature entwickeln, committen, pushen +3. **PR nach `develop`** für Tests auf der Testinstanz + - wird vom Autor selbst gemerged + - Branch bleibt bestehen +4. Nach bestandenem Test: **PR nach `main`** + - mit Review + - Merge per Squash + - danach wird der Feature‑Branch gelöscht + +Details stehen im Wiki. + +**Wichtiger Hinweis:** +Der komplette Ablauf ist im [Gitea-Workflow](https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/wiki) beschrieben und muss befolgt werden. + +--- + +## Aufgaben + | Aufgabe | Benutzer | | :---- | :---- | | Benutzer und Gruppen über LDAP anzeigen | Jens E (@blaerf), Stefan W (@viperion) | @@ -23,4 +69,32 @@ Bitte auch das Wiki beachten und durchlesen! | UI/UX anpassen | Yasin B (@Muchentuchen), Alexander M (@Alexander), Torsten J (@tojacobs) | --- -## Infos gibt es im [Wiki](https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/wiki) \ No newline at end of file + +## Dokumentation + +Alle weiteren Informationen wie Regeln, technische Details, Konzepte und Anleitungen befinden sich im Wiki: + +`Wiki → Gitea‑Workflow` +`Wiki → Implementierung / LDAP / IIS / Setup` + +Dieser Bereich muss von allen Entwicklern gelesen werden, bevor am Projekt gearbeitet wird. + +--- + +## Mitwirken + +Wer etwas ändern oder erweitern möchte: + +- Branch vom aktuellen Stand erstellen +- Entwickeln, Committen, Pushen +- PR zuerst nach `develop`, später nach bestandenen Tests nach `main` +- Reviewer zuweisen (für `main`) + +Nur tested und reviewed Code gelangt in die Produktion. + +--- + +## Ziel + +Das AdminTool soll eine wartbare, erweiterbare und zuverlässige Verwaltungsoberfläche bieten, die zentrale Aufgaben des AD‑Umfelds über eine moderne Weboberfläche ermöglicht. + diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php index 0785f4d..14f9e23 100644 --- a/app/Controllers/AuthController.php +++ b/app/Controllers/AuthController.php @@ -12,6 +12,11 @@ use App\Services\Ldap\LdapAuthService; * - Login-Formular anzeigen * - Login-Daten verarbeiten (Authentifizierung gegen LDAP/AD) * - Logout durchführen + * + * NEU: + * - Statt direkt HTML auszugeben oder header()-Redirects zu setzen, + * liefert der Controller "View-Results" zurück, die von index.php + * und einem zentralen Layout verarbeitet werden. */ class AuthController { @@ -39,33 +44,36 @@ class AuthController /** * Zeigt das Login-Formular an. * Optional kann eine Fehlermeldung übergeben werden, die in der View dargestellt wird. + * + * @return array View-Result für das zentrale Layout */ - public function showLoginForm(?string $errorMessage = null): void + public function showLoginForm(?string $errorMessage = null): array { // Pfad zur Login-View (Template-Datei) ermitteln. $viewPath = __DIR__ . '/../../public/views/login.php'; - // Variable für die View vorbereiten (wird in der eingebundenen Datei verwendet). - $error = $errorMessage; - - // Falls die View-Datei (noch) nicht existiert, einen Fallback-HTML-Output verwenden. - if (file_exists($viewPath) === false) { - $this->renderInlineLogin($error); - return; - } - - // View-Datei einbinden. Variablen aus dieser Methode (z. B. $error) sind dort verfügbar. - require $viewPath; + // Wichtig: Die View erwartet aktuell die Variable $error. + return [ + 'view' => $viewPath, + 'data' => [ + 'error' => $errorMessage, + ], + 'pageTitle' => 'Login', + // Beim Login ist typischerweise kein Menüpunkt aktiv. + 'activeMenu' => null, + ]; } /** * Verarbeitet das Login-Formular: * - Liest Benutzername und Passwort aus $_POST * - Ruft den LdapAuthService zur Authentifizierung auf - * - Setzt bei Erfolg die Session und leitet zum Dashboard weiter - * - Zeigt bei Fehlern erneut das Login-Formular mit Fehlermeldung an + * - Liefert bei Erfolg ein Redirect-Result zum Dashboard + * - Liefert bei Fehlern ein View-Result für das Login-Formular mit Fehlermeldung + * + * @return array View-Result ODER Redirect-Result */ - public function processLogin(): void + public function processLogin(): array { // Formulardaten aus dem POST-Request lesen. $username = trim($_POST['username'] ?? ''); @@ -79,14 +87,14 @@ class AuthController } catch (\Throwable $exception) { // Technischer Fehler (z. B. LDAP-Server nicht erreichbar, falsche Konfiguration). // In diesem Fall wird eine technische Fehlermeldung im Login-Formular angezeigt. - $this->showLoginForm('Technischer Fehler bei der Anmeldung: ' . $exception->getMessage()); - return; + return $this->showLoginForm( + 'Technischer Fehler bei der Anmeldung: ' . $exception->getMessage() + ); } // Fachlich fehlgeschlagene Anmeldung (z. B. falsches Passwort). if ($authenticated === false) { - $this->showLoginForm('Benutzername oder Passwort ist ungültig.'); - return; + return $this->showLoginForm('Benutzername oder Passwort ist ungültig.'); } // Ab hier ist die Anmeldung erfolgreich. @@ -96,22 +104,27 @@ class AuthController $sessionKey = $this->config['security']['session_key_user'] ?? 'admin_user'; // Benutzerinformationen in der Session hinterlegen. - // Diese Daten werden später von requireLogin() ausgewertet. + // Diese Daten werden später von requireLogin() bzw. im Layout ausgewertet. $_SESSION[$sessionKey] = [ 'username' => $username, 'login_at' => date('c'), // ISO-8601 Datum/Zeit der Anmeldung ]; // Nach erfolgreicher Anmeldung zum Dashboard weiterleiten. - header('Location: index.php?route=dashboard'); - exit; + // Kein direkter header()-Aufruf, sondern ein Redirect-Result + // für die zentrale Steuerung in index.php. + return [ + 'redirect' => 'index.php?route=dashboard', + ]; } /** * Meldet den aktuell eingeloggten Benutzer ab, indem der entsprechende Session-Eintrag entfernt wird, - * und leitet anschließend zurück auf die Login-Seite. + * und liefert anschließend ein Redirect-Result zurück auf die Login-Seite. + * + * @return array Redirect-Result */ - public function logout(): void + public function logout(): array { // Session-Key für den eingeloggten Benutzer aus der Konfiguration lesen. $sessionKey = $this->config['security']['session_key_user'] ?? 'admin_user'; @@ -119,49 +132,9 @@ class AuthController // Eintrag aus der Session entfernen → Benutzer gilt als ausgeloggt. unset($_SESSION[$sessionKey]); - // Zur Login-Seite zurückleiten. - header('Location: index.php?route=login'); - exit; - } - - /** - * Fallback-Ausgabe für das Login-Formular, falls noch keine separate View-Datei existiert. - * Gibt direkt HTML aus (inline-Template). - */ - private function renderInlineLogin(?string $errorMessage): void - { - ?> - - - - - AD Admin Tool – Login - - -

AD Admin Tool – Login

- - - -

- - - -
-
- - -
- -
- - -
- - -
- - - - 'index.php?route=login', + ]; } } diff --git a/app/Controllers/DashboardController.php b/app/Controllers/DashboardController.php index 089281e..49775ba 100644 --- a/app/Controllers/DashboardController.php +++ b/app/Controllers/DashboardController.php @@ -12,6 +12,9 @@ use App\Services\Snmp\SnmpServerStatusService; * Zuständig für: * - Abrufen des Serverstatus (über SnmpServerStatusService) * - Auswählen und Rendern der Dashboard-View + * + * NEU: + * - Gibt ein View-Result zurück, das von index.php + Layout gerendert wird. */ class DashboardController { @@ -42,8 +45,10 @@ class DashboardController /** * Zeigt das Dashboard an. * Holt die Serverstatus-Daten aus dem SnmpServerStatusService und übergibt sie an die View. + * + * @return array View-Result für das zentrale Layout */ - public function show(): void + public function show(): array { // Serverstatus über den SNMP-Service ermitteln. // In der aktuellen Version liefert der Service noch Demo-Daten. @@ -52,69 +57,15 @@ class DashboardController // Pfad zur Dashboard-View (Template-Datei) ermitteln. $viewPath = __DIR__ . '/../../public/views/dashboard.php'; - // Falls die View-Datei (noch) nicht existiert, Fallback-HTML direkt aus dem Controller ausgeben. - if (file_exists($viewPath) === false) { - $this->renderInlineDashboard($serverStatus); - return; - } - - // View-Datei einbinden. Die Variable $serverStatus steht in der View zur Verfügung. - require $viewPath; - } - - /** - * Fallback-Dashboard-Ausgabe direkt aus dem Controller (ohne separate View-Datei). - * Nur als Notlösung gedacht, falls die eigentliche View noch nicht vorhanden ist. - * - * @param array $serverStatus Serverstatus-Daten (Hostname, CPU%, RAM%, etc.) - */ - private function renderInlineDashboard(array $serverStatus): void - { - ?> - - - - - AD Admin Tool – Dashboard - - -

Dashboard

- -

Serverstatus

-
    -
  • - Hostname: - -
  • -
  • - Betriebssystem: - -
  • -
  • - Uptime: - -
  • -
  • - CPU-Auslastung: - % -
  • -
  • - RAM-Auslastung: - % -
  • -
  • - Datenträger C: - % -
  • -
  • - Letzte Aktualisierung: - -
  • -
- -

Logout

- - - $viewPath, + 'data' => [ + // Die View erwartet aktuell $serverStatus. + 'serverStatus' => $serverStatus, + ], + 'pageTitle' => 'Dashboard', + // In der Sidebar soll der Dashboard-Menüpunkt aktiv sein. + 'activeMenu' => 'dashboard', + ]; } } diff --git a/app/Controllers/UserManagementController.php b/app/Controllers/UserManagementController.php index 8b20a81..67d4e4f 100644 --- a/app/Controllers/UserManagementController.php +++ b/app/Controllers/UserManagementController.php @@ -18,6 +18,9 @@ use App\Services\Ldap\LdapDirectoryService; * WICHTIG: * - Es werden aktuell nur Daten angezeigt (Read-only). * - Es findet keine Änderung im Active Directory statt. + * + * NEU: + * - Gibt ein View-Result-Array zurück, das von index.php + Layout gerendert wird. */ class UserManagementController { @@ -45,8 +48,10 @@ class UserManagementController /** * Zeigt Benutzer- und Gruppenliste an. * Wird typischerweise über die Route "users" (index.php?route=users) aufgerufen. + * + * @return array View-Result für das zentrale Layout */ - public function show(): void + public function show(): array { // Standardwerte für die View-Variablen vorbereiten. $error = null; @@ -66,88 +71,16 @@ class UserManagementController // Pfad zur eigentlichen View-Datei bestimmen. $viewPath = __DIR__ . '/../../public/views/users.php'; - // Falls die View-Datei (noch) nicht existiert, Fallback-Ausgabe verwenden. - if (file_exists($viewPath) === false) { - $this->renderInline($users, $groups, $error); - return; - } - - // Variablen $users, $groups, $error stehen in der View zur Verfügung, - // weil sie im aktuellen Scope definiert sind. - require $viewPath; - } - - /** - * Fallback-Ausgabe, falls noch keine View-Datei existiert. - * - * @param array> $users Liste der Benutzer-Datensätze - * @param array> $groups Liste der Gruppen-Datensätze - * @param string|null $error Fehlermeldung (falls vorhanden) - */ - private function renderInline(array $users, array $groups, ?string $error): void - { - ?> - - - - - AD Admin Tool – Benutzer & Gruppen - - -

Benutzer & Gruppen

- - - -

- - -

Benutzer

- - - - - - - - - - - - - - - - - -
Benutzername (sAMAccountName)AnzeigenameE-Mail
- -

Gruppen

- - - - - - - - - - - - - - - - - -
Gruppenname (sAMAccountName)CNBeschreibung
- -

- Zurück zum Dashboard | - Logout -

- - - - $viewPath, + 'data' => [ + // Die View erwartet aktuell $users, $groups, $error. + 'users' => $users, + 'groups' => $groups, + 'error' => $error, + ], + 'pageTitle' => 'Benutzer & Gruppen', + 'activeMenu' => 'users', + ]; } } diff --git a/app/Services/Snmp/SnmpServerStatusService.php b/app/Services/Snmp/SnmpServerStatusService.php index 13150ad..56ae54a 100644 --- a/app/Services/Snmp/SnmpServerStatusService.php +++ b/app/Services/Snmp/SnmpServerStatusService.php @@ -29,38 +29,134 @@ class SnmpServerStatusService // SNMP-Konfiguration in der Instanz speichern. $this->config = $snmpConfig; } - /** * Liefert den aktuellen Serverstatus zurück. * - * In dieser Version werden Demo-Daten auf Basis der Konfiguration erzeugt. - * Später können hier echte SNMP-Queries (z. B. über die PHP-SNMP-Extension) verwendet werden. - * * @return array Assoziatives Array mit Statuswerten (Hostname, CPU%, RAM%, etc.) * - * @throws RuntimeException wenn die SNMP-Konfiguration unvollständig ist (z. B. host fehlt) + * @throws RuntimeException wenn die SNMP-Konfiguration unvollständig ist oder Abfragen fehlschlagen */ public function getServerStatus(): array { - // Hostnamen aus der SNMP-Konfiguration lesen. - $host = (string)($this->config['host'] ?? ''); + // --- 1. Konfiguration auslesen --- + $host = (string)($this->config['host'] ?? '127.0.0.1'); + $community = (string)($this->config['community'] ?? ''); + $oids = $this->config['oids'] ?? []; + $timeout = (int)($this->config['timeout'] ?? 1) * 1_000_000; // in Mikrosekunden + $retries = (int)($this->config['retries'] ?? 1); - // Ohne Host ist keine sinnvolle Abfrage möglich -> Konfigurationsfehler. + // --- Prüfungen für essentielle Konfig-Werte --- if ($host === '') { throw new RuntimeException('SNMP-Konfiguration ist unvollständig (host fehlt).'); } + if ($community === '') { + throw new RuntimeException('SNMP-Konfiguration ist unvollständig (community fehlt).'); + } + if (empty($oids)) { + throw new RuntimeException('SNMP-Konfiguration ist unvollständig (oids fehlen).'); + } + + // Helper-Funktion zum Bereinigen von SNMP-Antworten (z.B. "INTEGER: 123" -> 123) + $cleanSnmpValue = fn($v) => (int)filter_var($v, FILTER_SANITIZE_NUMBER_INT); - // TODO: Später hier echte SNMP-Abfragen einbauen (z. B. snmp2_get/snmp2_walk). - // Die OIDs können aus $this->config['oids'] gelesen werden. - // Für den Anfang verwenden wir dieselben Demo-Werte wie vorher im DashboardController. + // --- 2. Uptime abfragen (war vorher nicht implementiert) --- + $uptimeResult = snmpget($host, $community, $oids['uptime'], $timeout, $retries); + if ($uptimeResult === false) { + throw new RuntimeException("SNMP Uptime GET fehlgeschlagen."); + } + // Uptime (timeticks) in ein lesbares Format umwandeln (optional, hier als String) + // Format ist oft "Timeticks: (12345678) 1 day, 10:17:36.78" + // Wir extrahieren den Teil in Klammern (Hundertstelsekunden) + preg_match('/\((.*?)\)/', $uptimeResult, $matches); + $uptimeTicks = (int)($matches[1] ?? 0); + $uptimeSeconds = $uptimeTicks / 100; + $uptimeFormatted = sprintf( + '%d Tage, %02d:%02d:%02d', + floor($uptimeSeconds / 86400), + floor(($uptimeSeconds % 86400) / 3600), + floor(($uptimeSeconds % 3600) / 60), + $uptimeSeconds % 60 + ); + + + // --- 3. CPU --- + $cpuValues = snmpwalk($host, $community, $oids['cpu_table'], $timeout, $retries); + + if (!is_array($cpuValues) || empty($cpuValues)) { + throw new RuntimeException("SNMP CPU WALK fehlgeschlagen."); + } + + $cpuValues = array_map($cleanSnmpValue, $cpuValues); + $cpuAvg = array_sum($cpuValues) / count($cpuValues); + + + // --- 4. Memory --- + $memTotalResult = snmpget($host, $community, $oids['mem_size'], $timeout, $retries); + if($memTotalResult === false) { + throw new RuntimeException("SNMP MemTotal GET fehlgeschlagen."); + } + + // memTotal in Bytes berechnen (korrigierte Reihenfolge von filter/cast) + $memTotal = $cleanSnmpValue($memTotalResult) * 1024; // KB -> Bytes + + // Storage-Tabelle (RAM + Disks) + $descr = snmpwalk($host, $community, $oids['storage_descr'], $timeout, $retries); + $units = snmpwalk($host, $community, $oids['storage_units'], $timeout, $retries); + $size = snmpwalk($host, $community, $oids['storage_size'], $timeout, $retries); + $used = snmpwalk($host, $community, $oids['storage_used'], $timeout, $retries); + + if ($descr === false || $units === false || $size === false || $used === false) { + throw new RuntimeException("SNMP Storage WALK fehlgeschlagen."); + } + + // Werte bereinigen + // Die SNMP-Antwort enthält "STRING: " und Anführungszeichen, die wir entfernen müssen. + $descr = array_map(fn($v) => trim(str_ireplace('STRING:', '', $v), ' "'), $descr); + + $units = array_map($cleanSnmpValue, $units); // Ints + $size = array_map($cleanSnmpValue, $size); // Ints + $used = array_map($cleanSnmpValue, $used); // Ints + + + + // RAM + $ramIndex = array_search("Physical Memory", $descr); + if ($ramIndex === false) { + throw new RuntimeException("Konnte 'Physical Memory' in der SNMP Storage-Tabelle nicht finden."); + } + + $ramUsedBytes = $units[$ramIndex] * $used[$ramIndex]; + $ramPercent = ($memTotal > 0) ? ($ramUsedBytes / $memTotal) * 100 : 0; + + + // --- 5. Disk C: --- + $cIndex = false; + foreach ($descr as $index => $description) { + // str_starts_with prüft, ob der String mit C:\ beginnt + if (str_starts_with($description, 'C:\\')) { + $cIndex = $index; + break; + } + } + + if ($cIndex === false) { + throw new RuntimeException("Konnte Laufwerk 'C:\' in der SNMP Storage-Tabelle nicht finden."); + } + + $cUsed = $units[$cIndex] * $used[$cIndex]; + $cTotal = $units[$cIndex] * $size[$cIndex]; + + $diskCPercent = ($cTotal > 0) ? ($cUsed / $cTotal) * 100 : 0; + + // --- 6. Status-Array zusammenbauen --- $status = [ 'hostname' => $host, - 'os' => 'Windows Server 2025 Datacenter', - 'uptime' => '3 Tage 12 Stunden', - 'cpu_usage' => 23, // CPU-Auslastung in Prozent (Demo) - 'memory_usage' => 62, // RAM-Auslastung in Prozent (Demo) - 'disk_usage_c' => 71, // Datenträger C in Prozent (Demo) - 'last_update' => date('d.m.Y H:i:s'), // Zeitpunkt der letzten Aktualisierung + 'os' => 'Windows Server 2025 Datacenter', // TODO: OS dynamisch abfragen + 'uptime' => $uptimeFormatted, // Fehlende Variable hinzugefügt + 'cpu_usage' => round($cpuAvg), + 'memory_usage' => round($ramPercent), + 'disk_usage_c' => round($diskCPercent), + 'last_update' => date('d.m.Y H:i:s'), ]; return $status; diff --git a/config/config.php b/config/config.php index a17c805..a84872e 100644 --- a/config/config.php +++ b/config/config.php @@ -29,17 +29,24 @@ return [ ], 'snmp' => [ - 'host' => 'itfa-proj-srv.itfa-proj-dom.local', + 'host' => '127.0.0.1', 'community' => 'public_ro', // später: sinnvoller Community-String 'timeout' => 2, // Sekunden 'retries' => 1, // Anzahl Wiederholungen // Platzhalter für OIDs – später können wir die auf echte Werte setzen 'oids' => [ - 'uptime' => '1.3.6.1.2.1.1.3.0', - 'cpu_usage' => '1.3.6.1.4.1.example.cpu.0', - 'memory_usage' => '1.3.6.1.4.1.example.memory.0', - 'disk_c' => '1.3.6.1.4.1.example.diskc.0', - ], + 'uptime' => '1.3.6.1.2.1.1.3.0', + + // CPU pro Kern + 'cpu_table' => '1.3.6.1.2.1.25.3.3.1.2', + + // Memory + 'mem_size' => '1.3.6.1.2.1.25.2.2.0', + 'storage_descr' => '1.3.6.1.2.1.25.2.3.1.3', + 'storage_units' => '1.3.6.1.2.1.25.2.3.1.4', + 'storage_size' => '1.3.6.1.2.1.25.2.3.1.5', + 'storage_used' => '1.3.6.1.2.1.25.2.3.1.6', + ], ], ]; diff --git a/public/index.php b/public/index.php index f84856c..f56f975 100644 --- a/public/index.php +++ b/public/index.php @@ -13,9 +13,8 @@ session_start(); * im Namespace "App\..." verwendet wird. */ spl_autoload_register( - // Anonyme Funktion, die den Klassennamen prüft und den Dateipfad zur Klasse ermittelt. static function (string $class): void { - $prefix = 'App\\'; + $prefix = 'App\\'; $baseDir = __DIR__ . '/../app/'; $len = strlen($prefix); @@ -24,7 +23,7 @@ spl_autoload_register( } $relativeClass = substr($class, $len); - $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php'; + $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php'; if (file_exists($file) === true) { require $file; @@ -32,6 +31,8 @@ spl_autoload_register( } ); +require __DIR__ . '/views/layout.php'; + // Pfad zur Konfigurationsdatei prüfen und Konfiguration laden $configPath = __DIR__ . '/../config/config.php'; if (file_exists($configPath) === false) { @@ -49,7 +50,12 @@ use App\Controllers\AuthController; use App\Controllers\DashboardController; use App\Controllers\UserManagementController; -// Hilfsfunktion für geschützte Routen +/** + * Hilfsfunktion: Prüft, ob ein Benutzer eingeloggt ist. + * Wenn nicht, wird auf die Login-Seite umgeleitet. + * + * @param array $config + */ function requireLogin(array $config): void { // session_key_user aus dem Config-Array lesen. Wenn nicht gesetzt oder null, Standard "admin_user" verwenden. @@ -63,44 +69,77 @@ function requireLogin(array $config): void } } -// Route aus dem GET-Parameter lesen. Wenn nicht gesetzt, Standardroute "login" verwenden. -$route = $_GET['route'] ?? 'login'; +/** + * Verarbeitet ein View-Result oder Redirect-Result. + * + * @param array|null $result + */ +function handleResult(?array $result): void +{ + if ($result === null) { + return; + } -// Neue Instanz der Klasse AuthController erstellen (wird bei Bedarf über den Autoloader geladen). + if (isset($result['redirect']) === true) { + header('Location: ' . (string)$result['redirect']); + exit; + } + + $contentView = (string)($result['view'] ?? ''); + $viewData = (array)($result['data'] ?? []); + $pageTitle = (string)($result['pageTitle'] ?? ''); + $activeMenu = $result['activeMenu'] ?? null; + + if ($contentView === '' || file_exists($contentView) === false) { + http_response_code(500); + echo 'Interner Fehler: Content-View wurde nicht gefunden.'; + exit; + } + + // Hier rufen wir jetzt die Layout-Funktion aus layout.php auf + renderLayout($contentView, $viewData, $pageTitle, is_string($activeMenu) ? $activeMenu : null); +} + +// Controller instanziieren $authController = new AuthController($config); - -// Neue Instanz der Klasse DashboardController erstellen (wird bei Bedarf über den Autoloader geladen). $dashboardController = new DashboardController($config); - -// Neue Instanz der Klasse UserManagmentController erstellen (wird bei Bedarf über den Autoloader geladen). $userManagementController = new UserManagementController($config); -// Anhand des Routing-Ziels (route) entscheiden, welcher Code ausgeführt wird. +// Route bestimmen +$route = $_GET['route'] ?? 'login'; + switch ($route) { case 'login': - $authController->showLoginForm(); + // Login-Formular anzeigen (ggf. mit Fehlermeldung) + $result = $authController->showLoginForm(); + handleResult($result); break; case 'login.submit': if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + // Falscher HTTP-Verb: einfach zurück zum Login header('Location: index.php?route=login'); exit; } - $authController->processLogin(); + $result = $authController->processLogin(); + handleResult($result); break; case 'logout': - $authController->logout(); + $result = $authController->logout(); + handleResult($result); break; case 'dashboard': requireLogin($config); - $dashboardController->show(); + $result = $dashboardController->show(); + handleResult($result); break; case 'users': requireLogin($config); - $userManagementController->show(); + $result = $userManagementController->show(); + handleResult($result); break; default: @@ -108,3 +147,5 @@ switch ($route) { echo 'Route nicht gefunden.'; break; } + + diff --git a/public/views/dashboard.php b/public/views/dashboard.php index 3164c36..0fe1dad 100644 --- a/public/views/dashboard.php +++ b/public/views/dashboard.php @@ -1,792 +1,100 @@ $serverStatus */ ?> - - - +
+

Server-Dashboard

+
- - - - - +
- SB Admin 2 - Dashboard - - - - - - - - - - - - - -
- - - - - - -
- - -
- - - - - - -
- - -
-

Dashboard

- Generate Report -
- - -
- - -
-
-
-
-
-
CPU -
-
-
-
%
-
-
-
-
aria-valuenow= aria-valuemin="0" - aria-valuemax="100">
-
-
-
-
-
- -
-
-
-
-
- - -
-
-
-
-
-
RAM -
-
-
-
%
-
-
-
-
aria-valuenow= aria-valuemin="0" - aria-valuemax="100">
-
-
-
-
-
- -
-
-
-
-
- - -
-
-
-
-
-
HDD (C:) -
-
-
-
%
-
-
-
-
aria-valuenow= aria-valuemin="0" - aria-valuemax="100">
-
-
-
-
-
- -
-
-
-
-
- - -
-
-
-
-
-
- Pending Requests
-
18
-
-
- -
-
-
-
+
+
- - - -
- - -
-
- -
-
Earnings Overview
- -
- -
-
- -
-
-
-
- - -
-
- -
-
Revenue Sources
- -
- -
-
- -
-
- - Direct - - - Social - - - Referral - -
-
-
-
+
+
- - -
- - -
- - -
-
-
Projects
-
-
-

Server Migration 20%

-
-
-
-

Sales Tracking 40%

-
-
-
-

Customer Database 60%

-
-
-
-

Payout Details 80%

-
-
-
-

Account Setup Complete!

-
-
-
-
-
- - -
-
-
-
- Primary -
#4e73df
-
-
-
-
-
-
- Success -
#1cc88a
-
-
-
-
-
-
- Info -
#36b9cc
-
-
-
-
-
-
- Warning -
#f6c23e
-
-
-
-
-
-
- Danger -
#e74a3b
-
-
-
-
-
-
- Secondary -
#858796
-
-
-
-
-
-
- Light -
#f8f9fc
-
-
-
-
-
-
- Dark -
#5a5c69
-
-
-
-
- -
- -
- - -
-
-
Illustrations
-
-
-
- ... -
-

Add some quality, svg illustrations to your project courtesy of unDraw, a - constantly updated collection of beautiful svg images that you can use - completely free and without attribution!

- Browse Illustrations on - unDraw → -
-
- - -
-
-
Development Approach
-
-
-

SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce - CSS bloat and poor page performance. Custom CSS classes are used to create - custom components and custom utility classes.

-

Before working with this theme, you should become familiar with the - Bootstrap framework, especially the utility classes.

-
-
- -
-
- -
- - -
- - - -
-
- -
-
- - -
- - -
- - - - - - - - - - - - + +
+
+
+
+
+
+ Betriebssystem +
+
+ +
+
+
+ +
+
+
+
+
- - + +
+
+
+
+
+
+ CPU-Auslastung +
+
+ % +
+
+
+ +
+
+
+
+
- - + +
+
+
+
+
+
+ RAM-Auslastung +
+
+ % +
+
+
+ +
+
+
+
+
- - +
- - - - - - - \ No newline at end of file + diff --git a/public/views/layout.php b/public/views/layout.php new file mode 100644 index 0000000..b7be8f2 --- /dev/null +++ b/public/views/layout.php @@ -0,0 +1,43 @@ + $viewData Daten-Array für die View + * - string $pageTitle Seitentitel + * - string|null $activeMenu Kennung für die Sidebar (z. B. 'dashboard' oder 'users') + */ + +// Daten-Array in einzelne Variablen entpacken, +// sodass die Content-Views direkt mit $users, $groups, $serverStatus, $error etc. arbeiten können. +function renderLayout(string $contentView, array $viewData, string $pageTitle, ?string $activeMenu): void +{ + // Daten-Array in einzelne Variablen entpacken + foreach ($viewData as $key => $value) { + if (is_string($key) && $key !== '') { + $cleanKey = preg_replace('/[^a-zA-Z0-9_]/', '', $key); + if ($cleanKey !== '') { + $$cleanKey = $value; + } + } + } + + $partialsPath = __DIR__ . '/partials'; + + require $partialsPath . '/head.php'; + require $partialsPath . '/sidebar.php'; + require $partialsPath . '/topbar.php'; + ?> + +
+ +
+ + - - +
- - - - - - - - - SB Admin 2 - Login - - - - - - - - - - - - -
- - -
- -
- -
-
- -
- + + +
+ + + +
+
+ +
+
+ + +
+ + +
+ diff --git a/public/views/partials/head.php b/public/views/partials/head.php new file mode 100644 index 0000000..1f5ce7b --- /dev/null +++ b/public/views/partials/head.php @@ -0,0 +1,23 @@ + + + + + + + AD Admin Tool<?= isset($pageTitle) ? ' – ' . htmlspecialchars($pageTitle, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') : '' ?> + + + + + + + + + + + + + +
diff --git a/public/views/partials/scripts.php b/public/views/partials/scripts.php new file mode 100644 index 0000000..5845a34 --- /dev/null +++ b/public/views/partials/scripts.php @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/views/partials/sidebar.php b/public/views/partials/sidebar.php new file mode 100644 index 0000000..9a4047e --- /dev/null +++ b/public/views/partials/sidebar.php @@ -0,0 +1,49 @@ + + + + diff --git a/public/views/partials/topbar.php b/public/views/partials/topbar.php new file mode 100644 index 0000000..d29658c --- /dev/null +++ b/public/views/partials/topbar.php @@ -0,0 +1,49 @@ + + +
+ + +
+ + + + + + +
diff --git a/public/views/users.php b/public/views/users.php index ebd600a..0ef3b5f 100644 --- a/public/views/users.php +++ b/public/views/users.php @@ -1,520 +1,94 @@ > $users */ -/** @var array> $groups */ -/** @var string|null $error */ +/** + * @var array> $users + * @var array> $groups + * @var string|null $error + */ ?> - - - - - - - - - - - - SB Admin 2 - Tables - - - - - - - - - - - - - - - - -
- - - - - - -
- - -
- - - - - - -
- - -

Benutzer & Gruppen

- -

- -

- -

DataTables is a third party plugin that is used to generate the demo table below. - For more information about DataTables, please visit the official DataTables documentation.

- - -
-
-
Benutzer
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
AnmeldenameAnzeigenameE-Mail
AnmeldenameAnzeigenameE-Mail
-
-
-
-
-
-
Gruppen
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
GruppennameCNBeschreibung
GruppennameCNBeschreibung
-
-
-
- -
- - -
- - - -
-
- -
-
- - -
- +
+

Benutzer & Gruppen

- - - - - + + + - -