Compare commits
81
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de85f80541 | ||
|
|
165b1637da | ||
|
|
5d249b3929 | ||
|
|
327f650f58 | ||
|
|
d65b778215 | ||
|
|
14d78b548c | ||
|
|
3a86acc057 | ||
|
|
d24a674643 | ||
|
|
80b31284ab | ||
|
|
f61d0ea8fe | ||
|
|
b1de4c08f0 | ||
|
|
9c6629a61e | ||
|
|
f545c58456 | ||
|
|
2901a5e362 | ||
|
|
2231d65465 | ||
|
|
4558a717fa | ||
|
|
b2b4ac2124 | ||
|
|
f7fd4ed183 | ||
|
|
a73b2ec13f | ||
|
|
dce0a0caed | ||
|
|
3d8c4291be | ||
|
|
a0afeabb27 | ||
|
|
96c27a8722 | ||
|
|
4478f7e82f | ||
|
|
c1f3c3ea4e | ||
|
|
2a8564d3bf | ||
|
|
14f4143207 | ||
|
|
53b4bd3423 | ||
|
|
386bef9094 | ||
|
|
4e52aa468f | ||
|
|
f4b65903f7 | ||
|
|
29a1be5d22 | ||
|
|
51bc1f7876 | ||
|
|
4e0fcfea3f | ||
|
|
63373ac90c | ||
|
|
966ed2690c | ||
|
|
51ce63b0f0 | ||
|
|
d9f98176c5 | ||
|
|
2aa48a2601 | ||
|
|
2bd315efcd | ||
|
|
d862ed1102 | ||
|
|
5cc4fd0d10 | ||
|
|
24cb1be31f | ||
|
|
955744ee7e | ||
|
|
88f01fe156 | ||
|
|
0c189487c6 | ||
|
|
50934501ab | ||
|
|
54c77e4651 | ||
|
|
351ec88131 | ||
|
|
7ac6a3cd49 | ||
|
|
236fe1fd7f | ||
|
|
4b30f00872 | ||
|
|
e0a4f9ff77 | ||
|
|
ae5beb7ff3 | ||
|
|
614531394d | ||
|
|
773d48f6dd | ||
|
|
b98c8f39d7 | ||
|
|
c286ec3aa2 | ||
|
|
05086a12f5 | ||
|
|
9f76387eec | ||
|
|
d2e076e683 | ||
|
|
80184ed2f0 | ||
|
|
8e2270c674 | ||
|
|
53eb3d4bc2 | ||
|
|
527e783c75 | ||
|
|
6bcdf3f9e3 | ||
|
|
e58bfb6bb1 | ||
|
|
fc665213b3 | ||
|
|
d9ae978837 | ||
|
|
0559317875 | ||
|
|
865320a75c | ||
|
|
35dcb96e5d | ||
|
|
037396fec0 | ||
|
|
d916632d1b | ||
|
|
182997b3fb | ||
|
|
306e3b9c28 | ||
|
|
24a36b020d | ||
|
|
e3dae4c853 | ||
|
|
66fd469c3a | ||
|
|
0846dea367 | ||
|
|
2511b94616 |
No files matched your search
@@ -1,2 +1,3 @@
|
|||||||
WEBROOT/config_backups/*
|
WEBROOT/config_backups/*
|
||||||
WEBROOT/static/*
|
WEBROOT/static/*
|
||||||
|
WEBROOT/sitemap.xml
|
||||||
@@ -5,12 +5,18 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"workbench.colorTheme": "Default Dark Modern",
|
"workbench.colorTheme": "Dark Modern",
|
||||||
"workbench.colorCustomizations": {
|
"workbench.colorCustomizations": {
|
||||||
"activityBar.background": "#ff000075",
|
"activityBar.background": "#ff000075",
|
||||||
"activityBar.foreground": "#000000",
|
"activityBar.foreground": "#000000",
|
||||||
"breadcrumb.foreground": "#FFFFFF",
|
"breadcrumb.foreground": "#FFFFFF",
|
||||||
},
|
},
|
||||||
|
"files.exclude": {
|
||||||
|
"*.git*": true,
|
||||||
|
"WEBROOT/config_backups": true,
|
||||||
|
"WEBROOT/static": true,
|
||||||
|
"WEBROOT/templates": true,
|
||||||
|
},
|
||||||
"editor.renderWhitespace": "all",
|
"editor.renderWhitespace": "all",
|
||||||
"editor.smoothScrolling": true,
|
"editor.smoothScrolling": true,
|
||||||
"editor.fontSize": 14,
|
"editor.fontSize": 14,
|
||||||
|
|||||||
-73
@@ -1,73 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "============================="
|
|
||||||
echo " Git Management Script"
|
|
||||||
echo "============================="
|
|
||||||
echo ""
|
|
||||||
echo "1) Geänderte Dateien anzeigen"
|
|
||||||
echo "2) Einzelne Dateien zum Commit auswählen"
|
|
||||||
echo "3) Alles stagen"
|
|
||||||
echo "4) Commit & Push"
|
|
||||||
echo "5) Alte Stände anzeigen"
|
|
||||||
echo "6) Aktueller Branch"
|
|
||||||
echo "7) Änderungen"
|
|
||||||
echo "99) Programm beenden"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
read -p "Auswahl: " auswahl
|
|
||||||
|
|
||||||
case $auswahl in
|
|
||||||
|
|
||||||
1)
|
|
||||||
echo "Geänderte Dateien:"
|
|
||||||
git status -s
|
|
||||||
;;
|
|
||||||
|
|
||||||
2)
|
|
||||||
echo "Geänderte Dateien:"
|
|
||||||
git status -s
|
|
||||||
echo ""
|
|
||||||
read -p "Welche Datei soll gestaged werden? (Pfad eingeben): " file
|
|
||||||
git add "$file"
|
|
||||||
echo ">> Datei '$file' wurde gestaged."
|
|
||||||
;;
|
|
||||||
|
|
||||||
3)
|
|
||||||
echo ">> Alle Dateien werden gestaged..."
|
|
||||||
git add -A
|
|
||||||
;;
|
|
||||||
|
|
||||||
4)
|
|
||||||
read -p "Commit Nachricht: " commitmsg
|
|
||||||
git commit -m "$commitmsg"
|
|
||||||
echo ">> Pushen..."
|
|
||||||
git push
|
|
||||||
;;
|
|
||||||
|
|
||||||
5)
|
|
||||||
echo ">> Alte Commits:"
|
|
||||||
git log --oneline --graph --decorate --all
|
|
||||||
;;
|
|
||||||
6)
|
|
||||||
echo "Aktueller Branch"
|
|
||||||
git branch
|
|
||||||
;;
|
|
||||||
7)
|
|
||||||
echo "Änderungen"
|
|
||||||
git status
|
|
||||||
;;
|
|
||||||
|
|
||||||
99)
|
|
||||||
echo "Beendet."
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Ungültige Auswahl!"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
# 🏗️ Backend MVC Framework - Das Handbuch
|
||||||
|
|
||||||
|
Dieses Framework ist eine performante, leichtgewichtige und modulare PHP-Architektur, die speziell für die Entwicklung von professionellen Backends und Dashboards entwickelt wurde. Es implementiert das **Model-View-Controller (MVC)** Pattern und trennt strikt zwischen Datenlogik, Geschäftslogik und UI-Design.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏛️ 1. Architektur & Verzeichnisstruktur
|
||||||
|
|
||||||
|
Das Herzstück des Frameworks liegt in `WEBROOT/src/MVC/`. Die Struktur ist darauf ausgelegt, dass das Projekt beliebig wachsen kann, ohne unübersichtlich zu werden.
|
||||||
|
|
||||||
|
### 📂 Ordner-Struktur im Detail
|
||||||
|
|
||||||
|
* **`WEBROOT/index.php`**: Der zentrale Einstiegspunkt (Front Controller). Jede Anfrage läuft hier auf. Hier werden Routen registriert und das Layout zusammengesetzt.
|
||||||
|
* **`src/MVC/Core/`**:
|
||||||
|
* `Controller.php`: Basisklasse für alle Controller. Bietet Methoden für Rendering und JSON-Output.
|
||||||
|
* `Model.php`: Basisklasse für Datenbankzugriffe. Implementiert sichere CRUD-Methoden via Prepared Statements.
|
||||||
|
* `View.php`: Die Engine, die Daten in HTML-Templates extrahiert.
|
||||||
|
* `Router.php`: Verwaltet Routen und schaltet Middlewares (Auth/Admin) vor.
|
||||||
|
* `Layout/Layout.php`: Das Block-System für modulares UI-Design.
|
||||||
|
* **`src/MVC/Controller/`**: Hier landen deine PHP-Klassen für die Logik. Beispiel: `UserController.php`, `LogController.php`.
|
||||||
|
* **`src/MVC/Model/`**: Jede Klasse entspricht einer Datenbanktabelle. Hier wird definiert, *wie* Daten geholt oder gespeichert werden.
|
||||||
|
* **`src/MVC/View/`**: Reine HTML-Dateien mit minimalem PHP-Code für die Anzeige.
|
||||||
|
* **`src/MVC/Layout/Templates/`**: Enthält das Grundgerüst (`main.php`), in das die Views eingebettet werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚦 2. Der Request-Lifecycle (Wie eine Seite entsteht)
|
||||||
|
|
||||||
|
1. Ein Benutzer ruft `?service=benutzer` auf.
|
||||||
|
2. Die `index.php` lädt den **Autoloader** und initialisiert den **Router**.
|
||||||
|
3. Der **Router** prüft, ob eine Middleware (z.B. `auth`) für diesen Service konfiguriert ist.
|
||||||
|
4. Wenn der Zugriff erlaubt ist, erstellt der Router eine Instanz des jeweiligen **Controllers** und ruft die Zielmethode auf.
|
||||||
|
5. Der **Controller** nutzt ein **Model**, um Daten aus der Datenbank zu holen.
|
||||||
|
6. Der Controller übergibt diese Daten an eine **View** (`$this->render()`).
|
||||||
|
7. Die **index.php** nimmt das HTML der View entgegen, packt es in den `content`-Block des **Layouts** und gibt die fertige Seite aus.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏛️ 3. Arbeiten mit dem Model-Layer (Datenbank)
|
||||||
|
|
||||||
|
Vermeide SQL direkt im Controller! Nutze immer Models für eine saubere Abstraktion.
|
||||||
|
|
||||||
|
### Beispiel: Ein eigenes Model erstellen
|
||||||
|
```php
|
||||||
|
namespace MVC\Model;
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class Product extends Model {
|
||||||
|
protected static $table = "Produkte"; // Tabellenname in der DB
|
||||||
|
|
||||||
|
public static function getExpensiveProducts() {
|
||||||
|
return self::where("PREIS > ?", 100); // Sicher gegen SQL-Injection!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verfügbare Methoden der Basisklasse:
|
||||||
|
* `all()`: Lädt alles aus der Tabelle.
|
||||||
|
* `find($id)`: Findet einen Datensatz per ID.
|
||||||
|
* `where($sql, ...$params)`: Filtert mit Prepared Statements.
|
||||||
|
* `insert($array)`: Speichert neue Daten (z.B. `['TITEL' => 'Test']`).
|
||||||
|
* `update($array, $where, ...$params)`: Aktualisiert bestehende Daten.
|
||||||
|
* `delete($where, ...$params)`: Löscht Datensätze sicher.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 4. Controller & Formularverarbeitung
|
||||||
|
|
||||||
|
Controller steuern den Ablauf. Sie entscheiden, was bei `GET` und was bei `POST` passiert.
|
||||||
|
|
||||||
|
### Beispiel: Daten verarbeiten
|
||||||
|
```php
|
||||||
|
public function save() {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// Daten via Model speichern
|
||||||
|
User::update(['STATUS' => 'aktiv'], "ID = ?", $_POST['id']);
|
||||||
|
|
||||||
|
// WICHTIG: Danach immer Redirect (Post-Redirect-Get Pattern)
|
||||||
|
header("Location: ?service=user_list");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
return $this->render('edit_view');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧱 5. Das modulare Block-Layout
|
||||||
|
|
||||||
|
Das Layout-System ermöglicht es, verschiedene Teile der Seite (Header, Sidebar, Content) unabhängig voneinander zu befüllen.
|
||||||
|
|
||||||
|
### Blöcke in `index.php` befüllen:
|
||||||
|
```php
|
||||||
|
$layout = new \MVC\Layout\Layout('main');
|
||||||
|
$layout->setBlock('title', 'Admin Dashboard');
|
||||||
|
$layout->setBlock('sidebar', $sidebarHtml);
|
||||||
|
$layout->setBlock('content', $controllerOutput);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Blöcke im Template (`main.php`) ausgeben:
|
||||||
|
```html
|
||||||
|
<aside class="sidebar">
|
||||||
|
<?= $this->getBlock('sidebar') ?>
|
||||||
|
</aside>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 6. CLI-Werkzeuge für Entwickler
|
||||||
|
|
||||||
|
Das Framework kommt mit automatisierten Scripten, um lästige Routinearbeiten zu vermeiden.
|
||||||
|
|
||||||
|
### Projekt initialisieren
|
||||||
|
Erstellt ein komplettes Skelett inkl. Dev-Server und API.
|
||||||
|
```bash
|
||||||
|
php init_mvc.php MeinProjekt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modul (Controller + View) erstellen
|
||||||
|
Erstellt zwei Dateien und gibt dir den Code für den Router aus.
|
||||||
|
```bash
|
||||||
|
php create_module.php kunden_verwaltung
|
||||||
|
```
|
||||||
|
|
||||||
|
### Model erstellen
|
||||||
|
Erstellt eine Model-Klasse mit Tabellenzuweisung und dokumentierten Spalten.
|
||||||
|
```bash
|
||||||
|
php create_model.php Kunden ID,NAME,EMAIL,TELEFON
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ 7. Sicherheit & Best Practices
|
||||||
|
|
||||||
|
1. **XSS-Schutz**: Gib niemals Variablen direkt in der View aus. Nutze immer `<?= htmlspecialchars($var) ?>`.
|
||||||
|
2. **SQL-Injection**: Nutze niemals Variablen direkt in einem SQL-String. Nutze die Methoden `where()`, `update()` etc. des Models. Sie verwenden intern Prepared Statements.
|
||||||
|
3. **Namespaces**: Alle Klassen im `src/MVC` Ordner nutzen Namespaces. Achte beim Aufruf globaler Klassen (außerhalb des MVC-Ordners) auf den führenden Backslash, z.B. `\db_connect()`.
|
||||||
|
4. **Error Handling**: Fehler werden zentral in der `index.php` gefangen. Admins erhalten detaillierte Informationen, User sehen eine sichere Fehlermeldung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ 8. AJAX & API Entwicklung
|
||||||
|
|
||||||
|
Um Daten ohne Seiten-Reload zu laden, bietet der Controller die `json()` Methode:
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function getStatus() {
|
||||||
|
$data = ['online' => true, 'time' => date('H:i')];
|
||||||
|
return $this->json($data); // Setzt Header und beendet Script
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In deinem JavaScript kannst du dies dann einfach per `fetch()` abrufen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 9. Installation & Start
|
||||||
|
|
||||||
|
1. Stelle sicher, dass PHP 8.x installiert ist.
|
||||||
|
2. Passe die `src/config/env.ini` mit deinen Datenbankdaten an.
|
||||||
|
3. Starte den internen Server: `php server.php`.
|
||||||
|
4. Öffne `http://localhost:8000` im Browser.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Dieses Framework wurde für maximale Produktivität entwickelt. Bei Fragen schau in die integrierte Dokumentation im Backend.*
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteRule ^(Commands|config_backups|controller|modules)($|/.*) - [F,L,NC]
|
||||||
|
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||||
|
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
|
||||||
|
|
||||||
|
# -----------------------------------
|
||||||
|
# .php entfernen
|
||||||
|
# -----------------------------------
|
||||||
|
|
||||||
|
# browser requestet datei.php -> redirect zu /datei
|
||||||
|
RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC]
|
||||||
|
RewriteRule ^ /%1 [R=301,L]
|
||||||
|
|
||||||
|
# /datei -> datei.php
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
|
||||||
|
RewriteRule ^(.+)$ $1.php [L]
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
// loginTest.php
|
||||||
|
|
||||||
|
$loginUrl = "https://backend.blackpi.ipv64.net/login.php"; // URL zu deinem Login-Script
|
||||||
|
|
||||||
|
// Testfälle
|
||||||
|
$tests = [
|
||||||
|
[
|
||||||
|
'username' => 'Bruno', // korrektes Testkonto
|
||||||
|
'password' => 'bBHofurUJWKzPE8Z', // korrektes Passwort
|
||||||
|
'expected' => true,
|
||||||
|
'description' => 'Erfolgreiches Login'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'username' => 'admin',
|
||||||
|
'password' => 'falschespasswort',
|
||||||
|
'expected' => false,
|
||||||
|
'description' => 'Fehlerhaftes Login'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($tests as $test) {
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $loginUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||||
|
'username' => $test['username'],
|
||||||
|
'password' => $test['password']
|
||||||
|
]));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // Wichtige Info: redirect erkennen
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||||
|
$body = substr($response, $headerSize);
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Prüfen, ob Login erfolgreich war: Redirect auf index.php oder Session gesetzt
|
||||||
|
if (strpos($response, "Location: index.php") !== false || strpos($body, "Login fehlgeschlagen") === false) {
|
||||||
|
$success = true;
|
||||||
|
} else {
|
||||||
|
$success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($success === $test['expected']) {
|
||||||
|
echo "[PASS] {$test['description']}\n";
|
||||||
|
} else {
|
||||||
|
echo "[FAIL] {$test['description']}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
include_once(__DIR__ . "/../controller/DataController.php");
|
include_once( __DIR__ . '/../src/utils/databaseMethods.php');
|
||||||
include_once(__DIR__ . "/../controller/DatabaseConnect.php");
|
include_once( __DIR__ . '/../src/classes/getData.php');
|
||||||
include_once(__DIR__ . "/../controller/DatabaseController.php");
|
include_once( __DIR__ . '/../src/database/databaseConnect.php');
|
||||||
|
|
||||||
$data = new getSpecificData();
|
$data = new getSpecificData();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||||
|
include_once( __DIR__ . '/../../src/controller/apiController.php');
|
||||||
|
include_once( __DIR__ . '/../../src/utils/mailer.php');
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
header("Access-Control-Allow-Headers: Authorization, Function, Content-Type");
|
||||||
|
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$validTokens = DatabaseShow(
|
||||||
|
"API_Credentials",
|
||||||
|
"TOKEN"
|
||||||
|
);
|
||||||
|
|
||||||
|
$validTokens = array_column($validTokens, 'TOKEN');
|
||||||
|
|
||||||
|
$headers = getallheaders();
|
||||||
|
$token = $headers['Authorization'] ?? $headers['authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
||||||
|
$function = $headers['Function'] ?? $headers['function'] ?? '';
|
||||||
|
|
||||||
|
if (!in_array($token, $validTokens)) {
|
||||||
|
http_response_code(401);
|
||||||
|
$error = json_encode([
|
||||||
|
"error" => "Unauthorized",
|
||||||
|
"debug_received_token" => $token,
|
||||||
|
"debug_all_headers" => $headers
|
||||||
|
]);
|
||||||
|
|
||||||
|
DatabaseInsert(
|
||||||
|
"API_LOGS",
|
||||||
|
"CLIENT_IP, TOKEN, API, FUNKTION, JSON",
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
$token,
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
$function,
|
||||||
|
$error
|
||||||
|
);
|
||||||
|
|
||||||
|
errorMail(
|
||||||
|
"API Login failed",
|
||||||
|
$error,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
"error" => "Unauthorized",
|
||||||
|
"Contact" => "bruno.eckhardt07@gmail.com",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = null;
|
||||||
|
|
||||||
|
if (in_array($token, $validTokens)) {
|
||||||
|
DatabaseInsert(
|
||||||
|
"API_LOGS",
|
||||||
|
"CLIENT_IP, TOKEN, API, FUNKTION",
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
$token,
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
$function
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($function === "IPs") {
|
||||||
|
$data = DatabaseShow(
|
||||||
|
"logs",
|
||||||
|
"CLIENT_IP"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($function === "Ueberstunden") {
|
||||||
|
$data = DatabaseShow(
|
||||||
|
"Ueberstunden",
|
||||||
|
"MINUTEN"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($function === "Berichte") {
|
||||||
|
$data = DatabaseShowWithFK(
|
||||||
|
"Berufsschulberichte",
|
||||||
|
$Berufsschulberichte,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(
|
||||||
|
[
|
||||||
|
"success" => true,
|
||||||
|
"data" => $data,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
header("Access-Control-Allow-Headers: Authorization, Function, Content-Type");
|
||||||
|
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
|
||||||
|
|
||||||
|
$validTokens = [
|
||||||
|
"15a437d128c918633e57dc7d18118a073b807644f21731ed5a7ebcf3eca9e242",
|
||||||
|
];
|
||||||
|
$baseUploadDir = __DIR__ . "/../services/uploads/";
|
||||||
|
|
||||||
|
$headers = getallheaders();
|
||||||
|
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
||||||
|
|
||||||
|
$token = trim($token);
|
||||||
|
|
||||||
|
if (!in_array($token, $validTokens, true)) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode([
|
||||||
|
"error" => "Unauthorized",
|
||||||
|
"received_token" => $token
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_FILES['files'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["error" => "No files uploaded"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$folderName = null;
|
||||||
|
$readmeContent = null;
|
||||||
|
|
||||||
|
// Prüfen ob readme.md hochgeladen wurde
|
||||||
|
foreach ($_FILES['files']['name'] as $key => $name) {
|
||||||
|
if (strtolower($name) === "readme.md") {
|
||||||
|
$tmpName = $_FILES['files']['tmp_name'][$key];
|
||||||
|
$readmeContent = file_get_contents($tmpName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($readmeContent) {
|
||||||
|
// Erste Markdown-Überschrift extrahieren (# Titel)
|
||||||
|
if (preg_match('/^#\s*(.+)$/m', $readmeContent, $matches)) {
|
||||||
|
$rawName = $matches[1];
|
||||||
|
|
||||||
|
// Slug erzeugen (ordner-tauglich machen)
|
||||||
|
$folderName = strtolower($rawName);
|
||||||
|
$folderName = preg_replace("/[^a-z0-9]+/", "-", $folderName);
|
||||||
|
$folderName = trim($folderName, "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback falls keine readme oder kein Titel
|
||||||
|
if (!$folderName) {
|
||||||
|
$folderName = uniqid("upload_", true);
|
||||||
|
}
|
||||||
|
$uploadDir = $baseUploadDir . $folderName . "/";
|
||||||
|
|
||||||
|
if (!mkdir($uploadDir, 0755, true)) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(["error" => "Failed to create directory"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadedFiles = [];
|
||||||
|
|
||||||
|
foreach ($_FILES['files']['tmp_name'] as $key => $tmpName) {
|
||||||
|
|
||||||
|
if ($_FILES['files']['error'][$key] !== UPLOAD_ERR_OK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$originalName = basename($_FILES['files']['name'][$key]);
|
||||||
|
|
||||||
|
$safeName = preg_replace("/[^a-zA-Z0-9._-]/", "", $originalName);
|
||||||
|
|
||||||
|
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||||
|
$mime = $finfo->file($tmpName);
|
||||||
|
|
||||||
|
if ($_FILES['files']['size'][$key] > 25 * 1024 * 1024) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetPath = $uploadDir . $safeName;
|
||||||
|
|
||||||
|
if (move_uploaded_file($tmpName, $targetPath)) {
|
||||||
|
$uploadedFiles[] = $safeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = json_encode([
|
||||||
|
"success" => true,
|
||||||
|
"folder" => $folderName,
|
||||||
|
"uploaded_files" => $uploadedFiles
|
||||||
|
]);
|
||||||
|
|
||||||
|
DatabaseInsert(
|
||||||
|
"API",
|
||||||
|
"CLIENT_IP, TOKEN, API, FUNKTION, JSON",
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
$token,
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
"Upload",
|
||||||
|
$json
|
||||||
|
);
|
||||||
|
|
||||||
|
echo $json;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||||
|
include_once(__DIR__ . '/../../src/controller/apiController.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$api = (new ApiController());
|
||||||
|
|
||||||
|
if ($api->urlAuth($_GET['token']) == true ) {
|
||||||
|
|
||||||
|
if (isset($_GET['day'])) {
|
||||||
|
|
||||||
|
$day = $_GET['day'];
|
||||||
|
|
||||||
|
$data = DatabaseShowWithFK(
|
||||||
|
"Stundenplan",
|
||||||
|
$Stundenplan,
|
||||||
|
"ZEITEN, $day",
|
||||||
|
"$day IS NOT NULL AND $day != '' AND $day != '(NULL)' ORDER BY ZEITEN ASC"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$data = DatabaseShowWithFK(
|
||||||
|
"Stundenplan",
|
||||||
|
$Stundenplan,
|
||||||
|
"ZEITEN, MO, DI, MO, DO, FR",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo $api->jsonSuccess($data);
|
||||||
|
$api->requestSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://localhost:9000/api/v2/lessons.php?token=f3a50684d7f0cc2d9b21601798ea48242fab7481a0d0407a2b8683b0b74a79e3
|
||||||
|
* http://localhost:9000/api/v2/lessons.php?token=f3a50684d7f0cc2d9b21601798ea48242fab7481a0d0407a2b8683b0b74a79e3&day=FR
|
||||||
|
*/
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||||
|
include_once(__DIR__ . '/../../src/controller/apiController.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$api = (new ApiController());
|
||||||
|
|
||||||
|
if ($api->urlAuth($_GET['token']) == true ) {
|
||||||
|
|
||||||
|
if (isset($_GET['date'])) {
|
||||||
|
|
||||||
|
$date = $_GET['date'];
|
||||||
|
|
||||||
|
$data = DatabaseShowWithFK(
|
||||||
|
"Berufsschulberichte",
|
||||||
|
$Berufsschulberichte,
|
||||||
|
"*",
|
||||||
|
"DATUM = ?",
|
||||||
|
$date
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$data = DatabaseShowWithFK(
|
||||||
|
"Berufsschulberichte",
|
||||||
|
$Berufsschulberichte,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo $api->jsonSuccess($data);
|
||||||
|
$api->requestSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://localhost:9000/api/v2/schoolReports.php?token=f3a50684d7f0cc2d9b21601798ea48242fab7481a0d0407a2b8683b0b74a79e3
|
||||||
|
* http://localhost:9000/api/v2/schoolReports.php?token=f3a50684d7f0cc2d9b21601798ea48242fab7481a0d0407a2b8683b0b74a79e3&date=2025-09-09
|
||||||
|
*/
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/../../src/utils/requestLogger.php');
|
||||||
|
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||||
|
include_once(__DIR__ . '/../../src/controller/apiController.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$api = (new ApiController());
|
||||||
|
|
||||||
|
if ($api->urlAuth($_GET['token']) == true ) {
|
||||||
|
|
||||||
|
if (isset($_GET['date'])) {
|
||||||
|
|
||||||
|
$date = $_GET['date'];
|
||||||
|
|
||||||
|
$data = DatabaseShow(
|
||||||
|
"Firmenberichte",
|
||||||
|
"DATUM, PROZESS_1, PROZESS_2, PROZESS_3, PROZESS_4, BEGINN, ENDE_PUBLIC",
|
||||||
|
"DATUM = ?",
|
||||||
|
$date
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$data = DatabaseShow(
|
||||||
|
"Firmenberichte",
|
||||||
|
"DATUM, PROZESS_1, PROZESS_2, PROZESS_3, PROZESS_4, BEGINN, ENDE_PUBLIC",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo $api->jsonSuccess($data);
|
||||||
|
$api->requestSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://localhost:9000/api/v2/workReports.php?token=f3a50684d7f0cc2d9b21601798ea48242fab7481a0d0407a2b8683b0b74a79e3
|
||||||
|
* http://localhost:9000/api/v2/workReports.php?token=f3a50684d7f0cc2d9b21601798ea48242fab7481a0d0407a2b8683b0b74a79e3&date=2025-09-09
|
||||||
|
*/
|
||||||
+146
-13
@@ -17,8 +17,19 @@ $serviceGroups = array (
|
|||||||
array (
|
array (
|
||||||
0 => 'home_info',
|
0 => 'home_info',
|
||||||
1 => 'links',
|
1 => 'links',
|
||||||
2 => 'berichte_arbeit',
|
3 => 'documentation',
|
||||||
3 => 'berichte_arbeit_gen',
|
),
|
||||||
|
),
|
||||||
|
'Ausbildung' =>
|
||||||
|
array (
|
||||||
|
'label' => 'Ausbildung',
|
||||||
|
'defaultOpen' => true,
|
||||||
|
'services' =>
|
||||||
|
array (
|
||||||
|
0 => 'print_static',
|
||||||
|
1 => 'zeiterfassung',
|
||||||
|
2 => 'berichte_arbeit_gen',
|
||||||
|
3 => 'berichte',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'Logs' =>
|
'Logs' =>
|
||||||
@@ -37,13 +48,25 @@ $serviceGroups = array (
|
|||||||
'System' =>
|
'System' =>
|
||||||
array (
|
array (
|
||||||
'label' => 'Verwaltung',
|
'label' => 'Verwaltung',
|
||||||
'defaultOpen' => true,
|
'defaultOpen' => false,
|
||||||
'services' =>
|
'services' =>
|
||||||
array (
|
array (
|
||||||
0 => 'services_control',
|
0 => 'data_info',
|
||||||
1 => 'user_control',
|
1 => 'services_control',
|
||||||
2 => 'variablen_control',
|
2 => 'user_control',
|
||||||
3 => 'data_info',
|
3 => 'variablen_control',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'Test' =>
|
||||||
|
array (
|
||||||
|
'label' => 'Dev / Test',
|
||||||
|
'defaultOpen' => false,
|
||||||
|
'services' =>
|
||||||
|
array (
|
||||||
|
0 => 'api_tester',
|
||||||
|
1 => 'weather_chart',
|
||||||
|
2 => 'tabellen',
|
||||||
|
3 => 'domains',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -55,6 +78,24 @@ $services = array (
|
|||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => false,
|
'adminOnly' => false,
|
||||||
),
|
),
|
||||||
|
'print_static' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Berichte Drucken',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
),
|
||||||
|
'api_tester' =>
|
||||||
|
array (
|
||||||
|
'name' => 'API Testing',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => true,
|
||||||
|
),
|
||||||
|
'weather_chart' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Wetter Statistik',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => true,
|
||||||
|
),
|
||||||
'data_info' =>
|
'data_info' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'Data Info',
|
'name' => 'Data Info',
|
||||||
@@ -63,7 +104,7 @@ $services = array (
|
|||||||
),
|
),
|
||||||
'links' =>
|
'links' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'Externe Links',
|
'name' => 'Links',
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => false,
|
'adminOnly' => false,
|
||||||
),
|
),
|
||||||
@@ -95,7 +136,7 @@ $services = array (
|
|||||||
array (
|
array (
|
||||||
'name' => 'Persönliche Logs',
|
'name' => 'Persönliche Logs',
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => false,
|
'adminOnly' => true,
|
||||||
),
|
),
|
||||||
'services_control' =>
|
'services_control' =>
|
||||||
array (
|
array (
|
||||||
@@ -103,15 +144,15 @@ $services = array (
|
|||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => true,
|
'adminOnly' => true,
|
||||||
),
|
),
|
||||||
'berichte_arbeit' =>
|
'zeiterfassung' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'Arbeitsberichte',
|
'name' => 'Zeit Erfassung',
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => false,
|
'adminOnly' => false,
|
||||||
),
|
),
|
||||||
'berichte_arbeit_gen' =>
|
'berichte_arbeit_gen' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'Arbeitsberichte PDF',
|
'name' => 'Berichte PDF',
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => false,
|
'adminOnly' => false,
|
||||||
),
|
),
|
||||||
@@ -119,7 +160,7 @@ $services = array (
|
|||||||
array (
|
array (
|
||||||
'name' => 'Benutzer',
|
'name' => 'Benutzer',
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => false,
|
'adminOnly' => true,
|
||||||
),
|
),
|
||||||
'variablen_control' =>
|
'variablen_control' =>
|
||||||
array (
|
array (
|
||||||
@@ -127,6 +168,42 @@ $services = array (
|
|||||||
'active' => true,
|
'active' => true,
|
||||||
'adminOnly' => true,
|
'adminOnly' => true,
|
||||||
),
|
),
|
||||||
|
'tabellen' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Tabellen',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => true,
|
||||||
|
),
|
||||||
|
'berichte' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Berichte Einsehen',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
),
|
||||||
|
'domains' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Domains',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
),
|
||||||
|
'tester' =>
|
||||||
|
array (
|
||||||
|
'name' => 'TESTER',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
),
|
||||||
|
'documentation' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Dokumentation',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
),
|
||||||
|
'logout' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Abmelden',
|
||||||
|
'active' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
$linkGroups = array (
|
$linkGroups = array (
|
||||||
@@ -261,4 +338,60 @@ $linkButtons = array (
|
|||||||
'adminOnly' => false,
|
'adminOnly' => false,
|
||||||
'group' => 'tools',
|
'group' => 'tools',
|
||||||
),
|
),
|
||||||
|
13 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Backend',
|
||||||
|
'url' => 'https://backend.blackpi.ipv64.net',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
'group' => 'info',
|
||||||
|
),
|
||||||
|
14 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Dev Backend',
|
||||||
|
'url' => 'https://dev-backend.blackpi.ipv64.net',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
'group' => 'info',
|
||||||
|
),
|
||||||
|
15 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Dev Pubic',
|
||||||
|
'url' => 'https://dev-public.blackpi.ipv64.net',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
'group' => 'info',
|
||||||
|
),
|
||||||
|
16 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Local Backend',
|
||||||
|
'url' => 'http://backend.local.ipv64.net',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
'group' => 'info',
|
||||||
|
),
|
||||||
|
17 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Local Pubic',
|
||||||
|
'url' => 'http://public.local.ipv64.net',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
'group' => 'info',
|
||||||
|
),
|
||||||
|
18 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Proxmox',
|
||||||
|
'url' => 'https://100.88.208.86:8006',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => true,
|
||||||
|
'group' => 'devices',
|
||||||
|
),
|
||||||
|
19 =>
|
||||||
|
array (
|
||||||
|
'label' => 'Flatnotes',
|
||||||
|
'url' => 'http://10.233.1.130:8080',
|
||||||
|
'newTab' => true,
|
||||||
|
'adminOnly' => false,
|
||||||
|
'group' => 'tools',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
function db_connect() {
|
|
||||||
$servers = [
|
|
||||||
'dev' => [
|
|
||||||
'hostaddr' => '10.233.1.29',
|
|
||||||
'username' => 'worker',
|
|
||||||
'password' => 'bBHofurUJWKzPE8Z',
|
|
||||||
'database' => 'DEV',
|
|
||||||
],
|
|
||||||
'vpn' => [
|
|
||||||
'hostaddr' => '100.123.136.86',
|
|
||||||
'username' => 'worker',
|
|
||||||
'password' => 'bBHofurUJWKzPE8Z',
|
|
||||||
'database' => 'DEV',
|
|
||||||
],
|
|
||||||
'live' => [
|
|
||||||
'hostaddr' => '10.233.1.244',
|
|
||||||
'username' => 'worker',
|
|
||||||
'password' => 'bBHofurUJWKzPE8Z',
|
|
||||||
'database' => 'DEV',
|
|
||||||
],
|
|
||||||
'backup' => [
|
|
||||||
'hostaddr' => '10.233.1.243',
|
|
||||||
'username' => 'worker',
|
|
||||||
'password' => 'bBHofurUJWKzPE8Z',
|
|
||||||
'database' => 'DEV',
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
$enviroment = 'live';
|
|
||||||
|
|
||||||
$conn = new mysqli(
|
|
||||||
$servers[$enviroment]['hostaddr'],
|
|
||||||
$servers[$enviroment]['username'],
|
|
||||||
$servers[$enviroment]['password'],
|
|
||||||
$servers[$enviroment]['database'],
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
throw new Exception('Connection Error: ' . $conn->connect_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->set_charset("utf8mb4");
|
|
||||||
return $conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
function logging() {
|
|
||||||
include_once 'DataController.php';
|
|
||||||
if ((new getSpecificData())->getLoggingStatus("GENERAL_BACKEND_LOGGING")[0]['WERT'] == 'ON') {
|
|
||||||
include_once 'DatabaseConnect.php';
|
|
||||||
include_once 'DatabaseController.php';
|
|
||||||
include_once 'DataController.php';
|
|
||||||
|
|
||||||
$ClientIP = (new getSpecificData())->getClientIP();
|
|
||||||
$UserAgent = (new getSpecificData())->getUserAgent();
|
|
||||||
$RequestURL = (new getSpecificData())->getRequestURL();
|
|
||||||
$USERCOOKIE = (new getSpecificData())->getCookie("USER");
|
|
||||||
$SessionID = (new getSpecificData())->getSessionID();
|
|
||||||
$REQUESTTIME = (new getSpecificData())->getRequestTime();
|
|
||||||
$QUERY = (new getSpecificData())->getQuery();
|
|
||||||
$LAND = (new getSpecificData())->getGeoIP($ClientIP);
|
|
||||||
$Whitelisted = (new getSpecificData())->getWhitelistedIPs();
|
|
||||||
$node = (new getSpecificData())->getServerNode((new getSpecificData())->getServerIP());
|
|
||||||
$username = $_SESSION['username'] ?? 'Unbekannt';
|
|
||||||
$website = (new getSpecificData())->getWebsite();
|
|
||||||
|
|
||||||
if ((new getSpecificData())->getLoggingStatus("DEV_BACKEND_LOGGING")[0]['WERT'] == 'OFF') {
|
|
||||||
if (in_array($ClientIP, $Whitelisted)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$db_conn = db_connect();
|
|
||||||
|
|
||||||
$stmt = $db_conn->prepare("SELECT * FROM logs WHERE
|
|
||||||
`CLIENT_IP` LIKE ? and
|
|
||||||
`REQUEST_URL` = ? and
|
|
||||||
`REQUEST_URL` <> '/favicon.ico'
|
|
||||||
");
|
|
||||||
|
|
||||||
$stmt->bind_param("ss", $ClientIP, $RequestURL);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->store_result();
|
|
||||||
|
|
||||||
if ($RequestURL !== '/favicon.ico') {
|
|
||||||
if ($stmt->num_rows > 0) { //wenn mindestens eine zeile vorhanden ist für die client ip dann führe updates durch, wenn nicht mache neue zeile
|
|
||||||
$updateCounter = $db_conn->prepare("
|
|
||||||
UPDATE logs
|
|
||||||
SET COUNTER = COUNTER + 1
|
|
||||||
WHERE `CLIENT_IP` = ?
|
|
||||||
AND `REQUEST_URL` = ?
|
|
||||||
");
|
|
||||||
$updateCounter->bind_param("ss", $ClientIP, $RequestURL);
|
|
||||||
$updateCounter->execute();
|
|
||||||
|
|
||||||
$updateLastRequest = $db_conn->prepare("
|
|
||||||
UPDATE logs
|
|
||||||
SET `LAST_REQUEST` = ?
|
|
||||||
WHERE `CLIENT_IP` = ?
|
|
||||||
AND `REQUEST_URL` = ?
|
|
||||||
");
|
|
||||||
$updateLastRequest->bind_param("sss", $REQUESTTIME, $ClientIP, $RequestURL);
|
|
||||||
$updateLastRequest->execute();
|
|
||||||
|
|
||||||
$updateCookieValue = $db_conn->prepare("
|
|
||||||
UPDATE logs
|
|
||||||
SET `USER_COOKIE_VALUE` = ?
|
|
||||||
WHERE `CLIENT_IP` = ?
|
|
||||||
AND `REQUEST_URL` = ?
|
|
||||||
");
|
|
||||||
$updateCookieValue->bind_param("sss", $USERCOOKIE, $ClientIP, $RequestURL);
|
|
||||||
$updateCookieValue->execute();
|
|
||||||
} else {
|
|
||||||
DatabaseInsert(
|
|
||||||
"logs",
|
|
||||||
"CLIENT_IP, LAND, SERVER_IP, USER_AGENT, REQUEST_URL, USER_COOKIE_VALUE, USER_SESSION_VALUE, FIRST_REQUEST, QUERY, WEBSITE, USER",
|
|
||||||
$ClientIP,
|
|
||||||
$LAND,
|
|
||||||
$node,
|
|
||||||
$UserAgent,
|
|
||||||
$RequestURL,
|
|
||||||
$USERCOOKIE,
|
|
||||||
$SessionID,
|
|
||||||
$REQUESTTIME,
|
|
||||||
$QUERY,
|
|
||||||
$website,
|
|
||||||
$username
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// ConsoleLog("Client IP: $ClientIP | User Agent: $UserAgent | Request URL: $RequestURL | User Cookie: $USERCOOKIE | Session ID: $SessionID | Request Time: $REQUESTTIME | GeoIP: $LAND | Server Node: $node | Website: $website");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+141
-81
@@ -1,11 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
ob_start();
|
ob_start();
|
||||||
// Vor allen includes, vor jeglicher Ausgabe
|
|
||||||
session_set_cookie_params([
|
session_set_cookie_params([
|
||||||
'lifetime' => 0, // bis Browser geschlossen
|
'lifetime' => 0,
|
||||||
'path' => '/', // Gültig für ganze Domain
|
'path' => '/',
|
||||||
'domain' => '', // aktuelle Domain
|
'domain' => '',
|
||||||
'secure' => false, // true nur für HTTPS
|
'secure' => false,
|
||||||
'httponly' => true,
|
'httponly' => true,
|
||||||
'samesite' => 'Lax'
|
'samesite' => 'Lax'
|
||||||
]);
|
]);
|
||||||
@@ -16,20 +15,41 @@ if (empty($_SESSION['user_logged_in'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
include 'config.php';
|
require_once __DIR__ . '/src/MVC/autoloader.php';
|
||||||
include 'controller/CookieController.php';
|
|
||||||
include 'controller/DatabaseConnect.php';
|
|
||||||
include 'controller/DatabaseController.php';
|
|
||||||
include 'controller/DataController.php';
|
|
||||||
include 'controller/LogController.php';
|
|
||||||
include 'controller/UserController.php';
|
|
||||||
include 'modules/ConsoleLog.php';
|
|
||||||
// include 'controller/RoutesController.php';
|
|
||||||
|
|
||||||
|
include_once( __DIR__ . '/config.php');
|
||||||
|
include_once( __DIR__ . '/src/classes/getData.php');
|
||||||
|
include_once( __DIR__ . '/src/controller/apiController.php');
|
||||||
|
include_once( __DIR__ . '/src/controller/cookieController.php');
|
||||||
|
include_once( __DIR__ . '/src/controller/rightsController.php');
|
||||||
|
include_once( __DIR__ . '/src/controller/userController.php');
|
||||||
|
include_once( __DIR__ . '/src/database/databaseConnect.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/Debugging/ConsoleLogger.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/formatter.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/mailer.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/requestLogger.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/router.php');
|
||||||
|
|
||||||
|
$user_id = DatabaseShow(
|
||||||
|
"Benutzer",
|
||||||
|
"ID",
|
||||||
|
"NAME = ?",
|
||||||
|
$_SESSION['username'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$rightsArray = DatabaseShowWithFK(
|
||||||
|
"Benutzer_Berechtigungen",
|
||||||
|
$Berechtigungen,
|
||||||
|
"SEITEN_ID",
|
||||||
|
"BENUTZER_ID = ?",
|
||||||
|
$user_id[0]['ID'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$userRights = array_map('trim', array_column($rightsArray['rows'], 'SEITEN_ID'));
|
||||||
|
|
||||||
$selectedService = null;
|
$selectedService = null;
|
||||||
|
|
||||||
// 1. Service aus URL, wenn erlaubt & aktiv
|
|
||||||
if (
|
if (
|
||||||
isset($_GET['service']) &&
|
isset($_GET['service']) &&
|
||||||
isset($services[$_GET['service']]) &&
|
isset($services[$_GET['service']]) &&
|
||||||
@@ -38,7 +58,6 @@ if (
|
|||||||
$selectedService = $_GET['service'];
|
$selectedService = $_GET['service'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Default-Service
|
|
||||||
if (!$selectedService &&
|
if (!$selectedService &&
|
||||||
isset($defaultService) &&
|
isset($defaultService) &&
|
||||||
isset($services[$defaultService]) &&
|
isset($services[$defaultService]) &&
|
||||||
@@ -47,7 +66,6 @@ if (!$selectedService &&
|
|||||||
$selectedService = $defaultService;
|
$selectedService = $defaultService;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Fallback: erster aktiver Service
|
|
||||||
if (!$selectedService) {
|
if (!$selectedService) {
|
||||||
foreach ($services as $key => $service) {
|
foreach ($services as $key => $service) {
|
||||||
if ($service['active']) {
|
if ($service['active']) {
|
||||||
@@ -56,6 +74,11 @@ if (!$selectedService) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($selectedService && !in_array($selectedService, $userRights, true)) {
|
||||||
|
$selectedService = null;
|
||||||
|
}
|
||||||
|
|
||||||
$isAdmin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
|
$isAdmin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
|
||||||
|
|
||||||
if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
|
if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
|
||||||
@@ -64,76 +87,113 @@ if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
|
|||||||
$title = "Backend";
|
$title = "Backend";
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
// MVC Logic
|
||||||
<!DOCTYPE html>
|
try {
|
||||||
<html lang="de">
|
$router = new \MVC\Core\Router();
|
||||||
|
|
||||||
<head>
|
// Register Routes
|
||||||
<meta charset="UTF-8">
|
$router->add('home_info', \MVC\Controller\ServiceController::class, 'home_info', ['auth']);
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
$router->add('links', \MVC\Controller\ServiceController::class, 'links', ['auth']);
|
||||||
<title><?= $title ?></title>
|
$router->add('documentation', \MVC\Controller\DocumentationController::class, 'links', ['auth']);
|
||||||
<link rel="stylesheet" href="style.css">
|
$router->add('logout', \MVC\Controller\ServiceController::class, 'logout', ['auth']);
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
$router->add('public_request_logs', \MVC\Controller\LogController::class, 'public_request_logs', ['auth']);
|
||||||
<header>
|
$router->add('backend_request_logs', \MVC\Controller\LogController::class, 'backend_request_logs', ['auth']);
|
||||||
Backend
|
$router->add('personal_request_logs', \MVC\Controller\LogController::class, 'personal_request_logs', ['auth', 'admin']);
|
||||||
<button id="darkModeToggle">Dark Mode</button>
|
$router->add('public_request_chart', \MVC\Controller\LogController::class, 'public_request_chart', ['auth']);
|
||||||
<button id="logoutButton">Logout</button>
|
$router->add('backend_request_chart', \MVC\Controller\LogController::class, 'backend_request_chart', ['auth']);
|
||||||
</header>
|
|
||||||
<div class="main">
|
|
||||||
<div class="sidebar">
|
|
||||||
<?php foreach ($serviceGroups as $groupKey => $group): ?>
|
|
||||||
<?php
|
|
||||||
$isOpen = false;
|
|
||||||
|
|
||||||
if (!empty($group['defaultOpen'])) {
|
$router->add('user_control', \MVC\Controller\AdminController::class, 'user_control', ['auth', 'admin']);
|
||||||
$isOpen = true;
|
$router->add('services_control', \MVC\Controller\AdminController::class, 'services_control', ['auth', 'admin']);
|
||||||
}
|
$router->add('variablen_control', \MVC\Controller\AdminController::class, 'variablen_control', ['auth', 'admin']);
|
||||||
|
$router->add('data_info', \MVC\Controller\AdminController::class, 'data_info', ['auth', 'admin']);
|
||||||
|
$router->add('tabellen', \MVC\Controller\AdminController::class, 'tabellen', ['auth', 'admin']);
|
||||||
|
|
||||||
if (in_array($selectedService, $group['services'], true)) {
|
$router->add('berichte', \MVC\Controller\ReportController::class, 'berichte', ['auth']);
|
||||||
$isOpen = true;
|
$router->add('berichte_arbeit_gen', \MVC\Controller\ReportController::class, 'berichte_arbeit_gen', ['auth']);
|
||||||
}
|
$router->add('zeiterfassung', \MVC\Controller\ReportController::class, 'zeiterfassung', ['auth']);
|
||||||
?>
|
$router->add('print_static', \MVC\Controller\ReportController::class, 'print_static', ['auth']);
|
||||||
|
|
||||||
<div class="service-group <?= $isOpen ? 'open' : '' ?>" data-group="<?= $groupKey ?>">
|
$router->add('api_tester', \MVC\Controller\ApiController::class, 'api_tester', ['auth', 'admin']);
|
||||||
<div class="group-title">
|
$router->add('weather_chart', \MVC\Controller\StatsController::class, 'weather_chart', ['auth', 'admin']);
|
||||||
<?= htmlspecialchars($group['label']) ?>
|
$router->add('domains', \MVC\Controller\DomainController::class, 'domains', ['auth']);
|
||||||
<span class="arrow">▸</span>
|
$router->add('documentation', \MVC\Controller\DocumentationController::class, 'index', ['auth']);
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group-services">
|
$layout = new \MVC\Layout\Layout('main');
|
||||||
<?php foreach ($group['services'] as $serviceKey): ?>
|
$layout->setBlock('title', $title);
|
||||||
<?php
|
|
||||||
if (!isset($services[$serviceKey])) continue;
|
|
||||||
if (!$services[$serviceKey]['active']) continue;
|
|
||||||
|
|
||||||
if (!empty($services[$serviceKey]['adminOnly']) && !$isAdmin) {
|
// Header Block
|
||||||
continue;
|
ob_start();
|
||||||
}
|
?>
|
||||||
?>
|
Backend
|
||||||
<a href="?service=<?= $serviceKey ?>">
|
<button id="darkModeToggle">Dark Mode</button>
|
||||||
<button class="<?= $selectedService === $serviceKey ? 'active' : '' ?>">
|
<button id="logoutButton">Logout</button>
|
||||||
<?= htmlspecialchars($services[$serviceKey]['name']) ?>
|
<?php
|
||||||
</button>
|
$layout->setBlock('header', ob_get_clean());
|
||||||
</a>
|
|
||||||
<?php endforeach; ?>
|
// Sidebar Block
|
||||||
</div>
|
ob_start();
|
||||||
</div>
|
?>
|
||||||
<?php endforeach; ?>
|
<?php foreach ($serviceGroups as $groupKey => $group): ?>
|
||||||
</div>
|
<?php
|
||||||
<div class="content">
|
$visibleServices = [];
|
||||||
<?php
|
|
||||||
if ($selectedService) {
|
foreach ($group['services'] as $serviceKey) {
|
||||||
include "services/$selectedService.php";
|
if (!isset($services[$serviceKey])) continue;
|
||||||
logging();
|
if (!$services[$serviceKey]['active']) continue;
|
||||||
} else {
|
if (!empty($services[$serviceKey]['adminOnly']) && !$isAdmin) continue;
|
||||||
echo "<p>Kein aktiver Service verfügbar.</p>";
|
if (!in_array($serviceKey, $userRights, true)) continue;
|
||||||
}
|
$visibleServices[] = $serviceKey;
|
||||||
?>
|
}
|
||||||
</div>
|
|
||||||
|
if (empty($visibleServices)) continue;
|
||||||
|
|
||||||
|
$isOpen = !empty($group['defaultOpen']) || in_array($selectedService, $visibleServices, true);
|
||||||
|
?>
|
||||||
|
<div class="service-group <?= $isOpen ? 'open' : '' ?>" data-group="<?= $groupKey ?>">
|
||||||
|
|
||||||
|
<div class="group-title">
|
||||||
|
<?= htmlspecialchars($group['label']) ?>
|
||||||
|
<span class="arrow">▸</span>
|
||||||
</div>
|
</div>
|
||||||
<script src="main.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
<div class="group-services">
|
||||||
|
<?php foreach ($visibleServices as $serviceKey): ?>
|
||||||
|
<a href="?service=<?= $serviceKey ?>">
|
||||||
|
<button class="<?= $selectedService === $serviceKey ? 'active' : '' ?>">
|
||||||
|
<?= htmlspecialchars($services[$serviceKey]['name']) ?>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php
|
||||||
|
$layout->setBlock('sidebar', ob_get_clean());
|
||||||
|
|
||||||
|
// Content Block
|
||||||
|
if ($selectedService) {
|
||||||
|
$content = $router->dispatch($selectedService);
|
||||||
|
$layout->setBlock('content', $content);
|
||||||
|
logging();
|
||||||
|
} else {
|
||||||
|
$layout->setBlock('content', "<p>Kein aktiver Service verfügbar.</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $layout->render();
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Central Error Handling
|
||||||
|
error_log($e->getMessage());
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
if ($isAdmin) {
|
||||||
|
echo "<h1>Internal Server Error</h1>";
|
||||||
|
echo "<p><strong>Message:</strong> " . $e->getMessage() . "</p>";
|
||||||
|
echo "<p><strong>File:</strong> " . $e->getFile() . " on line " . $e->getLine() . "</p>";
|
||||||
|
echo "<pre>" . $e->getTraceAsString() . "</pre>";
|
||||||
|
} else {
|
||||||
|
echo "<h1>Hoppla!</h1>";
|
||||||
|
echo "<p>Ein interner Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.</p>";
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
-8
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
include_once 'controller/DatabaseController.php';
|
|
||||||
include_once 'controller/RoutesController.php';
|
include_once( __DIR__ . '/config.php');
|
||||||
include_once 'controller/LogController.php';
|
include_once( __DIR__ . '/src/utils/databaseMethods.php');
|
||||||
include_once 'config.php';
|
include_once( __DIR__ . '/src/utils/requestLogger.php');
|
||||||
|
include_once( __DIR__ . '/src/utils/mailer.php');
|
||||||
|
|
||||||
session_set_cookie_params([
|
session_set_cookie_params([
|
||||||
'lifetime' => 0,
|
'lifetime' => 0,
|
||||||
@@ -22,12 +23,32 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$username = $_POST['username'] ?? '';
|
$username = $_POST['username'] ?? '';
|
||||||
$password = $_POST['password'] ?? '';
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
// Gast-Login direkt prüfen
|
||||||
|
if ($username === 'gast') {
|
||||||
|
$_SESSION['user_logged_in'] = true;
|
||||||
|
$_SESSION['username'] = 'gast';
|
||||||
|
$_SESSION['is_admin'] = false;
|
||||||
|
$_SESSION['is_superuser'] = false;
|
||||||
|
|
||||||
|
logging();
|
||||||
|
sendMail(
|
||||||
|
"Gast Login erfolgreich",
|
||||||
|
"Benutzer: gast",
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
redirectToWebsite("index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regulärer Benutzer-Login
|
||||||
$results = DatabaseShow(
|
$results = DatabaseShow(
|
||||||
'Benutzer',
|
'Benutzer',
|
||||||
'PASSWORD',
|
'PASSWORD',
|
||||||
'NAME = ?',
|
'NAME = ?',
|
||||||
$username
|
$username
|
||||||
);
|
);
|
||||||
|
|
||||||
$isAdmin = DatabaseShow(
|
$isAdmin = DatabaseShow(
|
||||||
"Benutzer",
|
"Benutzer",
|
||||||
@@ -54,18 +75,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$_SESSION['is_admin'] = false;
|
$_SESSION['is_admin'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// header("Location: index.php");
|
|
||||||
logging();
|
logging();
|
||||||
|
sendMail(
|
||||||
|
"User Login Successful",
|
||||||
|
"$username, $password",
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
redirectToWebsite("index.php");
|
redirectToWebsite("index.php");
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
$error = 'Login fehlgeschlagen: Passwort inkorrekt';
|
$error = 'Login fehlgeschlagen';
|
||||||
|
sendMail(
|
||||||
|
"User Login failed",
|
||||||
|
"$username, $password",
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$error = 'Systemfehler: Spalte "password" nicht in der Datenbank gefunden.';
|
$error = 'Systemfehler: Spalte "password" nicht in der Datenbank gefunden.';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$error = 'Login fehlgeschlagen: Benutzer nicht gefunden';
|
$error = 'Login fehlgeschlagen';
|
||||||
|
sendMail(
|
||||||
|
"User Login failed",
|
||||||
|
"$username, $password",
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -100,6 +138,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<button style="margin-top: 9px" type="button"
|
||||||
|
onclick="document.querySelector('input[name=username]').value='gast'; document.querySelector('form').submit();">
|
||||||
|
Als Gast einloggen
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -27,8 +27,5 @@ document.querySelectorAll('.group-title').forEach(title => {
|
|||||||
|
|
||||||
const logoutButton = document.getElementById('logoutButton');
|
const logoutButton = document.getElementById('logoutButton');
|
||||||
logoutButton.addEventListener('click', () => {
|
logoutButton.addEventListener('click', () => {
|
||||||
fetch('services/logout.php', { method: 'POST' })
|
window.location.href = '?service=logout';
|
||||||
.then(() => {
|
|
||||||
window.location.href = 'login.php';
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
<?php
|
|
||||||
$conn = db_connect();
|
|
||||||
|
|
||||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE WEBSITE = 'backend.blackpi.ipv64.net' GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
|
||||||
$result = $conn->query($sql);
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
if ($result->num_rows > 0) {
|
|
||||||
while($row = $result->fetch_assoc()) {
|
|
||||||
$labels[] = $row['tag'];
|
|
||||||
$data[] = (int)$row['anzahl'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->close();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Logs Chart</title>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<canvas id="logChart" width="600" height="400"></canvas>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const ctx = document.getElementById('logChart').getContext('2d');
|
|
||||||
const logChart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: <?php echo json_encode($labels); ?>,
|
|
||||||
datasets: [{
|
|
||||||
label: 'Logs pro Tag',
|
|
||||||
data: <?php echo json_encode($data); ?>,
|
|
||||||
borderColor: 'rgba(75, 192, 192, 1)',
|
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
|
||||||
fill: true,
|
|
||||||
tension: 0
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Logs pro Tag'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
position: 'top'
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
pan: {
|
|
||||||
enabled: true,
|
|
||||||
mode: 'x',
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
wheel: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
mode: 'x',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Datum'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
min: 0,
|
|
||||||
max: 100,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Anzahl Logs'
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
stepSize: 20
|
|
||||||
},
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$conn = db_connect();
|
|
||||||
|
|
||||||
// Dev-IPs
|
|
||||||
$devips = (new getSpecificData())->getWhitelistedIPs();
|
|
||||||
$devips_sql = "'" . implode("','", $devips) . "'";
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
$filter = $_GET['filter'] ?? 'today';
|
|
||||||
$customQuery = $_POST['custom_query'] ?? '';
|
|
||||||
$useCustom = false;
|
|
||||||
|
|
||||||
// Custom Query Schutz
|
|
||||||
if ($customQuery) {
|
|
||||||
$forbidden = ['DELETE', 'UPDATE', 'INSERT', 'DROP', 'ALTER', 'TRUNCATE'];
|
|
||||||
$upper = strtoupper($customQuery);
|
|
||||||
|
|
||||||
foreach ($forbidden as $bad) {
|
|
||||||
if (strpos($upper, $bad) !== false) {
|
|
||||||
echo "<b style='color:red'>Gefährliche Query blockiert ($bad)</b>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripos(trim($customQuery), "SELECT") !== 0) {
|
|
||||||
echo "<b style='color:red'>Nur SELECT erlaubt</b>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = $customQuery;
|
|
||||||
$useCustom = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard Queries
|
|
||||||
if (!$useCustom) {
|
|
||||||
switch ($filter) {
|
|
||||||
case 'today':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE DATE(FIRST_REQUEST) = CURDATE()
|
|
||||||
AND (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
AND CLIENT_IP NOT IN ($devips_sql)
|
|
||||||
ORDER BY FIRST_REQUEST DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'counterperip':
|
|
||||||
$sql = "
|
|
||||||
SELECT CLIENT_IP, LAND, USER_AGENT, SUM(counter) AS gesamt_counter
|
|
||||||
FROM logs
|
|
||||||
WHERE (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
AND CLIENT_IP NOT IN ($devips_sql)
|
|
||||||
GROUP BY CLIENT_IP
|
|
||||||
ORDER BY gesamt_counter DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node1':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node1'
|
|
||||||
AND (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node2':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node2'
|
|
||||||
AND (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node3':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node3'
|
|
||||||
AND (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node4':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node4'
|
|
||||||
AND (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE (WEBSITE = 'backend.blackpi.ipv64.net')
|
|
||||||
AND CLIENT_IP NOT IN ($devips_sql)
|
|
||||||
ORDER BY FIRST_REQUEST DESC
|
|
||||||
";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<h1>📊 Logs</h1>
|
|
||||||
|
|
||||||
<form method="GET" action="index.php">
|
|
||||||
<input type="hidden" name="service" value="<?= htmlspecialchars($_GET['service'] ?? 'logs') ?>">
|
|
||||||
|
|
||||||
<select name="filter" onchange="this.form.submit()">
|
|
||||||
<?php
|
|
||||||
$options = ['alle','today','node1','node2','node3','node4','counterperip'];
|
|
||||||
foreach ($options as $opt) {
|
|
||||||
$sel = ($filter === $opt) ? 'selected' : '';
|
|
||||||
echo "<option value='$opt' $sel>$opt</option>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- <br>
|
|
||||||
|
|
||||||
<form method="POST">
|
|
||||||
<textarea name="custom_query" style="width:100%;height:60px;"><?= htmlspecialchars($customQuery) ?></textarea>
|
|
||||||
<br>
|
|
||||||
<button type="submit">Query ausführen</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<hr> -->
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$daten = [];
|
|
||||||
|
|
||||||
if ($erg = $conn->query($sql)) {
|
|
||||||
// echo "<pre style='background:var(--bg-soft,#f4f4f4);padding:10px;'>$sql</pre>";
|
|
||||||
|
|
||||||
while ($row = $erg->fetch_object()) {
|
|
||||||
$daten[] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if (!$daten): ?>
|
|
||||||
<b>Keine Daten gefunden</b>
|
|
||||||
<?php else: ?>
|
|
||||||
|
|
||||||
<div style="overflow-x:auto;">
|
|
||||||
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
|
||||||
<tr>
|
|
||||||
<?php foreach ($daten[0] as $key => $_): ?>
|
|
||||||
<th><?= htmlspecialchars($key) ?></th>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<?php foreach ($daten as $row): ?>
|
|
||||||
<tr>
|
|
||||||
<?php foreach ($row as $val): ?>
|
|
||||||
<td><?= htmlspecialchars($val ?? '') ?></td>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
<?php
|
|
||||||
$conn = db_connect();
|
|
||||||
$view = $_GET['view'] ?? 'show';
|
|
||||||
$selectedService = $_GET['service'];
|
|
||||||
|
|
||||||
// --- LOGIK: Überstunden-Eintrag (Abbau) speichern ---
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_overtime'])) {
|
|
||||||
$datum = $_POST['ot_date'];
|
|
||||||
$minuten = intval($_POST['ot_minutes']);
|
|
||||||
$grund = $conn->real_escape_string($_POST['ot_reason']);
|
|
||||||
$insert_sql = "INSERT INTO Ueberstunden (DATUM, MINUTEN, GRUND) VALUES ('$datum', $minuten, '$grund')";
|
|
||||||
$conn->query($insert_sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Daten laden ---
|
|
||||||
$daten = [];
|
|
||||||
$sql = "SELECT `DATUM`, `PROZESS_1`, `PROZESS_2`, `PROZESS_3`, `PROZESS_4`, `BEGINN`, `ENDE_REAL`, `OVERTIME` FROM Firmenberichte ORDER BY `DATUM` DESC";
|
|
||||||
if ($erg = $conn->query($sql)) {
|
|
||||||
while ($datensatz = $erg->fetch_object()) { $daten[] = $datensatz; }
|
|
||||||
$erg->free();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Gesamtsumme berechnen ---
|
|
||||||
$minuten_berichte = 0;
|
|
||||||
$res1 = $conn->query("SELECT SUM(OVERTIME) AS summe FROM Firmenberichte");
|
|
||||||
if ($row = $res1->fetch_object()) { $minuten_berichte = intval($row->summe); }
|
|
||||||
|
|
||||||
$minuten_extra = 0;
|
|
||||||
$res2 = $conn->query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden");
|
|
||||||
if ($row = $res2->fetch_object()) { $minuten_extra = intval($row->summe); }
|
|
||||||
|
|
||||||
$gesamt_minuten = $minuten_berichte - $minuten_extra;
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="arbeitsberichte-wrapper">
|
|
||||||
<div style="display: flex; gap: 10px; margin-bottom: 25px;">
|
|
||||||
<a href="?service=<?= $selectedService ?>&view=rechner" style="text-decoration:none;">
|
|
||||||
<button class="nav-btn <?= $view == 'rechner' ? 'active' : '' ?>" style="padding: 10px 20px; cursor: pointer; border: none; border-radius: 4px; font-weight: bold;
|
|
||||||
background: <?= $view == 'rechner' ? '#007bff' : 'rgba(150,150,150,0.2)' ?>;
|
|
||||||
color: <?= $view == 'rechner' ? '#fff' : '#fff' ?>;">
|
|
||||||
Live-Timer
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
<a href="?service=<?= $selectedService ?>&view=show" style="text-decoration:none;">
|
|
||||||
<button class="nav-btn <?= $view == 'show' ? 'active' : '' ?>" style="padding: 10px 20px; cursor: pointer; border: none; border-radius: 4px; font-weight: bold;
|
|
||||||
background: <?= $view == 'show' ? '#007bff' : 'rgba(150,150,150,0.2)' ?>;
|
|
||||||
color: <?= $view == 'show' ? '#fff' : '#fff' ?>;">
|
|
||||||
Übersicht
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($view === 'rechner'): ?>
|
|
||||||
<div class="overtime-container" style="color: inherit;">
|
|
||||||
<h2 style="color: inherit;">Überstunden Rechner</h2>
|
|
||||||
<label for="startTime">Arbeitsstartzeit:</label>
|
|
||||||
<input type="time" id="startTime" step="60"
|
|
||||||
style="background: rgba(150,150,150,0.1); color: inherit; border: 1px solid #555;">
|
|
||||||
<button id="startBtn">Timer starten</button>
|
|
||||||
|
|
||||||
<div id="resultBox" class="overtime-result">
|
|
||||||
<p style="margin:0; font-size: 0.9rem;">Überstunden / Restzeit:</p>
|
|
||||||
<div id="result">0:00</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php else: ?>
|
|
||||||
<div
|
|
||||||
style="background: rgba(150,150,150,0.05); padding: 25px; border-radius: 10px; margin-bottom: 25px; border: 1px solid rgba(150,150,150,0.2); border-left: 6px solid #007bff;">
|
|
||||||
<div style="margin-bottom: 20px;">
|
|
||||||
<span style="color: #888; font-size: 0.8rem; font-weight: bold; text-transform: uppercase;">Aktueller
|
|
||||||
Stand</span>
|
|
||||||
<h2 style="margin: 5px 0 0 0; font-size: 2rem; color: <?= $gesamt_minuten >= 0 ? '#28a745' : '#dc3545' ?>;">
|
|
||||||
<?= intdiv($gesamt_minuten, 60) ?>h <?= abs($gesamt_minuten % 60) ?>min
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="POST" style="display: flex; gap: 15px; flex-wrap: wrap; align-items: flex-end;">
|
|
||||||
<input type="hidden" name="add_overtime" value="1">
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 5px;">
|
|
||||||
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Datum</label>
|
|
||||||
<input type="date" name="ot_date" value="<?= date('Y-m-d') ?>" required
|
|
||||||
style="padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 5px;">
|
|
||||||
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Minuten Abbau</label>
|
|
||||||
<input type="number" name="ot_minutes" placeholder="480" required
|
|
||||||
style="width: 100px; padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 5px; flex-grow: 1;">
|
|
||||||
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Grund</label>
|
|
||||||
<input type="text" name="ot_reason" placeholder="z.B. Abbau" required
|
|
||||||
style="padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
|
||||||
</div>
|
|
||||||
<button type="submit"
|
|
||||||
style="background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; font-weight: bold; cursor: pointer;">
|
|
||||||
Eintrag speichern
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table style="width: 100%; border-collapse: collapse; color: inherit;">
|
|
||||||
<thead>
|
|
||||||
<tr style="background: rgba(0,123,255,0.15); text-align: left;">
|
|
||||||
<th style="padding: 12px; border-bottom: 2px solid #007bff;">Datum</th>
|
|
||||||
<th style="padding: 12px; border-bottom: 2px solid #007bff;">Prozesse</th>
|
|
||||||
<th style="padding: 12px; border-bottom: 2px solid #007bff;">Zeitraum</th>
|
|
||||||
<th style="padding: 12px; border-bottom: 2px solid #007bff;">Overtime</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($daten as $b): ?>
|
|
||||||
<tr style="border-bottom: 1px solid rgba(150,150,150,0.2); vertical-align: top;">
|
|
||||||
<td style="padding: 12px; white-space: nowrap;"><?= htmlspecialchars($b->DATUM) ?></td>
|
|
||||||
<td style="padding: 12px;">
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 3px; font-size: 0.85rem; opacity: 0.8;">
|
|
||||||
<?php for($i=1; $i<=4; $i++): $p = "PROZESS_$i"; ?>
|
|
||||||
<?php if(!empty($b->$p)): ?> <span>• <?= htmlspecialchars($b->$p) ?></span> <?php endif; ?>
|
|
||||||
<?php endfor; ?>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td style="padding: 12px; font-size: 0.9rem; white-space: nowrap;"><?= $b->BEGINN ?> -
|
|
||||||
<?= $b->ENDE_REAL ?></td>
|
|
||||||
<td style="padding: 12px; font-weight: bold; color: <?= $b->OVERTIME >= 0 ? '#28a745' : '#dc3545' ?>;">
|
|
||||||
<?= $b->OVERTIME ?> min
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Live-Rechner Logik
|
|
||||||
if (document.getElementById('startBtn')) {
|
|
||||||
document.getElementById('startBtn').addEventListener('click', function() {
|
|
||||||
const startTimeInput = document.getElementById('startTime').value;
|
|
||||||
if (!startTimeInput) return;
|
|
||||||
const [hours, minutes] = startTimeInput.split(':').map(Number);
|
|
||||||
let startDate = new Date();
|
|
||||||
startDate.setHours(hours, minutes, 0);
|
|
||||||
const targetWorkMs = 8.5 * 60 * 60 * 1000;
|
|
||||||
const resultEl = document.getElementById('result');
|
|
||||||
const resultBox = document.getElementById('resultBox');
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
const diffMs = (new Date() - startDate) - targetWorkMs;
|
|
||||||
const negative = diffMs < 0;
|
|
||||||
const absMs = Math.abs(diffMs);
|
|
||||||
const h = Math.floor(absMs / 3600000);
|
|
||||||
const m = Math.floor((absMs % 3600000) / 60000);
|
|
||||||
resultEl.innerText = (negative ? '-' : '+') + h + ":" + (m < 10 ? '0' : '') + m;
|
|
||||||
if (negative) {
|
|
||||||
resultBox.classList.add('negative');
|
|
||||||
} else {
|
|
||||||
resultBox.classList.remove('negative');
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Zugriff nur, wenn angemeldet
|
|
||||||
if (empty($_SESSION['user_logged_in'])) {
|
|
||||||
echo "<p>Zugriff verweigert.</p>";
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn = db_connect();
|
|
||||||
|
|
||||||
$conn->query("SET @row := 0");
|
|
||||||
|
|
||||||
$wochenQuery = "
|
|
||||||
SELECT MIN(DATUM) AS start_date,
|
|
||||||
MAX(DATUM) AS end_date
|
|
||||||
FROM (
|
|
||||||
SELECT DATUM,
|
|
||||||
DATE_SUB(DATUM, INTERVAL (@row := @row + 1) DAY) AS grp
|
|
||||||
FROM Firmenberichte
|
|
||||||
ORDER BY DATUM
|
|
||||||
) AS t
|
|
||||||
GROUP BY grp
|
|
||||||
ORDER BY start_date;
|
|
||||||
";
|
|
||||||
|
|
||||||
$resultWochen = $conn->query($wochenQuery);
|
|
||||||
|
|
||||||
$wochenArray = [];
|
|
||||||
while($row = $resultWochen->fetch_assoc()){
|
|
||||||
$wochenArray[] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard-Datenbereich: letzte 5 Tage
|
|
||||||
$startDate = $_POST['start_date'] ?? date('Y-m-d', strtotime('-5 days'));
|
|
||||||
$endDate = $_POST['end_date'] ?? date('Y-m-d');
|
|
||||||
|
|
||||||
$berichteQuery = "SELECT `DATUM`, `PROZESS_1`, `PROZESS_2`, `PROZESS_3`, `PROZESS_4`
|
|
||||||
FROM Firmenberichte
|
|
||||||
WHERE `DATUM` BETWEEN ? AND ?";
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($berichteQuery);
|
|
||||||
$stmt->bind_param("ss", $startDate, $endDate);
|
|
||||||
$stmt->execute();
|
|
||||||
$result = $stmt->get_result();
|
|
||||||
|
|
||||||
$berichteArray = [];
|
|
||||||
while($row = $result->fetch_assoc()){
|
|
||||||
$berichteArray[] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt->close();
|
|
||||||
$conn->close();
|
|
||||||
|
|
||||||
// PDF-Generierung
|
|
||||||
function generatePDF($berichteArray) {
|
|
||||||
$Einzelstunden = "2";
|
|
||||||
$Gesamtstunden = "8";
|
|
||||||
$data = [
|
|
||||||
'Text1' => '', // Nr
|
|
||||||
'Text2' => $berichteArray[0]['DATUM'] ?? '',
|
|
||||||
'Text3' => end($berichteArray)['DATUM'] ?? '',
|
|
||||||
'Text4' => 'Eckhardt',
|
|
||||||
'Text5' => 'Bruno Renato',
|
|
||||||
'Text6' => '1',
|
|
||||||
];
|
|
||||||
|
|
||||||
$fieldIndex = 7;
|
|
||||||
foreach ($berichteArray as $bericht) {
|
|
||||||
for ($i = 1; $i <= 4; $i++) {
|
|
||||||
$data["Text$fieldIndex"] = $bericht["PROZESS_$i"] ?? '';
|
|
||||||
$fieldIndex++;
|
|
||||||
$data["Text$fieldIndex"] = $Einzelstunden;
|
|
||||||
$fieldIndex++;
|
|
||||||
}
|
|
||||||
$data["Text$fieldIndex"] = $Gesamtstunden;
|
|
||||||
$fieldIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fdfFields = array_map(function($field, $value){
|
|
||||||
$value = str_replace(['(', ')'], ['\(', '\)'], $value);
|
|
||||||
return "<< /T ($field) /V ($value) >>";
|
|
||||||
}, array_keys($data), $data);
|
|
||||||
|
|
||||||
$fdf = "%FDF-1.2\n1 0 obj\n<<\n/FDF << /Fields [\n" .
|
|
||||||
implode("\n", $fdfFields) .
|
|
||||||
"\n] >> >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF";
|
|
||||||
|
|
||||||
$fdfFile = tempnam(sys_get_temp_dir(), 'fdf_') . '.fdf';
|
|
||||||
file_put_contents($fdfFile, $fdf);
|
|
||||||
|
|
||||||
$inputPdf = (new getSpecificData())->getDocumentRoot() . '/templates/ausbildungsnachweis.pdf';
|
|
||||||
ConsoleLog((new getSpecificData())->getDocumentRoot() . '/templates/ausbildungsnachweis.pdf');
|
|
||||||
$outputPdf = (new getSpecificData())->getDocumentRoot() . '/static/ausbildungsnachweis_' . $berichteArray[0]['DATUM'] . '-' . end($berichteArray)['DATUM'] . '.pdf';
|
|
||||||
|
|
||||||
exec("pdftk $inputPdf fill_form $fdfFile output $outputPdf flatten");
|
|
||||||
|
|
||||||
unlink($fdfFile);
|
|
||||||
|
|
||||||
return $outputPdf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle PDF generation
|
|
||||||
$pdfFile = null;
|
|
||||||
if (isset($_POST['generate_pdf']) && count($berichteArray) > 0) {
|
|
||||||
$pdfFile = generatePDF($berichteArray);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="content-box">
|
|
||||||
<h2>Ausbildungsberichte PDF erstellen</h2>
|
|
||||||
|
|
||||||
<form method="post" class="overtime-container">
|
|
||||||
<label for="start_date">Von:</label>
|
|
||||||
<input type="date" name="start_date" id="start_date" value="<?= htmlspecialchars($startDate) ?>">
|
|
||||||
|
|
||||||
<label for="end_date">Bis:</label>
|
|
||||||
<input type="date" name="end_date" id="end_date" value="<?= htmlspecialchars($endDate) ?>">
|
|
||||||
|
|
||||||
<button type="submit" name="generate_pdf">PDF erstellen</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<?php if ($pdfFile): ?>
|
|
||||||
<div class="overtime-result">
|
|
||||||
<?= htmlspecialchars(basename($pdfFile)) ?> erfolgreich erstellt!
|
|
||||||
</div>
|
|
||||||
<?php elseif(isset($_POST['generate_pdf'])): ?>
|
|
||||||
<div class="overtime-result negative">
|
|
||||||
Keine Berichte im gewählten Zeitraum gefunden.
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(!empty($wochenArray)): ?>
|
|
||||||
<div class="wochen-liste">
|
|
||||||
<h3>Verfügbare Wochenabschnitte:</h3>
|
|
||||||
<ul>
|
|
||||||
<?php
|
|
||||||
$outputDir = (new getSpecificData())->getDocumentRoot() . '/static/';
|
|
||||||
foreach($wochenArray as $woche):
|
|
||||||
$pdfName = 'ausbildungsnachweis_' . $woche['start_date'] . '-' . $woche['end_date'] . '.pdf';
|
|
||||||
$pdfPath = $outputDir . $pdfName;
|
|
||||||
$exists = file_exists($pdfPath);
|
|
||||||
?>
|
|
||||||
<li>
|
|
||||||
<code><?= htmlspecialchars($woche['start_date']) ?></code> bis
|
|
||||||
<code><?= htmlspecialchars($woche['end_date']) ?></code>
|
|
||||||
<?php if($exists): ?>
|
|
||||||
<span style="color:green;">(PDF vorhanden)</span>
|
|
||||||
<a href="<?= htmlspecialchars('/static/' . $pdfName) ?>" download class="btn btn-success">
|
|
||||||
Download
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
<?php else: ?>
|
|
||||||
<span style="color:red;">(noch keine PDF)</span>
|
|
||||||
<form method="post" style="display:inline;">
|
|
||||||
<input type="hidden" name="start_date" value="<?= htmlspecialchars($woche['start_date']) ?>">
|
|
||||||
<input type="hidden" name="end_date" value="<?= htmlspecialchars($woche['end_date']) ?>">
|
|
||||||
<button type="submit" name="generate_pdf" class="btn btn-primary">
|
|
||||||
PDF erzeugen
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</li>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
session_destroy();
|
|
||||||
|
|
||||||
header('Location: login.php');
|
|
||||||
exit;
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$conn = db_connect();
|
|
||||||
|
|
||||||
// Dev-IPs
|
|
||||||
$devips = (new getSpecificData())->getWhitelistedIPs();
|
|
||||||
$devips_sql = "'" . implode("','", $devips) . "'";
|
|
||||||
|
|
||||||
// Filter & Custom Query
|
|
||||||
$customQuery = $_POST['custom_query'] ?? '';
|
|
||||||
$sql = '';
|
|
||||||
$useCustom = false;
|
|
||||||
|
|
||||||
// Custom Query Schutz
|
|
||||||
if ($customQuery) {
|
|
||||||
$forbidden = ['DELETE', 'UPDATE', 'INSERT', 'DROP', 'ALTER', 'TRUNCATE'];
|
|
||||||
$upper = strtoupper($customQuery);
|
|
||||||
|
|
||||||
foreach ($forbidden as $bad) {
|
|
||||||
if (strpos($upper, $bad) !== false) {
|
|
||||||
echo "<b style='color:red'>Gefährliche Query blockiert ($bad)</b>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripos(trim($customQuery), "SELECT") !== 0) {
|
|
||||||
echo "<b style='color:red'>Nur SELECT erlaubt</b>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = $customQuery;
|
|
||||||
$useCustom = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard Query (falls kein Custom Query)
|
|
||||||
if (!$useCustom) {
|
|
||||||
// Einfache Standard-Query für Logs ohne Dev-IPs
|
|
||||||
$sql = "
|
|
||||||
SELECT WEBSITE, REQUEST_URL, FIRST_REQUEST, COUNTER
|
|
||||||
FROM logs
|
|
||||||
WHERE CLIENT_IP = '" . (new getSpecificData())->getClientIP() . "'
|
|
||||||
ORDER BY FIRST_REQUEST DESC
|
|
||||||
";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST['delete_logs'])) {
|
|
||||||
DatabaseDelete(
|
|
||||||
"logs",
|
|
||||||
"CLIENT_IP = ?",
|
|
||||||
(new getSpecificData())->getClientIP()
|
|
||||||
);
|
|
||||||
header("Location: " . (new getSpecificData())->getReferer());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<h1>📊 Logs</h1>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$daten = [];
|
|
||||||
|
|
||||||
if ($erg = $conn->query($sql)) {
|
|
||||||
while ($row = $erg->fetch_object()) {
|
|
||||||
$daten[] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if (!$daten): ?>
|
|
||||||
<b>Keine Daten gefunden</b>
|
|
||||||
<?php else: ?>
|
|
||||||
|
|
||||||
<div style="overflow-x:auto;">
|
|
||||||
<?php if ((new getSpecificData())->getIsAdmin()): ?>
|
|
||||||
<form method="post">
|
|
||||||
<button name="delete_logs" class="btn-delete" style="margin-bottom: 10px">Lösche Logs</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
|
||||||
<tr>
|
|
||||||
<?php foreach ($daten[0] as $key => $_): ?>
|
|
||||||
<th><?= htmlspecialchars($key) ?></th>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<?php foreach ($daten as $row): ?>
|
|
||||||
<tr>
|
|
||||||
<?php foreach ($row as $val): ?>
|
|
||||||
<td><?= htmlspecialchars($val ?? '') ?></td>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
<?php
|
|
||||||
$conn = db_connect();
|
|
||||||
|
|
||||||
// Dataset: Gesamte Logs
|
|
||||||
$sql_total = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
|
||||||
$result_total = $conn->query($sql_total);
|
|
||||||
|
|
||||||
$totalDataAssoc = [];
|
|
||||||
$allLabels = [];
|
|
||||||
|
|
||||||
if ($result_total->num_rows > 0) {
|
|
||||||
while($row = $result_total->fetch_assoc()) {
|
|
||||||
$date = $row['tag'];
|
|
||||||
$totalDataAssoc[$date] = (int)$row['anzahl'];
|
|
||||||
$allLabels[] = $date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nodes
|
|
||||||
$nodes = ['node1','node2','node3','node4'];
|
|
||||||
$nodeDataAssoc = [];
|
|
||||||
|
|
||||||
foreach ($nodes as $node) {
|
|
||||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE SERVER_IP = '$node' GROUP BY DATE(FIRST_REQUEST)";
|
|
||||||
$result = $conn->query($sql);
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
if ($result->num_rows > 0) {
|
|
||||||
while($row = $result->fetch_assoc()) {
|
|
||||||
$date = $row['tag'];
|
|
||||||
$data[$date] = (int)$row['anzahl'];
|
|
||||||
if (!in_array($date, $allLabels)) {
|
|
||||||
$allLabels[] = $date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$nodeDataAssoc[$node] = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alle Labels chronologisch sortieren
|
|
||||||
sort($allLabels);
|
|
||||||
|
|
||||||
// Finales Array: Gesamt + Nodes
|
|
||||||
$finalTotalData = [];
|
|
||||||
$finalNodeData = [];
|
|
||||||
|
|
||||||
foreach ($allLabels as $label) {
|
|
||||||
$finalTotalData[] = isset($totalDataAssoc[$label]) ? $totalDataAssoc[$label] : 0;
|
|
||||||
|
|
||||||
foreach ($nodes as $node) {
|
|
||||||
if (!isset($finalNodeData[$node])) $finalNodeData[$node] = [];
|
|
||||||
$finalNodeData[$node][] = isset($nodeDataAssoc[$node][$label]) ? $nodeDataAssoc[$node][$label] : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->close();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Logs Chart</title>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<canvas id="logChart" width="900" height="500"></canvas>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const ctx = document.getElementById('logChart').getContext('2d');
|
|
||||||
|
|
||||||
const logChart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: <?php echo json_encode($allLabels); ?>,
|
|
||||||
datasets: [{
|
|
||||||
label: 'Gesamt',
|
|
||||||
data: <?php echo json_encode($finalTotalData); ?>,
|
|
||||||
borderColor: 'rgb(255, 0, 0)',
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
|
||||||
fill: false,
|
|
||||||
tension: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Node1',
|
|
||||||
data: <?php echo json_encode($finalNodeData['node1']); ?>,
|
|
||||||
borderColor: 'rgba(75, 192, 192, 1)',
|
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
|
||||||
fill: false,
|
|
||||||
tension: 0,
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Node2',
|
|
||||||
data: <?php echo json_encode($finalNodeData['node2']); ?>,
|
|
||||||
borderColor: 'rgba(255, 99, 132, 1)',
|
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
|
||||||
fill: false,
|
|
||||||
tension: 0,
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Node3',
|
|
||||||
data: <?php echo json_encode($finalNodeData['node3']); ?>,
|
|
||||||
borderColor: 'rgba(255, 206, 86, 1)',
|
|
||||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
|
||||||
fill: false,
|
|
||||||
tension: 0,
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Node4',
|
|
||||||
data: <?php echo json_encode($finalNodeData['node4']); ?>,
|
|
||||||
borderColor: 'rgba(54, 162, 235, 1)',
|
|
||||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
|
||||||
fill: false,
|
|
||||||
tension: 0,
|
|
||||||
hidden: true,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Logs pro Tag nach Node und Gesamt'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
position: 'top'
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
pan: {
|
|
||||||
enabled: true,
|
|
||||||
mode: 'x'
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
wheel: {
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
mode: 'x'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Datum'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Anzahl Logs'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$conn = db_connect();
|
|
||||||
|
|
||||||
// Dev-IPs
|
|
||||||
$devips = (new getSpecificData())->getWhitelistedIPs();
|
|
||||||
$devips_sql = "'" . implode("','", $devips) . "'";
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
$filter = $_GET['filter'] ?? 'today';
|
|
||||||
$customQuery = $_POST['custom_query'] ?? '';
|
|
||||||
$useCustom = false;
|
|
||||||
|
|
||||||
// Custom Query Schutz
|
|
||||||
if ($customQuery) {
|
|
||||||
$forbidden = ['DELETE', 'UPDATE', 'INSERT', 'DROP', 'ALTER', 'TRUNCATE'];
|
|
||||||
$upper = strtoupper($customQuery);
|
|
||||||
|
|
||||||
foreach ($forbidden as $bad) {
|
|
||||||
if (strpos($upper, $bad) !== false) {
|
|
||||||
echo "<b style='color:red'>Gefährliche Query blockiert ($bad)</b>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripos(trim($customQuery), "SELECT") !== 0) {
|
|
||||||
echo "<b style='color:red'>Nur SELECT erlaubt</b>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = $customQuery;
|
|
||||||
$useCustom = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard Queries
|
|
||||||
if (!$useCustom) {
|
|
||||||
switch ($filter) {
|
|
||||||
case 'today':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE CLIENT_IP NOT IN ($devips_sql)
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
AND DATE(FIRST_REQUEST) = CURDATE()
|
|
||||||
ORDER BY FIRST_REQUEST DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'counterperip':
|
|
||||||
$sql = "
|
|
||||||
SELECT CLIENT_IP, LAND, USER_AGENT, SUM(counter) AS gesamt_counter
|
|
||||||
FROM logs
|
|
||||||
WHERE CLIENT_IP NOT IN ($devips_sql)
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
GROUP BY CLIENT_IP
|
|
||||||
ORDER BY gesamt_counter DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node1':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node1'
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node2':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node2'
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node3':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node3'
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'node4':
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE `SERVER_IP` = 'node4'
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
ORDER BY `FIRST_REQUEST` DESC
|
|
||||||
";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$sql = "
|
|
||||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
|
||||||
FROM logs
|
|
||||||
WHERE CLIENT_IP NOT IN ($devips_sql)
|
|
||||||
AND (WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)
|
|
||||||
ORDER BY FIRST_REQUEST DESC
|
|
||||||
";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<h1>📊 Logs</h1>
|
|
||||||
|
|
||||||
<form method="GET" action="index.php">
|
|
||||||
<input type="hidden" name="service" value="<?= htmlspecialchars($_GET['service'] ?? 'logs') ?>">
|
|
||||||
|
|
||||||
<select name="filter" onchange="this.form.submit()">
|
|
||||||
<?php
|
|
||||||
$options = ['alle','today','node1','node2','node3','node4','counterperip'];
|
|
||||||
foreach ($options as $opt) {
|
|
||||||
$sel = ($filter === $opt) ? 'selected' : '';
|
|
||||||
echo "<option value='$opt' $sel>$opt</option>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- <br>
|
|
||||||
|
|
||||||
<form method="POST">
|
|
||||||
<textarea name="custom_query" style="width:100%;height:60px;"><?= htmlspecialchars($customQuery) ?></textarea>
|
|
||||||
<br>
|
|
||||||
<button type="submit">Query ausführen</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<hr> -->
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$daten = [];
|
|
||||||
|
|
||||||
if ($erg = $conn->query($sql)) {
|
|
||||||
// echo "<pre style='background:var(--bg-soft,#f4f4f4);padding:10px;'>$sql</pre>";
|
|
||||||
|
|
||||||
while ($row = $erg->fetch_object()) {
|
|
||||||
$daten[] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if (!$daten): ?>
|
|
||||||
<b>Keine Daten gefunden</b>
|
|
||||||
<?php else: ?>
|
|
||||||
|
|
||||||
<div style="overflow-x:auto;">
|
|
||||||
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
|
||||||
<tr>
|
|
||||||
<?php foreach ($daten[0] as $key => $_): ?>
|
|
||||||
<th><?= htmlspecialchars($key) ?></th>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<?php foreach ($daten as $row): ?>
|
|
||||||
<tr>
|
|
||||||
<?php foreach ($row as $val): ?>
|
|
||||||
<td><?= htmlspecialchars($val ?? '') ?></td>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
@@ -1,317 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$configFile = 'config.php';
|
|
||||||
$backupDir = 'config_backups';
|
|
||||||
|
|
||||||
if (!is_dir($backupDir)) {
|
|
||||||
mkdir($backupDir, 0755, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
require $configFile;
|
|
||||||
|
|
||||||
if (isset($_POST['password'])) {
|
|
||||||
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
|
||||||
$_SESSION['logged_in'] = true;
|
|
||||||
} else {
|
|
||||||
$error = "Falsches Passwort!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET['logout'])) {
|
|
||||||
session_destroy();
|
|
||||||
header("Location: " . $_SERVER['PHP_SELF']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST['logout'])) {
|
|
||||||
unset($_SESSION['logged_in']);
|
|
||||||
header("Location: " . (new getSpecificData())->getReferer());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST['save']) && ($_SESSION['logged_in'] ?? false)) {
|
|
||||||
copy($configFile, $backupDir . '/config_' . date('Y-m-d_H-i-s') . '.php');
|
|
||||||
|
|
||||||
$newServices = [];
|
|
||||||
$serviceToGroupMapping = [];
|
|
||||||
if(isset($_POST['services'])){
|
|
||||||
foreach ($_POST['services'] as $id => $s) {
|
|
||||||
$finalId = !empty($s['new_id']) ? trim($s['new_id']) : $id;
|
|
||||||
if (empty($finalId)) continue;
|
|
||||||
$newServices[$finalId] = [
|
|
||||||
'name' => $s['name'],
|
|
||||||
'active' => isset($s['active']),
|
|
||||||
'adminOnly' => isset($s['adminOnly'])
|
|
||||||
];
|
|
||||||
if(!empty($s['targetGroup'])) {
|
|
||||||
$serviceToGroupMapping[$s['targetGroup']][] = $finalId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newServiceGroups = [];
|
|
||||||
if(isset($_POST['serviceGroups'])){
|
|
||||||
foreach ($_POST['serviceGroups'] as $id => $groupData) {
|
|
||||||
$finalId = !empty($groupData['new_id']) ? trim($groupData['new_id']) : $id;
|
|
||||||
if (empty($finalId)) continue;
|
|
||||||
$currentServices = $serviceToGroupMapping[$finalId] ?? ($serviceGroups[$id]['services'] ?? []);
|
|
||||||
$newServiceGroups[$finalId] = [
|
|
||||||
'label' => $groupData['label'],
|
|
||||||
'defaultOpen' => isset($groupData['defaultOpen']),
|
|
||||||
'services' => array_values(array_unique($currentServices))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newLinkGroups = [];
|
|
||||||
if(isset($_POST['linkGroups'])){
|
|
||||||
foreach ($_POST['linkGroups'] as $id => $lg) {
|
|
||||||
$finalId = !empty($lg['new_id']) ? trim($lg['new_id']) : $id;
|
|
||||||
if (empty($finalId)) continue;
|
|
||||||
$newLinkGroups[$finalId] = [
|
|
||||||
'label' => $lg['label'],
|
|
||||||
'color' => $lg['color'],
|
|
||||||
'defaultOpen' => isset($lg['defaultOpen'])
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newLinkButtons = [];
|
|
||||||
if(isset($_POST['linkButtons'])){
|
|
||||||
foreach ($_POST['linkButtons'] as $btn) {
|
|
||||||
if (empty($btn['label'])) continue;
|
|
||||||
$newLinkButtons[] = [
|
|
||||||
'label' => $btn['label'],
|
|
||||||
'url' => $btn['url'],
|
|
||||||
'newTab' => isset($btn['newTab']),
|
|
||||||
'adminOnly' => isset($btn['adminOnly']),
|
|
||||||
'group' => $btn['group']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = "<?php\n\n";
|
|
||||||
$content .= '$superuser = ' . var_export($superuser, true) . ";\n\n";
|
|
||||||
$content .= '$serviceGroups = ' . var_export($newServiceGroups, true) . ";\n\n";
|
|
||||||
$content .= '$services = ' . var_export($newServices, true) . ";\n\n";
|
|
||||||
$content .= '$linkGroups = ' . var_export($newLinkGroups, true) . ";\n\n";
|
|
||||||
$content .= '$linkButtons = ' . var_export($newLinkButtons, true) . ";\n";
|
|
||||||
|
|
||||||
if (file_put_contents($configFile, $content)) {
|
|
||||||
if (function_exists('opcache_invalidate')) { @opcache_invalidate($configFile, true); }
|
|
||||||
$_SESSION['status_message'] = "✅ Konfiguration erfolgreich aktualisiert!";
|
|
||||||
header("Location: " . $_SERVER['PHP_SELF']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = $_SESSION['status_message'] ?? null;
|
|
||||||
unset($_SESSION['status_message']);
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Admin Site Control</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="<?= ($_SESSION['dark_mode'] ?? false) ? 'dark-mode' : '' ?>">
|
|
||||||
<div class="main">
|
|
||||||
<?php if (!($_SESSION['logged_in'] ?? false)): ?>
|
|
||||||
<div class="login-container">
|
|
||||||
<div class="login-box">
|
|
||||||
<form method="post">
|
|
||||||
<h2>🔐 Login</h2>
|
|
||||||
<?php if(isset($error)): ?>
|
|
||||||
<div class="status-msg" style="background:#dc3545; color:#fff;"><?= htmlspecialchars($error) ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<input type="password" name="password" placeholder="Passwort" required>
|
|
||||||
<button type="submit">Einloggen</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="content">
|
|
||||||
<h2>Seitenverwaltung</h2>
|
|
||||||
<form method="post">
|
|
||||||
<button type="submit" name="logout" id="logoutSiteButton">Logout</button>
|
|
||||||
</form>
|
|
||||||
<?php if($message): ?><div class="status-msg"><?= htmlspecialchars($message) ?></div><?php endif; ?>
|
|
||||||
|
|
||||||
<form method="post">
|
|
||||||
|
|
||||||
<!-- Sidebar Gruppen -->
|
|
||||||
<h2>📦 Sidebar Gruppen
|
|
||||||
<button type="button" class="btn-add" onclick="addNew('serviceGroup')">+ Neue Gruppe</button>
|
|
||||||
</h2>
|
|
||||||
<div class="admin-grid" id="container-serviceGroup">
|
|
||||||
<?php foreach ($serviceGroups as $id => $group): ?>
|
|
||||||
<div class="admin-card">
|
|
||||||
<span class="badge">ID: <?= htmlspecialchars($id) ?></span>
|
|
||||||
<input type="text" name="serviceGroups[<?= $id ?>][label]"
|
|
||||||
value="<?= htmlspecialchars($group['label']) ?>" placeholder="Anzeigename">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="serviceGroups[<?= $id ?>][defaultOpen]"
|
|
||||||
<?= ($group['defaultOpen'] ?? false) ? 'checked' : '' ?>>
|
|
||||||
Offen
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Sidebar Services -->
|
|
||||||
<h2>🖥 Sidebar Services
|
|
||||||
<button type="button" class="btn-add" onclick="addNew('service')">+ Neuer Service</button>
|
|
||||||
</h2>
|
|
||||||
<div class="admin-grid" id="container-service">
|
|
||||||
<?php foreach ($services as $id => $s): ?>
|
|
||||||
<div class="admin-card">
|
|
||||||
<span class="badge">Datei: <?= htmlspecialchars($id) ?>.php</span>
|
|
||||||
<input type="text" name="services[<?= $id ?>][name]" value="<?= htmlspecialchars($s['name']) ?>"
|
|
||||||
placeholder="Name">
|
|
||||||
<label>Sidebar-Gruppe:</label>
|
|
||||||
<select name="services[<?= $id ?>][targetGroup]">
|
|
||||||
<?php foreach ($serviceGroups as $gid => $g): ?>
|
|
||||||
<option value="<?= $gid ?>"
|
|
||||||
<?= (isset($g['services']) && in_array($id, $g['services'])) ? 'selected' : '' ?>>
|
|
||||||
<?= htmlspecialchars($g['label']) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="services[<?= $id ?>][active]"
|
|
||||||
<?= ($s['active'] ?? false) ? 'checked' : '' ?>>
|
|
||||||
Aktiv
|
|
||||||
</label>
|
|
||||||
|
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="services[<?= $id ?>][adminOnly]"
|
|
||||||
<?= ($s['adminOnly'] ?? false) ? 'checked' : '' ?>>
|
|
||||||
Nur Admins
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dashboard Kategorien -->
|
|
||||||
<h2>🎨 Dashboard Kategorien
|
|
||||||
<button type="button" class="btn-add" onclick="addNew('linkGroup')">+ Neue Kategorie</button>
|
|
||||||
</h2>
|
|
||||||
<div class="admin-grid" id="container-linkGroup">
|
|
||||||
<?php foreach ($linkGroups as $id => $lg): ?>
|
|
||||||
<div class="admin-card">
|
|
||||||
<span class="badge">ID: <?= htmlspecialchars($id) ?></span>
|
|
||||||
<input type="text" name="linkGroups[<?= $id ?>][label]"
|
|
||||||
value="<?= htmlspecialchars($lg['label']) ?>" placeholder="Gruppen-Label">
|
|
||||||
<label>Farbe:</label>
|
|
||||||
<input type="color" name="linkGroups[<?= $id ?>][color]"
|
|
||||||
value="<?= $lg['color'] ?? '#3498db' ?>">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="linkGroups[<?= $id ?>][defaultOpen]"
|
|
||||||
<?= ($lg['defaultOpen'] ?? false) ? 'checked' : '' ?>>
|
|
||||||
Offen
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dashboard Kacheln -->
|
|
||||||
<h2>🔘 Dashboard Kacheln
|
|
||||||
<button type="button" class="btn-add" onclick="addNew('linkButton')">+ Neue Kachel</button>
|
|
||||||
</h2>
|
|
||||||
<div class="admin-grid" id="container-linkButton">
|
|
||||||
<?php foreach ($linkButtons as $key => $btn): ?>
|
|
||||||
<div class="admin-card">
|
|
||||||
<input type="text" name="linkButtons[<?= $key ?>][label]"
|
|
||||||
value="<?= htmlspecialchars($btn['label']) ?>" placeholder="Name">
|
|
||||||
<input type="text" name="linkButtons[<?= $key ?>][url]"
|
|
||||||
value="<?= htmlspecialchars($btn['url']) ?>" placeholder="URL">
|
|
||||||
<label>Dashboard Kategorie:</label>
|
|
||||||
<select name="linkButtons[<?= $key ?>][group]">
|
|
||||||
<?php foreach ($linkGroups as $id => $lg): ?>
|
|
||||||
<option value="<?= $id ?>" <?= ($btn['group'] == $id) ? 'selected' : '' ?>>
|
|
||||||
<?= htmlspecialchars($lg['label']) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
<div style="margin-top:5px;">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="linkButtons[<?= $key ?>][newTab]"
|
|
||||||
<?= ($btn['newTab'] ?? false) ? 'checked' : '' ?>>
|
|
||||||
Neuer Tab
|
|
||||||
</label>
|
|
||||||
|
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="linkButtons[<?= $key ?>][adminOnly]"
|
|
||||||
<?= ($btn['adminOnly'] ?? false) ? 'checked' : '' ?>>
|
|
||||||
Nur Admins
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="save-bar">
|
|
||||||
<button type="submit" name="save" class="btn-save">💾 Gesamte Konfiguration speichern</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function addNew(type) {
|
|
||||||
const container = document.getElementById('container-' + type);
|
|
||||||
const tempId = 'new_' + Date.now();
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'admin-card new-item';
|
|
||||||
|
|
||||||
let html = `<div><span class="badge">Neu hinzufügen</span></div>`;
|
|
||||||
|
|
||||||
if (type === 'serviceGroup') {
|
|
||||||
html +=
|
|
||||||
`<input type="text" name="serviceGroups[${tempId}][new_id]" placeholder="ID">
|
|
||||||
<input type="text" name="serviceGroups[${tempId}][label]" placeholder="Name">
|
|
||||||
<label><input type="checkbox" name="serviceGroups[${tempId}][defaultOpen]" checked> Ausgeklappt</label>`;
|
|
||||||
} else if (type === 'service') {
|
|
||||||
html += `<input type="text" name="services[${tempId}][new_id]" placeholder="Datei ohne .php">
|
|
||||||
<input type="text" name="services[${tempId}][name]" placeholder="Anzeigename">
|
|
||||||
<label>Gruppe:</label>
|
|
||||||
<select name="services[${tempId}][targetGroup]">
|
|
||||||
<?php foreach ($serviceGroups as $gid => $gl): ?>
|
|
||||||
<option value="<?= $gid ?>"><?= $gl['label'] ?></option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
<label><input type="checkbox" name="services[${tempId}][active]" checked> Aktiv</label>`;
|
|
||||||
} else if (type === 'linkGroup') {
|
|
||||||
html += `<input type="text" name="linkGroups[${tempId}][new_id]" placeholder="ID">
|
|
||||||
<input type="text" name="linkGroups[${tempId}][label]" placeholder="Label">
|
|
||||||
<input type="color" name="linkGroups[${tempId}][color]" value="#3498db">`;
|
|
||||||
} else if (type === 'linkButton') {
|
|
||||||
html += `<input type="text" name="linkButtons[${tempId}][label]" placeholder="Anzeigename">
|
|
||||||
<input type="text" name="linkButtons[${tempId}][url]" placeholder="https://...">
|
|
||||||
<label>Kategorie:</label>
|
|
||||||
<select name="linkButtons[${tempId}][group]">
|
|
||||||
<?php foreach ($linkGroups as $gid => $gl): ?>
|
|
||||||
<option value="<?= $gid ?>"><?= $gl['label'] ?></option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
<label><input type="checkbox" name="linkButtons[${tempId}][newTab]" checked> Tab</label>
|
|
||||||
| <label><input type="checkbox" name="linkButtons[${tempId}][adminOnly]"> Nur Admins</label>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
card.innerHTML = html;
|
|
||||||
container.appendChild(card);
|
|
||||||
card.scrollIntoView({
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/.htaccess</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/api/get.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/api/upload.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/favicon.ico</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/index.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>weekly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/modules/ConsoleLog.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/api_tester.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/backend_request_chart.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/backend_request_logs.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/berichte_arbeit.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/berichte_arbeit_gen.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/data_info.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/home_info.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/links.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/logout.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/personal_request_logs.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/public_request_chart.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/public_request_logs.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/services_control.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/tabellen.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/user_control.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/variablen_control.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/services/weather_chart.php</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/sitemap.xml</loc>
|
||||||
|
<lastmod>2026-03-03</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>http://backend.blackpi.ipv64.net/style.css</loc>
|
||||||
|
<lastmod>2026-03-02</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
use MVC\Model\User;
|
||||||
|
use MVC\Model\Variable;
|
||||||
|
|
||||||
|
class AdminController extends Controller {
|
||||||
|
public function user_control() {
|
||||||
|
global $isAdmin, $superuser;
|
||||||
|
$specificData = new \getSpecificData();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['add_user']) && $isAdmin) {
|
||||||
|
User::insert([
|
||||||
|
'NAME' => $_POST['name'],
|
||||||
|
'NACHNAME' => $_POST['nachname'],
|
||||||
|
'EMAIL' => $_POST['email'],
|
||||||
|
'PASSWORD' => password_hash($_POST['passwort'], PASSWORD_DEFAULT),
|
||||||
|
'ROLLE' => $_POST['rolle'],
|
||||||
|
'ADMIN' => $_POST['admin'] ?? 0,
|
||||||
|
'HOME_IP' => $_POST['home_ip']
|
||||||
|
]);
|
||||||
|
header("Location: " . $specificData->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['delete_user']) && $isAdmin) {
|
||||||
|
User::delete("NAME = ? AND NACHNAME = ? AND EMAIL = ?", $_POST['delete_name'], $_POST['delete_nachname'], $_POST['delete_email']);
|
||||||
|
header("Location: " . $specificData->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['password'])) {
|
||||||
|
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
||||||
|
$_SESSION['logged_in'] = true;
|
||||||
|
} else {
|
||||||
|
$error = "Falsches Passwort!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['logout'])) {
|
||||||
|
unset($_SESSION['logged_in']);
|
||||||
|
header("Location: " . $specificData->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $isAdmin ? User::all() : User::getByName($specificData->getUsername());
|
||||||
|
|
||||||
|
return $this->render('user_control', [
|
||||||
|
'users' => $users,
|
||||||
|
'isAdmin' => $isAdmin,
|
||||||
|
'error' => $error ?? null,
|
||||||
|
'loggedIn' => $_SESSION['logged_in'] ?? false
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function variablen_control() {
|
||||||
|
global $isAdmin;
|
||||||
|
if (!$isAdmin) return "Access Denied";
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['toggle_variable'])) {
|
||||||
|
Variable::toggle($_POST['variablenname']);
|
||||||
|
header("Location: " . (new \getSpecificData())->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('variablen_control', ['variablen' => Variable::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function services_control() {
|
||||||
|
global $isAdmin, $superuser, $serviceGroups, $services, $linkGroups, $linkButtons;
|
||||||
|
if (!$isAdmin) return "Access Denied";
|
||||||
|
|
||||||
|
$configFile = 'config.php';
|
||||||
|
$backupDir = 'config_backups';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['password'])) {
|
||||||
|
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
||||||
|
$_SESSION['logged_in'] = true;
|
||||||
|
} else {
|
||||||
|
$error = "Falsches Passwort!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['logout'])) {
|
||||||
|
unset($_SESSION['logged_in']);
|
||||||
|
header("Location: " . (new \getSpecificData())->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['save']) && ($_SESSION['logged_in'] ?? false)) {
|
||||||
|
if (!is_dir($backupDir)) mkdir($backupDir, 0755, true);
|
||||||
|
copy($configFile, $backupDir . '/config_' . date('Y-m-d_H-i-s') . '.php');
|
||||||
|
|
||||||
|
$newServices = [];
|
||||||
|
$serviceToGroupMapping = [];
|
||||||
|
if(isset($_POST['services'])){
|
||||||
|
foreach ($_POST['services'] as $id => $s) {
|
||||||
|
$finalId = !empty($s['new_id']) ? trim($s['new_id']) : $id;
|
||||||
|
if (empty($finalId)) continue;
|
||||||
|
$newServices[$finalId] = [
|
||||||
|
'name' => $s['name'],
|
||||||
|
'active' => isset($s['active']),
|
||||||
|
'adminOnly' => isset($s['adminOnly'])
|
||||||
|
];
|
||||||
|
if(!empty($s['targetGroup'])) {
|
||||||
|
$serviceToGroupMapping[$s['targetGroup']][] = $finalId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(new \RightsController())->RightsHandler($s['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newServiceGroups = [];
|
||||||
|
if(isset($_POST['serviceGroups'])){
|
||||||
|
foreach ($_POST['serviceGroups'] as $id => $groupData) {
|
||||||
|
$finalId = !empty($groupData['new_id']) ? trim($groupData['new_id']) : $id;
|
||||||
|
if (empty($finalId)) continue;
|
||||||
|
$currentServices = $serviceToGroupMapping[$finalId] ?? ($serviceGroups[$id]['services'] ?? []);
|
||||||
|
$newServiceGroups[$finalId] = [
|
||||||
|
'label' => $groupData['label'],
|
||||||
|
'defaultOpen' => isset($groupData['defaultOpen']),
|
||||||
|
'services' => array_values(array_unique($currentServices))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$newLinkGroups = [];
|
||||||
|
if(isset($_POST['linkGroups'])){
|
||||||
|
foreach ($_POST['linkGroups'] as $id => $lg) {
|
||||||
|
$finalId = !empty($lg['new_id']) ? trim($lg['new_id']) : $id;
|
||||||
|
if (empty($finalId)) continue;
|
||||||
|
$newLinkGroups[$finalId] = [
|
||||||
|
'label' => $lg['label'],
|
||||||
|
'color' => $lg['color'],
|
||||||
|
'defaultOpen' => isset($lg['defaultOpen'])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$newLinkButtons = [];
|
||||||
|
if(isset($_POST['linkButtons'])){
|
||||||
|
foreach ($_POST['linkButtons'] as $btn) {
|
||||||
|
if (empty($btn['label'])) continue;
|
||||||
|
$newLinkButtons[] = [
|
||||||
|
'label' => $btn['label'],
|
||||||
|
'url' => $btn['url'],
|
||||||
|
'newTab' => isset($btn['newTab']),
|
||||||
|
'adminOnly' => isset($btn['adminOnly']),
|
||||||
|
'group' => $btn['group']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = "<?php\n\n";
|
||||||
|
$content .= '$superuser = ' . var_export($superuser, true) . ";\n\n";
|
||||||
|
$content .= '$serviceGroups = ' . var_export($newServiceGroups, true) . ";\n\n";
|
||||||
|
$content .= '$services = ' . var_export($newServices, true) . ";\n\n";
|
||||||
|
$content .= '$linkGroups = ' . var_export($newLinkGroups, true) . ";\n\n";
|
||||||
|
$content .= '$linkButtons = ' . var_export($newLinkButtons, true) . ";\n";
|
||||||
|
|
||||||
|
if (file_put_contents($configFile, $content)) {
|
||||||
|
if (function_exists('opcache_invalidate')) { @opcache_invalidate($configFile, true); }
|
||||||
|
$_SESSION['status_message'] = "✅ Konfiguration erfolgreich aktualisiert!";
|
||||||
|
header("Location: " . (new \getSpecificData())->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = $_SESSION['status_message'] ?? null;
|
||||||
|
unset($_SESSION['status_message']);
|
||||||
|
|
||||||
|
return $this->render('services_control', [
|
||||||
|
'serviceGroups' => $serviceGroups,
|
||||||
|
'services' => $services,
|
||||||
|
'linkGroups' => $linkGroups,
|
||||||
|
'linkButtons' => $linkButtons,
|
||||||
|
'loggedIn' => $_SESSION['logged_in'] ?? false,
|
||||||
|
'error' => $error ?? null,
|
||||||
|
'message' => $message
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data_info() {
|
||||||
|
global $isAdmin;
|
||||||
|
if (!$isAdmin) return "Access Denied";
|
||||||
|
|
||||||
|
$data = new \getSpecificData();
|
||||||
|
$allData = $data->getAllData();
|
||||||
|
|
||||||
|
$groupedData = [];
|
||||||
|
foreach ($allData as $key => $value) {
|
||||||
|
if (strpos($key, ' - ') !== false) {
|
||||||
|
[$group, $field] = explode(' - ', $key, 2);
|
||||||
|
} else {
|
||||||
|
$group = 'Other';
|
||||||
|
$field = $key;
|
||||||
|
}
|
||||||
|
$groupedData[$group][$field] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('data_info', ['groupedData' => $groupedData]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tabellen() {
|
||||||
|
global $isAdmin;
|
||||||
|
if (!$isAdmin) return "Access Denied";
|
||||||
|
|
||||||
|
$tables = \DatabaseShowTables();
|
||||||
|
$selectedTable = $_GET['table'] ?? null;
|
||||||
|
$structure = [];
|
||||||
|
|
||||||
|
if ($selectedTable && in_array($selectedTable, $tables)) {
|
||||||
|
$structure = \DatabaseShowTableStructure($selectedTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('tabellen', [
|
||||||
|
'tables' => $tables,
|
||||||
|
'selectedTable' => $selectedTable,
|
||||||
|
'structure' => $structure
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
|
||||||
|
class ApiController extends Controller {
|
||||||
|
public function api_tester() {
|
||||||
|
$apiDir = realpath(__DIR__ . '/../../../api');
|
||||||
|
$apiOptions = [];
|
||||||
|
|
||||||
|
if ($apiDir !== false) {
|
||||||
|
$iterator = new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator($apiDir, \RecursiveDirectoryIterator::SKIP_DOTS)
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
if (!$file->isFile() || $file->getExtension() !== 'php') continue;
|
||||||
|
$relativePath = str_replace($apiDir . DIRECTORY_SEPARATOR, '', $file->getPathname());
|
||||||
|
$relativePath = str_replace('\\', '/', $relativePath);
|
||||||
|
$apiOptions[] = 'api/' . $relativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort($apiOptions);
|
||||||
|
|
||||||
|
return $this->render('api_tester', [
|
||||||
|
'apiOptions' => $apiOptions,
|
||||||
|
'defaultToken' => "15a437d128c918633e57dc7d18118a073b807644f21731ed5a7ebcf3eca9e242"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
use MVC\Model\Documentation;
|
||||||
|
|
||||||
|
class DocumentationController extends Controller {
|
||||||
|
public function index() {
|
||||||
|
$query = $_GET['q'] ?? '';
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
$limit = 5;
|
||||||
|
|
||||||
|
$results = Documentation::search($query, $page, $limit);
|
||||||
|
|
||||||
|
return $this->render('documentation', [
|
||||||
|
'docs' => $results['rows'],
|
||||||
|
'total' => $results['total'],
|
||||||
|
'pages' => $results['pages'],
|
||||||
|
'currentPage' => $results['current_page'],
|
||||||
|
'query' => $query
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
use MVC\Model\Domain;
|
||||||
|
|
||||||
|
class DomainController extends Controller {
|
||||||
|
public function domains() {
|
||||||
|
$statusMsg = "";
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST["add_domain"])) {
|
||||||
|
$subdomain = trim($_POST["subdomain"]);
|
||||||
|
if ($subdomain != "") {
|
||||||
|
Domain::insert(['SUBDOMAIN' => $subdomain]);
|
||||||
|
$statusMsg = "Domain wurde hinzugefügt.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($_POST["delete_domain"])) {
|
||||||
|
Domain::delete("ID = ?", intval($_POST["id"]));
|
||||||
|
$statusMsg = "Domain wurde gelöscht.";
|
||||||
|
}
|
||||||
|
if (isset($_POST["save_domain"])) {
|
||||||
|
Domain::update(['SUBDOMAIN' => trim($_POST["subdomain"])], "ID = ?", intval($_POST["id"]));
|
||||||
|
$statusMsg = "Domain wurde gespeichert.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('domains', [
|
||||||
|
'domains' => Domain::all(),
|
||||||
|
'statusMsg' => $statusMsg
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
use MVC\Model\Log;
|
||||||
|
|
||||||
|
class LogController extends Controller {
|
||||||
|
protected function getDevIpsSql() {
|
||||||
|
$devips = (new \getSpecificData())->getWhitelistedIPs();
|
||||||
|
return "'" . implode("','", $devips) . "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleLogs($websiteFilter, $serviceName) {
|
||||||
|
$devips_sql = $this->getDevIpsSql();
|
||||||
|
$filter = $_GET['filter'] ?? 'today';
|
||||||
|
|
||||||
|
$daten = Log::getFiltered($websiteFilter, $devips_sql, $filter);
|
||||||
|
|
||||||
|
return $this->render('logs', [
|
||||||
|
'daten' => $daten,
|
||||||
|
'filter' => $filter,
|
||||||
|
'serviceName' => $serviceName
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function public_request_logs() {
|
||||||
|
return $this->handleLogs("(WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)", 'public_request_logs');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function backend_request_logs() {
|
||||||
|
return $this->handleLogs("(WEBSITE = 'backend.blackpi.ipv64.net')", 'backend_request_logs');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function personal_request_logs() {
|
||||||
|
return $this->handleLogs("1=1", 'personal_request_logs');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function public_request_chart() {
|
||||||
|
$totalData = Log::getChartData();
|
||||||
|
$totalDataAssoc = [];
|
||||||
|
$allLabels = [];
|
||||||
|
foreach ($totalData as $row) {
|
||||||
|
$totalDataAssoc[$row['tag']] = (int)$row['anzahl'];
|
||||||
|
$allLabels[] = $row['tag'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$nodes = ['node1','node2','node3','node4'];
|
||||||
|
$finalNodeData = [];
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
$nodeData = Log::getNodeChartData($node);
|
||||||
|
$nodeAssoc = [];
|
||||||
|
foreach ($nodeData as $row) {
|
||||||
|
$nodeAssoc[$row['tag']] = (int)$row['anzahl'];
|
||||||
|
if (!in_array($row['tag'], $allLabels)) $allLabels[] = $row['tag'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-map to all labels
|
||||||
|
$finalNodeData[$node] = $nodeAssoc;
|
||||||
|
}
|
||||||
|
sort($allLabels);
|
||||||
|
|
||||||
|
$finalTotalData = [];
|
||||||
|
$mappedNodeData = [];
|
||||||
|
foreach ($allLabels as $label) {
|
||||||
|
$finalTotalData[] = $totalDataAssoc[$label] ?? 0;
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
$mappedNodeData[$node][] = $finalNodeData[$node][$label] ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('public_chart', [
|
||||||
|
'allLabels' => $allLabels,
|
||||||
|
'finalTotalData' => $finalTotalData,
|
||||||
|
'finalNodeData' => $mappedNodeData
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function backend_request_chart() {
|
||||||
|
$data = Log::getChartData("WEBSITE = 'backend.blackpi.ipv64.net'");
|
||||||
|
$labels = [];
|
||||||
|
$counts = [];
|
||||||
|
foreach ($data as $row) {
|
||||||
|
$labels[] = $row['tag'];
|
||||||
|
$counts[] = (int)$row['anzahl'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('backend_chart', [
|
||||||
|
'labels' => $labels,
|
||||||
|
'data' => $counts
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
use MVC\Model\CompanyReport;
|
||||||
|
use MVC\Model\SchoolReport;
|
||||||
|
use MVC\Model\Overtime;
|
||||||
|
|
||||||
|
class ReportController extends Controller {
|
||||||
|
public function berichte() {
|
||||||
|
$mode = $_GET['mode'] ?? 'fk';
|
||||||
|
|
||||||
|
if ($mode === 'normal') {
|
||||||
|
$data = CompanyReport::all();
|
||||||
|
} else {
|
||||||
|
$data = SchoolReport::allWithFK();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array_reverse($data);
|
||||||
|
$displayData = (is_array($data) && isset($data['rows'])) ? $data['rows'] : $data;
|
||||||
|
$count = is_array($displayData) ? count($displayData) : 0;
|
||||||
|
|
||||||
|
return $this->render('berichte', [
|
||||||
|
'mode' => $mode,
|
||||||
|
'displayData' => $displayData,
|
||||||
|
'count' => $count,
|
||||||
|
'selectedService' => $_GET['service'] ?? 'berichte'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function berichte_arbeit_gen() {
|
||||||
|
if (empty($_SESSION['user_logged_in'])) return "Access Denied";
|
||||||
|
|
||||||
|
$wochenArray = CompanyReport::getWochen();
|
||||||
|
|
||||||
|
$startDate = $_POST['start_date'] ?? date('Y-m-d', strtotime('-5 days'));
|
||||||
|
$endDate = $_POST['end_date'] ?? date('Y-m-d');
|
||||||
|
|
||||||
|
$berichteArray = CompanyReport::getByDateRange($startDate, $endDate);
|
||||||
|
|
||||||
|
$pdfFile = null;
|
||||||
|
if (isset($_POST['generate_pdf']) && count($berichteArray) > 0) {
|
||||||
|
$pdfFile = $this->generatePDF($berichteArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('berichte_arbeit_gen', [
|
||||||
|
'startDate' => $startDate,
|
||||||
|
'endDate' => $endDate,
|
||||||
|
'wochenArray' => $wochenArray,
|
||||||
|
'pdfFile' => $pdfFile,
|
||||||
|
'berichteArray' => $berichteArray
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generatePDF($berichteArray) {
|
||||||
|
$Einzelstunden = "2";
|
||||||
|
$Gesamtstunden = "8";
|
||||||
|
$data = [
|
||||||
|
'Text1' => '', // Nr
|
||||||
|
'Text2' => $berichteArray[0]['DATUM'] ?? '',
|
||||||
|
'Text3' => end($berichteArray)['DATUM'] ?? '',
|
||||||
|
'Text4' => 'Eckhardt',
|
||||||
|
'Text5' => 'Bruno Renato',
|
||||||
|
'Text6' => '1',
|
||||||
|
];
|
||||||
|
|
||||||
|
$fieldIndex = 7;
|
||||||
|
foreach ($berichteArray as $bericht) {
|
||||||
|
for ($i = 1; $i <= 4; $i++) {
|
||||||
|
$data["Text$fieldIndex"] = $bericht["PROZESS_$i"] ?? '';
|
||||||
|
$fieldIndex++;
|
||||||
|
$data["Text$fieldIndex"] = $Einzelstunden;
|
||||||
|
$fieldIndex++;
|
||||||
|
}
|
||||||
|
$data["Text$fieldIndex"] = $Gesamtstunden;
|
||||||
|
$fieldIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fdfFields = array_map(function($field, $value){
|
||||||
|
$value = str_replace(['(', ')'], ['\(', '\)'], $value);
|
||||||
|
return "<< /T ($field) /V ($value) >>";
|
||||||
|
}, array_keys($data), $data);
|
||||||
|
|
||||||
|
$fdf = "%FDF-1.2\n1 0 obj\n<<\n/FDF << /Fields [\n" . implode("\n", $fdfFields) . "\n] >> >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF";
|
||||||
|
|
||||||
|
$fdfFile = tempnam(sys_get_temp_dir(), 'fdf_') . '.fdf';
|
||||||
|
file_put_contents($fdfFile, $fdf);
|
||||||
|
|
||||||
|
$specificData = new \getSpecificData();
|
||||||
|
$inputPdf = $specificData->getDocumentRoot() . '/templates/ausbildungsnachweis.pdf';
|
||||||
|
$outputPdf = $specificData->getDocumentRoot() . '/static/ausbildungsnachweis_' . $berichteArray[0]['DATUM'] . '-' . end($berichteArray)['DATUM'] . '.pdf';
|
||||||
|
|
||||||
|
exec("pdftk $inputPdf fill_form $fdfFile output $outputPdf flatten");
|
||||||
|
unlink($fdfFile);
|
||||||
|
|
||||||
|
return $outputPdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function zeiterfassung() {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_overtime'])) {
|
||||||
|
Overtime::insert([
|
||||||
|
'DATUM' => $_POST['ot_date'],
|
||||||
|
'MINUTEN' => (int)$_POST['ot_minutes'],
|
||||||
|
'GRUND' => $_POST['ot_reason']
|
||||||
|
]);
|
||||||
|
\redirectToWebsite((new \getSpecificData())->getReferer());
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$minuten_berichte = CompanyReport::getTotalOvertime();
|
||||||
|
$minuten_abbau = Overtime::getSumByReason('Abbau');
|
||||||
|
$minuten_aufbau = Overtime::getSumByReason('Aufbau');
|
||||||
|
|
||||||
|
$gesamt_minuten = $minuten_berichte - $minuten_abbau + $minuten_aufbau;
|
||||||
|
|
||||||
|
return $this->render('zeiterfassung', [
|
||||||
|
'gesamt_minuten' => $gesamt_minuten,
|
||||||
|
'username' => $_SESSION['username']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function print_static() {
|
||||||
|
$dir = (new \getSpecificData())->getDocumentRoot() . "/static";
|
||||||
|
$files = is_dir($dir) ? array_diff(scandir($dir), ['.', '..']) : [];
|
||||||
|
|
||||||
|
return $this->render('print_static', ['files' => $files]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
|
||||||
|
class ServiceController extends Controller {
|
||||||
|
public function home_info() {
|
||||||
|
$username = $_SESSION['username'] ?? 'Gast';
|
||||||
|
$is_superuser = $_SESSION['is_superuser'] ?? false;
|
||||||
|
$is_admin = $_SESSION['is_admin'] ?? false;
|
||||||
|
|
||||||
|
$db_res = \DatabaseShow(
|
||||||
|
"Benutzer",
|
||||||
|
"NACHNAME, EMAIL, ROLLE, HOME_IP",
|
||||||
|
"NAME = ?",
|
||||||
|
$username
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->render('home_info', [
|
||||||
|
'username' => $username,
|
||||||
|
'is_superuser' => $is_superuser,
|
||||||
|
'is_admin' => $is_admin,
|
||||||
|
'db_res' => $db_res
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function links() {
|
||||||
|
global $linkButtons, $linkGroups, $isAdmin;
|
||||||
|
|
||||||
|
return $this->render('links', [
|
||||||
|
'linkButtons' => $linkButtons,
|
||||||
|
'linkGroups' => $linkGroups,
|
||||||
|
'isAdmin' => $isAdmin
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout() {
|
||||||
|
session_destroy();
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder for other services
|
||||||
|
public function __call($name, $arguments) {
|
||||||
|
return "Service $name logic not implemented yet in Controller.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
use MVC\Model\Domain;
|
||||||
|
|
||||||
|
class StatsController extends Controller {
|
||||||
|
public function weather_chart() {
|
||||||
|
// Since Weather is not yet a dedicated model, we use the Domain query helper or raw query
|
||||||
|
$res = Domain::query("SELECT ZEIT, AKTUELL FROM Wetter");
|
||||||
|
$labels = [];
|
||||||
|
$data = [];
|
||||||
|
while($row = $res->fetch_assoc()) {
|
||||||
|
$labels[] = $row['ZEIT'];
|
||||||
|
$data[] = (float)$row['AKTUELL'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('weather_chart', [
|
||||||
|
'labels' => $labels,
|
||||||
|
'data' => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
protected $view;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->view = new View(__DIR__ . '/../View');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render($template, $data = []) {
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->view->assign($key, $value);
|
||||||
|
}
|
||||||
|
return $this->view->render($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function json($data, $status = 200) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
http_response_code($status);
|
||||||
|
echo json_encode($data);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
abstract class Model {
|
||||||
|
protected static $table;
|
||||||
|
protected static $conn;
|
||||||
|
|
||||||
|
protected static function getConnection() {
|
||||||
|
if (!self::$conn) {
|
||||||
|
self::$conn = \db_connect();
|
||||||
|
}
|
||||||
|
return self::$conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function all() {
|
||||||
|
return \DatabaseShow(static::$table, "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function find($id, $column = 'ID') {
|
||||||
|
$result = \DatabaseShow(static::$table, "*", "$column = ?", $id);
|
||||||
|
return $result[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function where($where, ...$params) {
|
||||||
|
return \DatabaseShow(static::$table, "*", $where, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function insert($data) {
|
||||||
|
$columns = implode(", ", array_keys($data));
|
||||||
|
$values = array_values($data);
|
||||||
|
return \DatabaseInsert(static::$table, $columns, ...$values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function update($data, $where, ...$whereParams) {
|
||||||
|
$sets = [];
|
||||||
|
$values = [];
|
||||||
|
foreach ($data as $column => $value) {
|
||||||
|
$sets[] = "$column = ?";
|
||||||
|
$values[] = $value;
|
||||||
|
}
|
||||||
|
$setString = implode(", ", $sets);
|
||||||
|
return \DatabaseUpdate(static::$table, $setString, $where, ...array_merge($values, $whereParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function delete($where, ...$params) {
|
||||||
|
return \DatabaseDelete(static::$table, $where, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function query($sql, ...$params) {
|
||||||
|
$conn = self::getConnection();
|
||||||
|
if (empty($params)) {
|
||||||
|
return $conn->query($sql);
|
||||||
|
}
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$types = str_repeat('s', count($params));
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->get_result();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
protected $routes = [];
|
||||||
|
protected $middlewares = [];
|
||||||
|
|
||||||
|
public function add($service, $controller, $method, $middlewares = []) {
|
||||||
|
$this->routes[$service] = [
|
||||||
|
'controller' => $controller,
|
||||||
|
'method' => $method,
|
||||||
|
'middlewares' => $middlewares
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatch($service) {
|
||||||
|
if (!isset($this->routes[$service])) {
|
||||||
|
throw new \Exception("Service '$service' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$route = $this->routes[$service];
|
||||||
|
|
||||||
|
// Run Middlewares
|
||||||
|
foreach ($route['middlewares'] as $middleware) {
|
||||||
|
$this->runMiddleware($middleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
$controllerClass = $route['controller'];
|
||||||
|
$method = $route['method'];
|
||||||
|
|
||||||
|
if (!class_exists($controllerClass)) {
|
||||||
|
throw new \Exception("Controller class '$controllerClass' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$controller = new $controllerClass();
|
||||||
|
|
||||||
|
if (!method_exists($controller, $method)) {
|
||||||
|
throw new \Exception("Method '$method' not found in controller '$controllerClass'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $controller->$method();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function runMiddleware($middleware) {
|
||||||
|
if ($middleware === 'admin') {
|
||||||
|
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||||
|
die("Access Denied: Admin rights required.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($middleware === 'auth') {
|
||||||
|
if (empty($_SESSION['user_logged_in'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
class View {
|
||||||
|
protected $data = [];
|
||||||
|
protected $viewPath;
|
||||||
|
|
||||||
|
public function __construct($viewPath) {
|
||||||
|
$this->viewPath = $viewPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assign($key, $value) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($template) {
|
||||||
|
extract($this->data);
|
||||||
|
$file = $this->viewPath . '/' . $template . '.php';
|
||||||
|
if (file_exists($file)) {
|
||||||
|
ob_start();
|
||||||
|
include $file;
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
return "View $template not found at $file";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Layout;
|
||||||
|
|
||||||
|
class Layout {
|
||||||
|
protected $blocks = [];
|
||||||
|
protected $template;
|
||||||
|
|
||||||
|
public function __construct($template = 'main') {
|
||||||
|
$this->template = $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBlock($name, $content) {
|
||||||
|
$this->blocks[$name] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlock($name) {
|
||||||
|
return $this->blocks[$name] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
$layoutFile = __DIR__ . '/Templates/' . $this->template . '.php';
|
||||||
|
if (file_exists($layoutFile)) {
|
||||||
|
ob_start();
|
||||||
|
include $layoutFile;
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
return "Layout template {$this->template} not found.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?= $this->getBlock('title') ?></title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<?= $this->getBlock('head') ?>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="main-header">
|
||||||
|
<?= $this->getBlock('header') ?>
|
||||||
|
</header>
|
||||||
|
<div class="main">
|
||||||
|
<div class="sidebar">
|
||||||
|
<?= $this->getBlock('sidebar') ?>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<?= $this->getBlock('content') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<?= $this->getBlock('scripts') ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
# 🏗️ Backend MVC - Umfassende Dokumentation
|
||||||
|
|
||||||
|
Diese Dokumentation beschreibt die Architektur, die Funktionsweise und die Besonderheiten des neuen Backend-Systems.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏛️ 1. Architektur-Übersicht
|
||||||
|
|
||||||
|
Das System folgt dem **MVC (Model-View-Controller)** Pattern. Das Ziel ist eine strikte Trennung von Datenverarbeitung, Geschäftslogik und Benutzeroberfläche.
|
||||||
|
|
||||||
|
### 📂 Verzeichnisstruktur & Verantwortlichkeiten
|
||||||
|
- **`WEBROOT/index.php`**: Der zentrale Einstiegspunkt. Hier wird das Routing initialisiert, die Layout-Blöcke definiert und das Error-Handling gesteuert.
|
||||||
|
- **`src/MVC/Core/`**: Das Herzstück. Enthält die Basisklassen `Controller`, `Model`, `View`, `Router` und `Layout`. Diese Klassen sollten selten angefasst werden.
|
||||||
|
- **`src/MVC/Model/`**: Daten-Logik. Jede Klasse repräsentiert eine Datenbanktabelle oder einen logischen Datenbereich.
|
||||||
|
- **`src/MVC/Controller/`**: Geschäftslogik. Controller nehmen Anfragen entgegen, nutzen Models zur Datenbeschaffung und geben Daten an Views weiter.
|
||||||
|
- **`src/MVC/View/`**: Präsentationsschicht. Reine HTML/PHP-Dateien für die Anzeige.
|
||||||
|
- **`src/MVC/Layout/`**: Definition des Rahmendesigns und Verwaltung der Inhaltsblöcke.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚦 2. Routing & Middleware (Der Verkehrspolizist)
|
||||||
|
|
||||||
|
Das Routing wird in der `index.php` über den `Router` konfiguriert.
|
||||||
|
|
||||||
|
### Routen registrieren
|
||||||
|
```php
|
||||||
|
$router->add($service_id, $controller_class, $method_name, $middlewares);
|
||||||
|
```
|
||||||
|
- **`$service_id`**: Der Wert des URL-Parameters `?service=...`.
|
||||||
|
- **`$middlewares`**: Ein Array von Sicherheitsstufen (z.B. `['auth', 'admin']`).
|
||||||
|
|
||||||
|
### Verfügbare Middlewares
|
||||||
|
- `auth`: Stellt sicher, dass die Session `user_logged_in` aktiv ist. Leitet sonst zum Login um.
|
||||||
|
- `admin`: Stellt sicher, dass die Session `is_admin` wahr ist. Gibt sonst "Access Denied" aus.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏛️ 3. Der Model-Layer (Datenabstraktion)
|
||||||
|
|
||||||
|
Alle Models erben von `MVC\Core\Model`. Dies gibt ihnen Zugriff auf mächtige Standardmethoden:
|
||||||
|
|
||||||
|
### CRUD-Operationen
|
||||||
|
- `Model::all()`: Gibt alle Datensätze der Tabelle zurück.
|
||||||
|
- `Model::find($id)`: Sucht einen Datensatz primär über die ID.
|
||||||
|
- `Model::where($sql, ...$params)`: Filtert Datensätze mit Prepared Statements.
|
||||||
|
- `Model::insert($data)`: Erwartet ein assoziatives Array (z.B. `['NAME' => 'Max']`).
|
||||||
|
- `Model::update($data, $where, ...$whereParams)`: Aktualisiert Felder basierend auf einer Bedingung.
|
||||||
|
- `Model::delete($where, ...$params)`: Löscht Datensätze.
|
||||||
|
- `Model::query($sql, ...$params)`: Ermöglicht komplexe, manuelle SQL-Abfragen.
|
||||||
|
|
||||||
|
### 💡 Besonderheit: Statische Bindung
|
||||||
|
In den Models wird die Tabelle über `protected static $table` definiert. Die Basisklasse nutzt `static::`, um immer auf die Tabelle des Kind-Models zuzugreifen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 4. Controller & AJAX (Die Logik)
|
||||||
|
|
||||||
|
Controller fungieren als Vermittler. Sie verarbeiten sowohl klassische Seitenaufrufe als auch AJAX-Requests.
|
||||||
|
|
||||||
|
### Seiten rendern
|
||||||
|
```php
|
||||||
|
return $this->render('view_name', ['key' => 'value']);
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON-Antworten (für AJAX)
|
||||||
|
Für moderne Oberflächen, die Daten nachladen oder umschalten ohne Refresh:
|
||||||
|
```php
|
||||||
|
return $this->json(['success' => true, 'data' => $result]);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧱 5. Das modulare Block-Layout
|
||||||
|
|
||||||
|
Das Layout ist in "Blöcke" unterteilt, die in der `index.php` befüllt werden.
|
||||||
|
|
||||||
|
### Blöcke setzen
|
||||||
|
```php
|
||||||
|
$layout->setBlock('header', $html_content);
|
||||||
|
$layout->setBlock('sidebar', $sidebar_html);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Blöcke in der View nutzen (Layout-Template)
|
||||||
|
In `Layout/Templates/main.php` werden die Blöcke mit `$this->getBlock('name')` ausgegeben.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ 6. Error-Handling & Sicherheit
|
||||||
|
|
||||||
|
### Zentrales Catch-System
|
||||||
|
Die gesamte Applikation läuft in einem `try-catch` Block.
|
||||||
|
- Tritt ein Fehler (Exception) auf, wird dieser im System-Log gespeichert.
|
||||||
|
- **Admins** sehen den "Stacktrace" (Pfad des Fehlers) zur schnellen Behebung.
|
||||||
|
- **Benutzer** sehen eine generische, freundliche Fehlermeldung.
|
||||||
|
|
||||||
|
### SQL-Injection Schutz
|
||||||
|
Durch die konsequente Nutzung von `Model::where`, `Model::insert` etc., werden automatisch **Prepared Statements** genutzt. SQL-Injections sind dadurch nahezu ausgeschlossen.
|
||||||
|
|
||||||
|
### XSS-Schutz
|
||||||
|
Variablen in der View sollten **immer** mit `htmlspecialchars()` ausgegeben werden:
|
||||||
|
```html
|
||||||
|
<p><?= htmlspecialchars($user_name) ?></p>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 7. Besonderheiten & Tipps
|
||||||
|
|
||||||
|
- **Namespace-Beachtung**: Da wir Namespaces nutzen (`MVC\Controller`), müssen globale PHP-Funktionen oder Klassen aus dem `src/utils` Ordner mit einem Backslash aufgerufen werden (z.B. `\db_connect()` oder `\getSpecificData()`).
|
||||||
|
- **Globaler State**: Variablen aus der `config.php` (wie `$Berechtigungen`) müssen in Controllern oft via `global $variable;` bekannt gemacht werden, bevor sie an Models übergeben werden.
|
||||||
|
- **Post-Redirect-Get**: Nach dem Speichern von Daten (`POST`) im Controller immer ein `header("Location: ...")` ausführen, um doppelte Einsendungen beim Neuladen zu verhindern.
|
||||||
|
- **Statische Files**: Bilder, CSS und JS gehören in den Ordner `WEBROOT/static/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Modul-Generator
|
||||||
|
Nutze das Script im Hauptverzeichnis für schnelle Ergebnisse:
|
||||||
|
```bash
|
||||||
|
php create_module.php mein_name
|
||||||
|
```
|
||||||
|
Dieses erstellt automatisch Controller und View nach den oben genannten Standards.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class CompanyReport extends Model {
|
||||||
|
protected static $table = "Firmenberichte";
|
||||||
|
|
||||||
|
public static function getByDateRange($start, $end) {
|
||||||
|
$sql = "SELECT `DATUM`, `PROZESS_1`, `PROZESS_2`, `PROZESS_3`, `PROZESS_4` FROM Firmenberichte WHERE `DATUM` BETWEEN ? AND ?";
|
||||||
|
return self::query($sql, $start, $end)->fetch_all(MYSQLI_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getWochen() {
|
||||||
|
self::query("SET @row := 0");
|
||||||
|
$sql = "SELECT MIN(DATUM) AS start_date, MAX(DATUM) AS end_date FROM (SELECT DATUM, DATE_SUB(DATUM, INTERVAL (@row := @row + 1) DAY) AS grp FROM Firmenberichte ORDER BY DATUM) AS t GROUP BY grp ORDER BY start_date";
|
||||||
|
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getTotalOvertime() {
|
||||||
|
$res = self::query("SELECT SUM(OVERTIME) AS summe FROM Firmenberichte");
|
||||||
|
return intval($res->fetch_object()->summe ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class Documentation extends Model {
|
||||||
|
protected static $table = "Dokumentation";
|
||||||
|
|
||||||
|
public static function search($query, $page = 1, $limit = 5) {
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
$search = "%$query%";
|
||||||
|
|
||||||
|
$countRes = \DatabaseShow(self::$table, "COUNT(*) as total", "TITEL LIKE ? OR INHALT LIKE ? OR KATEGORIE LIKE ?", $search, $search, $search);
|
||||||
|
$total = $countRes[0]['total'] ?? 0;
|
||||||
|
|
||||||
|
$sql = "SELECT * FROM Dokumentation WHERE TITEL LIKE ? OR INHALT LIKE ? OR KATEGORIE LIKE ? ORDER BY ERSTELLT_AM DESC LIMIT ? OFFSET ?";
|
||||||
|
$result = self::query($sql, $search, $search, $search, $limit, $offset);
|
||||||
|
|
||||||
|
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'rows' => $rows,
|
||||||
|
'total' => $total,
|
||||||
|
'pages' => ceil($total / $limit),
|
||||||
|
'current_page' => $page
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class Domain extends Model {
|
||||||
|
protected static $table = "Domains";
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class Log extends Model {
|
||||||
|
protected static $table = "logs";
|
||||||
|
|
||||||
|
public static function getFiltered($websiteFilter, $devIpsSql, $filter) {
|
||||||
|
switch ($filter) {
|
||||||
|
case 'today':
|
||||||
|
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE DATE(FIRST_REQUEST) = CURDATE() AND $websiteFilter AND CLIENT_IP NOT IN ($devIpsSql) ORDER BY FIRST_REQUEST DESC";
|
||||||
|
break;
|
||||||
|
case 'counterperip':
|
||||||
|
$sql = "SELECT CLIENT_IP, LAND, USER_AGENT, SUM(counter) AS gesamt_counter FROM logs WHERE $websiteFilter AND CLIENT_IP NOT IN ($devIpsSql) GROUP BY CLIENT_IP ORDER BY gesamt_counter DESC";
|
||||||
|
break;
|
||||||
|
case 'node1':
|
||||||
|
case 'node2':
|
||||||
|
case 'node3':
|
||||||
|
case 'node4':
|
||||||
|
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE `SERVER_IP` = ? AND $websiteFilter ORDER BY `FIRST_REQUEST` DESC";
|
||||||
|
$res = self::query($sql, $filter);
|
||||||
|
return $res->fetch_all(MYSQLI_ASSOC);
|
||||||
|
default:
|
||||||
|
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE $websiteFilter AND CLIENT_IP NOT IN ($devIpsSql) ORDER BY FIRST_REQUEST DESC";
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = self::query($sql);
|
||||||
|
return $res->fetch_all(MYSQLI_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getChartData($websiteFilter = null) {
|
||||||
|
$where = $websiteFilter ? "WHERE $websiteFilter" : "";
|
||||||
|
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs $where GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
||||||
|
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getNodeChartData($node) {
|
||||||
|
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE SERVER_IP = ? GROUP BY DATE(FIRST_REQUEST)";
|
||||||
|
return self::query($sql, $node)->fetch_all(MYSQLI_ASSOC);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class Overtime extends Model {
|
||||||
|
protected static $table = "Ueberstunden";
|
||||||
|
|
||||||
|
public static function getSumByReason($reason) {
|
||||||
|
$res = self::query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden WHERE GRUND = ?", $reason);
|
||||||
|
return intval($res->fetch_object()->summe ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class SchoolReport extends Model {
|
||||||
|
protected static $table = "Berufsschulberichte";
|
||||||
|
|
||||||
|
public static function allWithFK() {
|
||||||
|
global $Berufsschulberichte;
|
||||||
|
return \DatabaseShowWithFK(self::$table, $Berufsschulberichte);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class User extends Model {
|
||||||
|
protected static $table = "Benutzer";
|
||||||
|
|
||||||
|
public static function getByName($name) {
|
||||||
|
return self::where("NAME = ?", $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isAdmin($userId) {
|
||||||
|
$user = self::find($userId);
|
||||||
|
return ($user['ADMIN'] ?? 0) == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
class Variable extends Model {
|
||||||
|
protected static $table = "Variablen";
|
||||||
|
|
||||||
|
public static function getByName($name) {
|
||||||
|
return self::where("VARIABLENNAME = ?", $name)[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function toggle($name) {
|
||||||
|
$var = self::getByName($name);
|
||||||
|
if ($var) {
|
||||||
|
$newValue = ($var['WERT'] === "ON") ? "OFF" : "ON";
|
||||||
|
return self::update(['WERT' => $newValue], "VARIABLENNAME = ?", $name);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
<style>
|
||||||
|
.api-tester-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
padding: 25px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-tester-container h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: var(--btn-bg);
|
||||||
|
border-bottom: 1px solid var(--card-border);
|
||||||
|
padding-bottom: 12px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-form-group {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-input,
|
||||||
|
.tester-textarea,
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--input-bg);
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
color: var(--input-text);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-textarea {
|
||||||
|
min-height: 120px;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-btn-send {
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--btn-bg);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-btn-send:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#tester-response-container {
|
||||||
|
margin-top: 25px;
|
||||||
|
background-color: var(--input-bg);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
min-height: 100px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tester-output-content {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-status-200 {
|
||||||
|
background-color: #198754;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-status-error {
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-status-warning {
|
||||||
|
background-color: #ffc107;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-small-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-small-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-response-info {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.tester-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.tester-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="api-tester-container">
|
||||||
|
<h2>🛠️ API Tester</h2>
|
||||||
|
|
||||||
|
<div class="tester-form-group">
|
||||||
|
<label for="endpoint">API Endpunkt</label>
|
||||||
|
<select id="endpoint" class="tester-input" onchange="checkEndpoint()">
|
||||||
|
<?php foreach ($apiOptions as $opt): ?>
|
||||||
|
<option value="<?= htmlspecialchars($opt) ?>"><?= htmlspecialchars($opt) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tester-grid">
|
||||||
|
<div class="tester-form-group">
|
||||||
|
<label for="method">HTTP Methode</label>
|
||||||
|
<select id="method" class="tester-input">
|
||||||
|
<option value="GET">GET</option>
|
||||||
|
<option value="POST">POST</option>
|
||||||
|
<option value="PUT">PUT</option>
|
||||||
|
<option value="DELETE">DELETE</option>
|
||||||
|
<option value="PATCH">PATCH</option>
|
||||||
|
<option value="OPTIONS">OPTIONS</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="tester-form-group" id="function-group">
|
||||||
|
<label for="function-header">Function Header</label>
|
||||||
|
<input type="text" id="function-header" class="tester-input" placeholder="z.B. IPs, Ueberstunden">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tester-form-group">
|
||||||
|
<label for="token">Authorization Token</label>
|
||||||
|
<div class="tester-row">
|
||||||
|
<input type="text" id="token" class="tester-input" value="<?= htmlspecialchars($defaultToken) ?>">
|
||||||
|
<label class="tester-checkbox">
|
||||||
|
<input type="checkbox" id="send-token" checked>
|
||||||
|
Mitsenden
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tester-form-group">
|
||||||
|
<label for="custom-headers">Zusätzliche Header (JSON)</label>
|
||||||
|
<textarea id="custom-headers" class="tester-textarea" placeholder='{"X-Test": "123", "Client": "Frontend"}'></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tester-form-group" id="json-body-group">
|
||||||
|
<label for="json-body">JSON Body</label>
|
||||||
|
<textarea id="json-body" class="tester-textarea" placeholder='{"test": true}'></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tester-form-group hidden" id="file-group">
|
||||||
|
<label for="files">Dateien Upload</label>
|
||||||
|
<input type="file" id="files" class="tester-input" multiple>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tester-grid">
|
||||||
|
<button class="tester-btn-send" onclick="sendApiRequest()">Anfrage senden 🚀</button>
|
||||||
|
<button class="tester-btn-send" onclick="clearResponse()">Ausgabe leeren 🧹</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tester-response-container">
|
||||||
|
<div style="text-align:center; opacity:0.7; padding:15px;">Bereit für Anfragen.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function checkEndpoint() {
|
||||||
|
const endpoint = document.getElementById('endpoint').value;
|
||||||
|
const fileGroup = document.getElementById('file-group');
|
||||||
|
const functionGroup = document.getElementById('function-group');
|
||||||
|
const jsonBodyGroup = document.getElementById('json-body-group');
|
||||||
|
const methodSelect = document.getElementById('method');
|
||||||
|
|
||||||
|
fileGroup.classList.add('hidden');
|
||||||
|
functionGroup.classList.remove('hidden');
|
||||||
|
jsonBodyGroup.classList.remove('hidden');
|
||||||
|
|
||||||
|
if (endpoint.includes('upload.php')) {
|
||||||
|
fileGroup.classList.remove('hidden');
|
||||||
|
jsonBodyGroup.classList.add('hidden');
|
||||||
|
methodSelect.value = 'POST';
|
||||||
|
}
|
||||||
|
if (endpoint.includes('get.php')) {
|
||||||
|
methodSelect.value = 'GET';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearResponse() {
|
||||||
|
document.getElementById('tester-response-container').innerHTML = `
|
||||||
|
<div style="text-align:center; opacity:0.7; padding:15px;">Ausgabe geleert.</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendApiRequest() {
|
||||||
|
const endpoint = document.getElementById('endpoint').value;
|
||||||
|
const method = document.getElementById('method').value;
|
||||||
|
const token = document.getElementById('token').value;
|
||||||
|
const functionHeader = document.getElementById('function-header').value;
|
||||||
|
const customHeadersInput = document.getElementById('custom-headers').value;
|
||||||
|
const jsonBodyInput = document.getElementById('json-body').value;
|
||||||
|
const fileInput = document.getElementById('files');
|
||||||
|
const sendToken = document.getElementById('send-token').checked;
|
||||||
|
|
||||||
|
const responseContainer = document.getElementById('tester-response-container');
|
||||||
|
responseContainer.innerHTML = `<div style="text-align:center; padding:15px;">Anfrage läuft...</div>`;
|
||||||
|
|
||||||
|
let headers = {};
|
||||||
|
if (sendToken && token.trim() !== '') headers['Authorization'] = token;
|
||||||
|
if (functionHeader.trim() !== '') headers['Function'] = functionHeader;
|
||||||
|
|
||||||
|
if (customHeadersInput.trim() !== '') {
|
||||||
|
try {
|
||||||
|
const customHeaders = JSON.parse(customHeadersInput);
|
||||||
|
headers = { ...headers, ...customHeaders };
|
||||||
|
} catch (e) {
|
||||||
|
responseContainer.innerHTML = `<div style="color:#dc3545; padding:15px;">Fehlerhafte Header JSON Struktur.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = { method: method, headers: headers };
|
||||||
|
|
||||||
|
if (endpoint.includes('upload.php') && fileInput.files.length > 0) {
|
||||||
|
const formData = new FormData();
|
||||||
|
for (let i = 0; i < fileInput.files.length; i++) formData.append('files[]', fileInput.files[i]);
|
||||||
|
options.body = formData;
|
||||||
|
} else {
|
||||||
|
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) && jsonBodyInput.trim() !== '') {
|
||||||
|
try {
|
||||||
|
JSON.parse(jsonBodyInput);
|
||||||
|
headers['Content-Type'] = 'application/json';
|
||||||
|
options.body = jsonBodyInput;
|
||||||
|
} catch (e) {
|
||||||
|
responseContainer.innerHTML = `<div style="color:#dc3545; padding:15px;">Fehlerhafte JSON Body Struktur.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startedAt = performance.now();
|
||||||
|
try {
|
||||||
|
const response = await fetch(endpoint, options);
|
||||||
|
const duration = (performance.now() - startedAt).toFixed(0);
|
||||||
|
const status = response.status;
|
||||||
|
const rawText = await response.text();
|
||||||
|
let resultText = rawText;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(rawText);
|
||||||
|
resultText = JSON.stringify(json, null, 2);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
let badgeClass = status >= 200 && status < 300 ? 'tester-status-200' : (status >= 300 && status < 400 ? 'tester-status-warning' : 'tester-status-error');
|
||||||
|
|
||||||
|
responseContainer.innerHTML = `
|
||||||
|
<div class="tester-badge ${badgeClass}">Status: ${status}</div>
|
||||||
|
<div class="tester-response-info">⏱️ ${duration} ms</div>
|
||||||
|
<button class="tester-small-btn" onclick="copyResponse()">Antwort kopieren</button>
|
||||||
|
<pre id="tester-output-content"></pre>
|
||||||
|
`;
|
||||||
|
document.getElementById('tester-output-content').textContent = resultText;
|
||||||
|
} catch (error) {
|
||||||
|
responseContainer.innerHTML = `<div style="color:#dc3545; padding:15px;">Fehler: ${error.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyResponse() {
|
||||||
|
const content = document.getElementById('tester-output-content');
|
||||||
|
if (!content) return;
|
||||||
|
navigator.clipboard.writeText(content.textContent);
|
||||||
|
alert('Antwort kopiert.');
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEndpoint();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
||||||
|
|
||||||
|
<canvas id="logChart" width="600" height="400"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const ctx = document.getElementById('logChart').getContext('2d');
|
||||||
|
const logChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: <?php echo json_encode($labels); ?>,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Logs pro Tag',
|
||||||
|
data: <?php echo json_encode($data); ?>,
|
||||||
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Logs pro Tag'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'top'
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
pan: {
|
||||||
|
enabled: true,
|
||||||
|
mode: 'x',
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
wheel: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
mode: 'x',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Datum'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Anzahl Logs'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
stepSize: 20
|
||||||
|
},
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<style>
|
||||||
|
.debug-table-container {
|
||||||
|
background: #252525;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
color: #ccc;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #333;
|
||||||
|
text-align: left;
|
||||||
|
padding: 15px 12px;
|
||||||
|
border-bottom: 2px solid #3498db;
|
||||||
|
color: #3498db;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:hover {
|
||||||
|
background: rgba(52, 152, 219, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child,
|
||||||
|
th:first-child {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
background: #252525;
|
||||||
|
z-index: 5;
|
||||||
|
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
th:first-child {
|
||||||
|
z-index: 11;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-note {
|
||||||
|
padding: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: #777;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h2 style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<span>📊 Berichtte-Vorschau:</span>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a href="?service=<?= htmlspecialchars($selectedService) ?>&mode=fk">
|
||||||
|
<button <?= $mode === 'fk' ? 'style="background:#3498db;color:white;"' : '' ?>>
|
||||||
|
Berufsschulberichte
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="?service=<?= htmlspecialchars($selectedService) ?>&mode=normal">
|
||||||
|
<button <?= $mode === 'normal' ? 'style="background:#3498db;color:white;"' : '' ?>>
|
||||||
|
Arbeitsberichte
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<?php if ($count > 0 && is_array($displayData[0])): ?>
|
||||||
|
<div class="debug-table-container">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<?php foreach (array_keys($displayData[0]) as $column): ?>
|
||||||
|
<th><?= htmlspecialchars($column) ?></th>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($displayData as $row): ?>
|
||||||
|
<tr>
|
||||||
|
<?php foreach ($row as $key => $cell): ?>
|
||||||
|
<td><?= htmlspecialchars($cell ?? '-') ?></td>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="debug-table-container">
|
||||||
|
<div class="empty-note">
|
||||||
|
Keine tabellarischen Daten gefunden.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<div class="content-box">
|
||||||
|
<h2>Ausbildungsberichte PDF erstellen</h2>
|
||||||
|
|
||||||
|
<form method="post" class="overtime-container">
|
||||||
|
<label for="start_date">Von:</label>
|
||||||
|
<input type="date" name="start_date" id="start_date" value="<?= htmlspecialchars($startDate) ?>">
|
||||||
|
|
||||||
|
<label for="end_date">Bis:</label>
|
||||||
|
<input type="date" name="end_date" id="end_date" value="<?= htmlspecialchars($endDate) ?>">
|
||||||
|
|
||||||
|
<button type="submit" name="generate_pdf">PDF erstellen</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php if ($pdfFile): ?>
|
||||||
|
<div class="overtime-result">
|
||||||
|
<?= htmlspecialchars(basename($pdfFile)) ?> erfolgreich erstellt!
|
||||||
|
</div>
|
||||||
|
<?php elseif(isset($_POST['generate_pdf'])): ?>
|
||||||
|
<div class="overtime-result negative">
|
||||||
|
Keine Berichte im gewählten Zeitraum gefunden.
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if(!empty($wochenArray)): ?>
|
||||||
|
<div class="wochen-liste">
|
||||||
|
<h3>Verfügbare Wochenabschnitte:</h3>
|
||||||
|
<ul>
|
||||||
|
<?php
|
||||||
|
foreach($wochenArray as $woche):
|
||||||
|
$pdfName = 'ausbildungsnachweis_' . $woche['start_date'] . '-' . $woche['end_date'] . '.pdf';
|
||||||
|
$pdfPath = (new \getSpecificData())->getDocumentRoot() . '/static/' . $pdfName;
|
||||||
|
$exists = file_exists($pdfPath);
|
||||||
|
?>
|
||||||
|
<li>
|
||||||
|
<code><?= htmlspecialchars($woche['start_date']) ?></code> bis
|
||||||
|
<code><?= htmlspecialchars($woche['end_date']) ?></code>
|
||||||
|
<?php if($exists): ?>
|
||||||
|
<span style="color:green;">(PDF vorhanden)</span>
|
||||||
|
<a href="<?= htmlspecialchars('/static/' . $pdfName) ?>" download class="btn btn-success">
|
||||||
|
Download
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span style="color:red;">(noch keine PDF)</span>
|
||||||
|
<form method="post" style="display:inline;">
|
||||||
|
<input type="hidden" name="start_date" value="<?= htmlspecialchars($woche['start_date']) ?>">
|
||||||
|
<input type="hidden" name="end_date" value="<?= htmlspecialchars($woche['end_date']) ?>">
|
||||||
|
<button type="submit" name="generate_pdf" class="btn btn-primary">
|
||||||
|
PDF erzeugen
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
@@ -1,20 +1,3 @@
|
|||||||
<?php
|
|
||||||
$data = new getSpecificData();
|
|
||||||
$allData = $data->getAllData();
|
|
||||||
|
|
||||||
// Daten nach Gruppen sortieren
|
|
||||||
$groupedData = [];
|
|
||||||
foreach ($allData as $key => $value) {
|
|
||||||
if (strpos($key, ' - ') !== false) {
|
|
||||||
[$group, $field] = explode(' - ', $key, 2);
|
|
||||||
} else {
|
|
||||||
$group = 'Other';
|
|
||||||
$field = $key;
|
|
||||||
}
|
|
||||||
$groupedData[$group][$field] = $value;
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<h2>System & Client Info</h2>
|
<h2>System & Client Info</h2>
|
||||||
|
|
||||||
<?php foreach ($groupedData as $groupName => $fields): ?>
|
<?php foreach ($groupedData as $groupName => $fields): ?>
|
||||||
@@ -36,7 +19,6 @@ foreach ($allData as $key => $value) {
|
|||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<!-- ===== JS für einklappbare Gruppen ===== -->
|
|
||||||
<script>
|
<script>
|
||||||
document.querySelectorAll('.group-header').forEach(header => {
|
document.querySelectorAll('.group-header').forEach(header => {
|
||||||
header.addEventListener('click', () => {
|
header.addEventListener('click', () => {
|
||||||
@@ -47,7 +29,6 @@ document.querySelectorAll('.group-header').forEach(header => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- ===== CSS für einklappbare Gruppen ===== -->
|
|
||||||
<style>
|
<style>
|
||||||
.group-container {
|
.group-container {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
@@ -85,7 +66,6 @@ body.dark-mode .group-header:hover {
|
|||||||
|
|
||||||
.group-content.open {
|
.group-content.open {
|
||||||
max-height: 2000px;
|
max-height: 2000px;
|
||||||
/* ausreichend hoch für alle Elemente */
|
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +98,6 @@ body.dark-mode .group-header:hover {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark Mode Anpassungen */
|
|
||||||
body.dark-mode .group-header {
|
body.dark-mode .group-header {
|
||||||
background-color: var(--card-bg);
|
background-color: var(--card-bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<div class="documentation-container">
|
||||||
|
<h2>📚 Projekt-Dokumentation</h2>
|
||||||
|
|
||||||
|
<div class="search-bar">
|
||||||
|
<form method="GET">
|
||||||
|
<input type="hidden" name="service" value="documentation">
|
||||||
|
<input type="text" name="q" value="<?= htmlspecialchars($query) ?>" placeholder="Suchen...">
|
||||||
|
<button type="submit">Suchen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="doc-list">
|
||||||
|
<?php if (empty($docs)): ?>
|
||||||
|
<p>Keine Einträge gefunden.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($docs as $doc): ?>
|
||||||
|
<div class="doc-item">
|
||||||
|
<span class="category-badge"><?= htmlspecialchars($doc['KATEGORIE']) ?></span>
|
||||||
|
<h3><?= htmlspecialchars($doc['TITEL']) ?></h3>
|
||||||
|
<p><?= nl2br(htmlspecialchars($doc['INHALT'])) ?></p>
|
||||||
|
<small>Erstellt am: <?= date('d.m.Y H:i', strtotime($doc['ERSTELLT_AM'])) ?></small>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($pages > 1): ?>
|
||||||
|
<div class="pagination">
|
||||||
|
<?php for ($i = 1; $i <= $pages; $i++): ?>
|
||||||
|
<a href="?service=documentation&q=<?= urlencode($query) ?>&page=<?= $i ?>"
|
||||||
|
class="<?= $i === $currentPage ? 'active' : '' ?>">
|
||||||
|
<?= $i ?>
|
||||||
|
</a>
|
||||||
|
<?php endfor; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.documentation-container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar form {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
background: var(--input-bg);
|
||||||
|
color: var(--input-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
background: var(--btn-bg);
|
||||||
|
color: white;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: var(--btn-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item p {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination a.active {
|
||||||
|
background: var(--btn-bg);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--btn-bg);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<h2>Domain Verwaltung</h2>
|
||||||
|
|
||||||
|
<?php if ($statusMsg): ?>
|
||||||
|
<div class="status-msg"><?= htmlspecialchars($statusMsg) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="admin-grid">
|
||||||
|
<?php foreach ($domains as $domain): ?>
|
||||||
|
<form method="post" class="admin-card">
|
||||||
|
<input type="hidden" name="id" value="<?= $domain["ID"] ?>">
|
||||||
|
<label>Subdomain</label>
|
||||||
|
<input type="text" name="subdomain" value="<?= htmlspecialchars($domain["SUBDOMAIN"]) ?>">
|
||||||
|
<div style="margin-top:10px; display:flex; gap:10px;">
|
||||||
|
<button type="submit" name="save_domain" class="btn-save">Speichern</button>
|
||||||
|
<button type="submit" name="delete_domain" class="btn-delete">Löschen</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<form method="post" class="admin-card new-item">
|
||||||
|
<h3>Neu</h3>
|
||||||
|
<label>Subdomain</label>
|
||||||
|
<input type="text" name="subdomain" placeholder="z.B. subdomain.domain.net">
|
||||||
|
<button type="submit" name="add_domain" class="btn-add">Domain hinzufügen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@@ -1,17 +1,3 @@
|
|||||||
<?php
|
|
||||||
$username = $_SESSION['username'] ?? 'Gast';
|
|
||||||
$is_superuser = $_SESSION['is_superuser'] ?? false;
|
|
||||||
$is_admin = $_SESSION['is_admin'] ?? false;
|
|
||||||
|
|
||||||
$db_res = DatabaseShow(
|
|
||||||
"Benutzer",
|
|
||||||
"NACHNAME, EMAIL, ROLLE, HOME_IP",
|
|
||||||
"NAME = ?",
|
|
||||||
$username
|
|
||||||
);
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="home-dashboard">
|
<div class="home-dashboard">
|
||||||
<div class="links-container">
|
<div class="links-container">
|
||||||
|
|
||||||
@@ -19,18 +5,20 @@ $db_res = DatabaseShow(
|
|||||||
<h3 style="background-color: #007bff;">👤 Benutzerprofil</h3>
|
<h3 style="background-color: #007bff;">👤 Benutzerprofil</h3>
|
||||||
<div class="group-links" style="padding: 10px; color: inherit;">
|
<div class="group-links" style="padding: 10px; color: inherit;">
|
||||||
<p style="margin: 5px 0;"><strong>Name:</strong> <code><?= htmlspecialchars($username) ?></code></p>
|
<p style="margin: 5px 0;"><strong>Name:</strong> <code><?= htmlspecialchars($username) ?></code></p>
|
||||||
|
<?php if ($username != 'gast'): ?>
|
||||||
<p style="margin: 5px 0;"><strong>Nachname:</strong>
|
<p style="margin: 5px 0;"><strong>Nachname:</strong>
|
||||||
<code><?= htmlspecialchars($db_res[0]['NACHNAME']) ?></code>
|
<code><?= htmlspecialchars($db_res[0]['NACHNAME'] ?? '') ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>E-Mail:</strong>
|
<p style="margin: 5px 0;"><strong>E-Mail:</strong>
|
||||||
<code><?= htmlspecialchars($db_res[0]['EMAIL']) ?></code>
|
<code><?= htmlspecialchars($db_res[0]['EMAIL'] ?? '') ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>Benutzerrolle:</strong>
|
<p style="margin: 5px 0;"><strong>Benutzerrolle:</strong>
|
||||||
<code><?= htmlspecialchars($db_res[0]['ROLLE']) ?></code>
|
<code><?= htmlspecialchars($db_res[0]['ROLLE'] ?? '') ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>Home IP:</strong>
|
<p style="margin: 5px 0;"><strong>Home IP:</strong>
|
||||||
<code><?= htmlspecialchars($db_res[0]['HOME_IP']) ?></code>
|
<code><?= htmlspecialchars($db_res[0]['HOME_IP'] ?? '') ?></code>
|
||||||
</p>
|
</p>
|
||||||
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -38,17 +26,17 @@ $db_res = DatabaseShow(
|
|||||||
<h3 style="background-color: #28a745;">🛡️ Status & Rechte</h3>
|
<h3 style="background-color: #28a745;">🛡️ Status & Rechte</h3>
|
||||||
<div class="group-links"
|
<div class="group-links"
|
||||||
style="padding: 10px; display: flex; flex-direction: row; gap: 10px; flex-wrap: wrap;">
|
style="padding: 10px; display: flex; flex-direction: row; gap: 10px; flex-wrap: wrap;">
|
||||||
<?php if ((new getSpecificData())->getIsAdmin()): ?>
|
<?php if ((new \getSpecificData())->getIsAdmin()): ?>
|
||||||
<span
|
<span
|
||||||
style="background: #dc3545; color: white; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Admin</span>
|
style="background: #dc3545; color: white; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Admin</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ((new getSpecificData())->getIsSuperuser()): ?>
|
<?php if ((new \getSpecificData())->getIsSuperuser()): ?>
|
||||||
<span
|
<span
|
||||||
style="background: #ffc107; color: #000; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Superuser</span>
|
style="background: #ffc107; color: #000; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Superuser</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (!(new getSpecificData())->getIsSuperuser() && !(new getSpecificData())->getIsAdmin()): ?>
|
<?php if (!(new \getSpecificData())->getIsSuperuser() && !(new \getSpecificData())->getIsAdmin()): ?>
|
||||||
<span
|
<span
|
||||||
style="background: #6c757d; color: white; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Standard-Nutzer</span>
|
style="background: #6c757d; color: white; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Standard-Nutzer</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -66,19 +54,19 @@ $db_res = DatabaseShow(
|
|||||||
</p>
|
</p>
|
||||||
<?php if ($is_admin): ?>
|
<?php if ($is_admin): ?>
|
||||||
<p style="margin: 5px 0;"><strong>Client IP:</strong>
|
<p style="margin: 5px 0;"><strong>Client IP:</strong>
|
||||||
<code><?= htmlspecialchars((new getSpecificData())->getClientIP()) ?></code>
|
<code><?= htmlspecialchars((new \getSpecificData())->getClientIP()) ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>Server IP:</strong>
|
<p style="margin: 5px 0;"><strong>Server IP:</strong>
|
||||||
<code><?= htmlspecialchars((new getSpecificData())->getServerIP()) ?></code>
|
<code><?= htmlspecialchars((new \getSpecificData())->getServerIP()) ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>Geo IP:</strong>
|
<p style="margin: 5px 0;"><strong>Geo IP:</strong>
|
||||||
<code><?= htmlspecialchars((new getSpecificData())->getGeoIP((new getSpecificData())->getClientIP())) ?></code>
|
<code><?= htmlspecialchars((new \getSpecificData())->getGeoIP((new \getSpecificData())->getClientIP())) ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>Database IP:</strong>
|
<p style="margin: 5px 0;"><strong>Database IP:</strong>
|
||||||
<code><?= htmlspecialchars((new getSpecificData())->getDatabase()) ?></code>
|
<code><?= htmlspecialchars((new \getSpecificData())->getDatabase()) ?></code>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin: 5px 0;"><strong>Wetter:</strong>
|
<p style="margin: 5px 0;"><strong>Wetter:</strong>
|
||||||
<code><?= htmlspecialchars((new getSpecificData())->getWeather()['aktuell']) ?></code>
|
<code><?= htmlspecialchars((new \getSpecificData())->getWeather()['aktuell']) ?></code>
|
||||||
</p>
|
</p>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,14 +10,11 @@ if (!isset($linkGroups) || !is_array($linkGroups)) {
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!-- <h2>Externe Links</h2> -->
|
|
||||||
|
|
||||||
<div class="links-container">
|
<div class="links-container">
|
||||||
|
|
||||||
<?php foreach ($linkGroups as $groupKey => $group): ?>
|
<?php foreach ($linkGroups as $groupKey => $group): ?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
// Links dieser Gruppe sammeln
|
|
||||||
$groupLinks = array_filter($linkButtons, function($button) use ($groupKey, $isAdmin) {
|
$groupLinks = array_filter($linkButtons, function($button) use ($groupKey, $isAdmin) {
|
||||||
if (($button['group'] ?? null) !== $groupKey) {
|
if (($button['group'] ?? null) !== $groupKey) {
|
||||||
return false;
|
return false;
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<h1>📊 Logs</h1>
|
||||||
|
|
||||||
|
<form method="GET" action="index.php">
|
||||||
|
<input type="hidden" name="service" value="<?= htmlspecialchars($serviceName) ?>">
|
||||||
|
|
||||||
|
<select name="filter" onchange="this.form.submit()">
|
||||||
|
<?php
|
||||||
|
$options = ['alle','today','node1','node2','node3','node4','counterperip'];
|
||||||
|
foreach ($options as $opt) {
|
||||||
|
$sel = ($filter === $opt) ? 'selected' : '';
|
||||||
|
echo "<option value='$opt' $sel>$opt</option>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<?php if (!$daten): ?>
|
||||||
|
<b>Keine Daten gefunden</b>
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<div style="overflow-x:auto;">
|
||||||
|
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
||||||
|
<tr>
|
||||||
|
<?php foreach (array_keys($daten[0]) as $key): ?>
|
||||||
|
<th><?= htmlspecialchars($key) ?></th>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<?php foreach ($daten as $row): ?>
|
||||||
|
<tr>
|
||||||
|
<?php foreach ($row as $val): ?>
|
||||||
|
<td><?= htmlspecialchars($val ?? '') ?></td>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<button onclick="printFiles()">Alle Dateien drucken</button>
|
||||||
|
|
||||||
|
<div class="print-container">
|
||||||
|
<?php foreach ($files as $file):
|
||||||
|
$url = "static/" . rawurlencode($file);
|
||||||
|
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||||
|
?>
|
||||||
|
<div class="print-page">
|
||||||
|
<?php if (in_array($ext, ['png','jpg','jpeg','webp'])): ?>
|
||||||
|
<img src='<?= $url ?>'>
|
||||||
|
<?php elseif ($ext === "pdf"): ?>
|
||||||
|
<embed src='<?= $url ?>' type='application/pdf'>
|
||||||
|
<?php else: ?>
|
||||||
|
<p><?= htmlspecialchars($file) ?> kann nicht direkt gedruckt werden.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function printFiles() {
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.print-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-page img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-page embed {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
body * {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.print-container,
|
||||||
|
.print-container * {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.print-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
||||||
|
|
||||||
|
<canvas id="logChart" width="900" height="500"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const ctx = document.getElementById('logChart').getContext('2d');
|
||||||
|
|
||||||
|
const logChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: <?php echo json_encode($allLabels); ?>,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Gesamt',
|
||||||
|
data: <?php echo json_encode($finalTotalData); ?>,
|
||||||
|
borderColor: 'rgb(255, 0, 0)',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Node1',
|
||||||
|
data: <?php echo json_encode($finalNodeData['node1']); ?>,
|
||||||
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0,
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Node2',
|
||||||
|
data: <?php echo json_encode($finalNodeData['node2']); ?>,
|
||||||
|
borderColor: 'rgba(255, 99, 132, 1)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0,
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Node3',
|
||||||
|
data: <?php echo json_encode($finalNodeData['node3']); ?>,
|
||||||
|
borderColor: 'rgba(255, 206, 86, 1)',
|
||||||
|
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0,
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Node4',
|
||||||
|
data: <?php echo json_encode($finalNodeData['node4']); ?>,
|
||||||
|
borderColor: 'rgba(54, 162, 235, 1)',
|
||||||
|
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0,
|
||||||
|
hidden: true,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Logs pro Tag nach Node und Gesamt'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'top'
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
pan: {
|
||||||
|
enabled: true,
|
||||||
|
mode: 'x'
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
wheel: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
mode: 'x'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Datum'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Anzahl Logs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
<div class="content">
|
||||||
|
<?php if (!$loggedIn): ?>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="login-box">
|
||||||
|
<form method="post">
|
||||||
|
<h2>🔐 Login</h2>
|
||||||
|
<?php if($error): ?>
|
||||||
|
<div class="status-msg" style="background:#dc3545; color:#fff;"><?= htmlspecialchars($error) ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<input type="password" name="password" placeholder="Passwort" required>
|
||||||
|
<button type="submit">Einloggen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="content">
|
||||||
|
<h2>Seitenverwaltung</h2>
|
||||||
|
<form method="post">
|
||||||
|
<button type="submit" name="logout" id="logoutSiteButton">Logout</button>
|
||||||
|
</form>
|
||||||
|
<?php if($message): ?><div class="status-msg"><?= htmlspecialchars($message) ?></div><?php endif; ?>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
|
||||||
|
<!-- Sidebar Gruppen -->
|
||||||
|
<h2>📦 Sidebar Gruppen
|
||||||
|
<button type="button" class="btn-add" onclick="addNew('serviceGroup')">+ Neue Gruppe</button>
|
||||||
|
</h2>
|
||||||
|
<div class="admin-grid" id="container-serviceGroup">
|
||||||
|
<?php foreach ($serviceGroups as $id => $group): ?>
|
||||||
|
<div class="admin-card">
|
||||||
|
<span class="badge">ID: <?= htmlspecialchars($id) ?></span>
|
||||||
|
<input type="text" name="serviceGroups[<?= $id ?>][label]"
|
||||||
|
value="<?= htmlspecialchars($group['label']) ?>" placeholder="Anzeigename">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="serviceGroups[<?= $id ?>][defaultOpen]"
|
||||||
|
<?= ($group['defaultOpen'] ?? false) ? 'checked' : '' ?>>
|
||||||
|
Offen
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar Services -->
|
||||||
|
<h2>🖥 Sidebar Services
|
||||||
|
<button type="button" class="btn-add" onclick="addNew('service')">+ Neuer Service</button>
|
||||||
|
</h2>
|
||||||
|
<div class="admin-grid" id="container-service">
|
||||||
|
<?php foreach ($services as $id => $s): ?>
|
||||||
|
<div class="admin-card">
|
||||||
|
<span class="badge">Datei: <?= htmlspecialchars($id) ?>.php</span>
|
||||||
|
<input type="text" name="services[<?= $id ?>][name]" value="<?= htmlspecialchars($s['name']) ?>"
|
||||||
|
placeholder="Name">
|
||||||
|
<label>Sidebar-Gruppe:</label>
|
||||||
|
<select name="services[<?= $id ?>][targetGroup]">
|
||||||
|
<?php foreach ($serviceGroups as $gid => $g): ?>
|
||||||
|
<option value="<?= $gid ?>"
|
||||||
|
<?= (isset($g['services']) && in_array($id, $g['services'])) ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($g['label']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="services[<?= $id ?>][active]"
|
||||||
|
<?= ($s['active'] ?? false) ? 'checked' : '' ?>>
|
||||||
|
Aktiv
|
||||||
|
</label>
|
||||||
|
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="services[<?= $id ?>][adminOnly]"
|
||||||
|
<?= ($s['adminOnly'] ?? false) ? 'checked' : '' ?>>
|
||||||
|
Nur Admins
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dashboard Kategorien -->
|
||||||
|
<h2>🎨 Dashboard Kategorien
|
||||||
|
<button type="button" class="btn-add" onclick="addNew('linkGroup')">+ Neue Kategorie</button>
|
||||||
|
</h2>
|
||||||
|
<div class="admin-grid" id="container-linkGroup">
|
||||||
|
<?php foreach ($linkGroups as $id => $lg): ?>
|
||||||
|
<div class="admin-card">
|
||||||
|
<span class="badge">ID: <?= htmlspecialchars($id) ?></span>
|
||||||
|
<input type="text" name="linkGroups[<?= $id ?>][label]"
|
||||||
|
value="<?= htmlspecialchars($lg['label']) ?>" placeholder="Gruppen-Label">
|
||||||
|
<label>Farbe:</label>
|
||||||
|
<input type="color" name="linkGroups[<?= $id ?>][color]"
|
||||||
|
value="<?= $lg['color'] ?? '#3498db' ?>">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="linkGroups[<?= $id ?>][defaultOpen]"
|
||||||
|
<?= ($lg['defaultOpen'] ?? false) ? 'checked' : '' ?>>
|
||||||
|
Offen
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dashboard Kacheln -->
|
||||||
|
<h2>🔘 Dashboard Kacheln
|
||||||
|
<button type="button" class="btn-add" onclick="addNew('linkButton')">+ Neue Kachel</button>
|
||||||
|
</h2>
|
||||||
|
<div class="admin-grid" id="container-linkButton">
|
||||||
|
<?php foreach ($linkButtons as $key => $btn): ?>
|
||||||
|
<div class="admin-card">
|
||||||
|
<input type="text" name="linkButtons[<?= $key ?>][label]"
|
||||||
|
value="<?= htmlspecialchars($btn['label']) ?>" placeholder="Name">
|
||||||
|
<input type="text" name="linkButtons[<?= $key ?>][url]"
|
||||||
|
value="<?= htmlspecialchars($btn['url']) ?>" placeholder="URL">
|
||||||
|
<label>Dashboard Kategorie:</label>
|
||||||
|
<select name="linkButtons[<?= $key ?>][group]">
|
||||||
|
<?php foreach ($linkGroups as $id => $lg): ?>
|
||||||
|
<option value="<?= $id ?>" <?= ($btn['group'] == $id) ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($lg['label']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<div style="margin-top:5px;">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="linkButtons[<?= $key ?>][newTab]"
|
||||||
|
<?= ($btn['newTab'] ?? false) ? 'checked' : '' ?>>
|
||||||
|
Neuer Tab
|
||||||
|
</label>
|
||||||
|
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="linkButtons[<?= $key ?>][adminOnly]"
|
||||||
|
<?= ($btn['adminOnly'] ?? false) ? 'checked' : '' ?>>
|
||||||
|
Nur Admins
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="save-bar">
|
||||||
|
<button type="submit" name="save" class="btn-save">💾 Gesamte Konfiguration speichern</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function addNew(type) {
|
||||||
|
const container = document.getElementById('container-' + type);
|
||||||
|
const tempId = 'new_' + Date.now();
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'admin-card new-item';
|
||||||
|
|
||||||
|
let html = `<div><span class="badge">Neu hinzufügen</span></div>`;
|
||||||
|
|
||||||
|
if (type === 'serviceGroup') {
|
||||||
|
html +=
|
||||||
|
`<input type="text" name="serviceGroups[${tempId}][new_id]" placeholder="ID">
|
||||||
|
<input type="text" name="serviceGroups[${tempId}][label]" placeholder="Name">
|
||||||
|
<label><input type="checkbox" name="serviceGroups[${tempId}][defaultOpen]" checked> Ausgeklappt</label>`;
|
||||||
|
} else if (type === 'service') {
|
||||||
|
html += `<input type="text" name="services[${tempId}][new_id]" placeholder="Datei ohne .php">
|
||||||
|
<input type="text" name="services[${tempId}][name]" placeholder="Anzeigename">
|
||||||
|
<label>Gruppe:</label>
|
||||||
|
<select name="services[${tempId}][targetGroup]">
|
||||||
|
<?php foreach ($serviceGroups as $gid => $gl): ?>
|
||||||
|
<option value="<?= $gid ?>"><?= $gl['label'] ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<label><input type="checkbox" name="services[${tempId}][active]" checked> Aktiv</label>`;
|
||||||
|
} else if (type === 'linkGroup') {
|
||||||
|
html += `<input type="text" name="linkGroups[${tempId}][new_id]" placeholder="ID">
|
||||||
|
<input type="text" name="linkGroups[${tempId}][label]" placeholder="Label">
|
||||||
|
<input type="color" name="linkGroups[${tempId}][color]" value="#3498db">`;
|
||||||
|
} else if (type === 'linkButton') {
|
||||||
|
html += `<input type="text" name="linkButtons[${tempId}][label]" placeholder="Anzeigename">
|
||||||
|
<input type="text" name="linkButtons[${tempId}][url]" placeholder="https://...">
|
||||||
|
<label>Kategorie:</label>
|
||||||
|
<select name="linkButtons[${tempId}][group]">
|
||||||
|
<?php foreach ($linkGroups as $gid => $gl): ?>
|
||||||
|
<option value="<?= $gid ?>"><?= $gl['label'] ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<label><input type="checkbox" name="linkButtons[${tempId}][newTab]" checked> Tab</label>
|
||||||
|
| <label><input type="checkbox" name="linkButtons[${tempId}][adminOnly]"> Nur Admins</label>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
card.innerHTML = html;
|
||||||
|
container.appendChild(card);
|
||||||
|
card.scrollIntoView({
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<h2>Datenbank Struktur</h2>
|
||||||
|
|
||||||
|
<form method="GET">
|
||||||
|
<input type="hidden" name="service" value="tabellen">
|
||||||
|
|
||||||
|
<label for="table">Tabelle auswählen:</label>
|
||||||
|
<select name="table" id="table" onchange="this.form.submit()">
|
||||||
|
<option value="">-- Bitte wählen --</option>
|
||||||
|
<?php foreach ($tables as $table): ?>
|
||||||
|
<option value="<?= htmlspecialchars($table) ?>" <?= ($selectedTable === $table) ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($table) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php if ($selectedTable && !empty($structure)): ?>
|
||||||
|
<div class='table-container'>
|
||||||
|
<table class='db-table'>
|
||||||
|
<tr>
|
||||||
|
<th>Field</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Null</th>
|
||||||
|
<th>Key</th>
|
||||||
|
<th>Default</th>
|
||||||
|
<th>Extra</th>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($structure as $column): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($column['Field']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($column['Type']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($column['Null']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($column['Key']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($column['Default'] ?? '') ?></td>
|
||||||
|
<td><?= htmlspecialchars($column['Extra']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
@@ -1,78 +1,10 @@
|
|||||||
<?php
|
|
||||||
// 1. Logik zum Verarbeiten des Formulars (bevor die Liste geladen wird)
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_user'])) {
|
|
||||||
// Nur Admins dürfen Benutzer hinzufügen (Sicherheitscheck)
|
|
||||||
if ((new getSpecificData())->getIsAdmin()) {
|
|
||||||
$controller = new UserController();
|
|
||||||
$controller->setUser(
|
|
||||||
$_POST['name'],
|
|
||||||
$_POST['nachname'],
|
|
||||||
$_POST['email'],
|
|
||||||
$_POST['passwort'],
|
|
||||||
$_POST['rolle'],
|
|
||||||
$_POST['admin'] ?? 0, // Falls Checkbox nicht gesetzt, 0 senden
|
|
||||||
$_POST['home_ip']
|
|
||||||
);
|
|
||||||
// Optional: Seite neu laden, um die Liste zu aktualisieren
|
|
||||||
header("Location: " . (new getSpecificData())->getReferer());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST['password'])) {
|
|
||||||
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
|
||||||
$_SESSION['logged_in'] = true;
|
|
||||||
} else {
|
|
||||||
$error = "Falsches Passwort!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Daten für die Tabelle holen (dein bestehender Code)
|
|
||||||
$specificData = new getSpecificData();
|
|
||||||
$isAdmin = $specificData->getIsAdmin();
|
|
||||||
|
|
||||||
if ($isAdmin) {
|
|
||||||
$users = DatabaseShow("Benutzer", "NAME, NACHNAME, EMAIL, ROLLE, ADMIN, HOME_IP");
|
|
||||||
} else {
|
|
||||||
$users = DatabaseShow(
|
|
||||||
"Benutzer",
|
|
||||||
"NAME, NACHNAME, EMAIL, ROLLE, ADMIN, HOME_IP",
|
|
||||||
"NAME = ?",
|
|
||||||
$specificData->getUsername()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_user'])) {
|
|
||||||
|
|
||||||
if ((new getSpecificData())->getIsAdmin()) {
|
|
||||||
|
|
||||||
$controller = new UserController();
|
|
||||||
$controller->DeleteUser(
|
|
||||||
$_POST['delete_name'],
|
|
||||||
$_POST['delete_nachname'],
|
|
||||||
$_POST['delete_email']
|
|
||||||
);
|
|
||||||
|
|
||||||
header("Location: " . (new getSpecificData())->getReferer());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST['logout'])) {
|
|
||||||
unset($_SESSION['logged_in']);
|
|
||||||
header("Location: " . (new getSpecificData())->getReferer());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<?php if (!($_SESSION['logged_in'] ?? false)): ?>
|
<?php if (!$loggedIn): ?>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<h2>🔐 Login</h2>
|
<h2>🔐 Login</h2>
|
||||||
<?php if(isset($error)): ?>
|
<?php if($error): ?>
|
||||||
<div class="status-msg" style="background:#dc3545; color:#fff;"><?= htmlspecialchars($error) ?>
|
<div class="status-msg" style="background:#dc3545; color:#fff;"><?= htmlspecialchars($error) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -156,7 +88,3 @@ if (isset($_POST['logout'])) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@@ -1,29 +1,3 @@
|
|||||||
<?php
|
|
||||||
if (isset($_POST['toggle_variable'])) {
|
|
||||||
|
|
||||||
$name = $_POST['variablenname'];
|
|
||||||
|
|
||||||
$current = (new getSpecificData())->getLoggingStatus($name)[0]['WERT'];
|
|
||||||
$newValue = ($current === "ON") ? "OFF" : "ON";
|
|
||||||
|
|
||||||
DatabaseUpdate(
|
|
||||||
"Variablen",
|
|
||||||
"WERT = ?",
|
|
||||||
"VARIABLENNAME = ?",
|
|
||||||
$newValue,
|
|
||||||
$name
|
|
||||||
);
|
|
||||||
header("Location: " . (new getSpecificData())->getReferer());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$variablen = DatabaseShow(
|
|
||||||
"Variablen",
|
|
||||||
"VARIABLENNAME, WERT, ANZEIGENAME"
|
|
||||||
);
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="admin-grid">
|
<div class="admin-grid">
|
||||||
|
|
||||||
<?php foreach ($variablen as $var): ?>
|
<?php foreach ($variablen as $var): ?>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
||||||
|
|
||||||
|
<canvas id="logChart" width="600" height="400"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const ctx = document.getElementById('logChart').getContext('2d');
|
||||||
|
const logChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: <?php echo json_encode($labels); ?>,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Wetter',
|
||||||
|
data: <?php echo json_encode($data); ?>,
|
||||||
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
title: { display: true },
|
||||||
|
legend: { display: true, position: 'top' },
|
||||||
|
zoom: {
|
||||||
|
pan: { enabled: true, mode: 'x' },
|
||||||
|
zoom: { wheel: { enabled: true }, mode: 'x' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interaction: { mode: 'index', intersect: false },
|
||||||
|
scales: {
|
||||||
|
x: { title: { display: true, text: 'Zeitraum' } },
|
||||||
|
y: {
|
||||||
|
min: -10,
|
||||||
|
max: 40,
|
||||||
|
title: { display: true, text: 'Temperatur in °C' },
|
||||||
|
ticks: { stepSize: 1 },
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<div class="arbeitsberichte-wrapper">
|
||||||
|
<div
|
||||||
|
style="background: rgba(150,150,150,0.05); padding: 25px; border-radius: 10px; margin-bottom: 25px; border: 1px solid rgba(150,150,150,0.2); border-left: 6px solid #007bff;">
|
||||||
|
<div style="margin-bottom: 20px;">
|
||||||
|
<span style="color: #888; font-size: 0.8rem; font-weight: bold; text-transform: uppercase;">Aktueller
|
||||||
|
Stand</span>
|
||||||
|
<h2 style="margin: 5px 0 0 0; font-size: 2rem; color: <?= $gesamt_minuten >= 0 ? '#28a745' : '#dc3545' ?>;">
|
||||||
|
<?= intdiv($gesamt_minuten, 60) ?>h <?= abs($gesamt_minuten % 60) ?>min
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<?php if (strtolower($username) == "bruno"): ?>
|
||||||
|
<form method="POST" style="display: flex; gap: 15px; flex-wrap: wrap; align-items: flex-end;">
|
||||||
|
<input type="hidden" name="add_overtime" value="1">
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 5px;">
|
||||||
|
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Datum</label>
|
||||||
|
<input type="date" name="ot_date" value="<?= date('Y-m-d') ?>" required
|
||||||
|
style="padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 5px;">
|
||||||
|
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Minuten Abbau</label>
|
||||||
|
<input type="number" name="ot_minutes" placeholder="480" required
|
||||||
|
style="width: 100px; padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Grund</label>
|
||||||
|
<select name="ot_reason" required>
|
||||||
|
<option value="">Bitte wählen</option>
|
||||||
|
<option value="Abbau">Abbau</option>
|
||||||
|
<option value="Aufbau">Aufbau</option>
|
||||||
|
<option value="Anderes">Anderes</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit"
|
||||||
|
style="background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; font-weight: bold; cursor: pointer;">
|
||||||
|
Eintrag speichern
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
$prefix = 'MVC\\';
|
||||||
|
$base_dir = __DIR__ . '/';
|
||||||
|
|
||||||
|
$len = strlen($prefix);
|
||||||
|
if (strncmp($prefix, $class, $len) !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relative_class = substr($class, $len);
|
||||||
|
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
||||||
|
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
class getSpecificData {
|
class getSpecificData {
|
||||||
|
|
||||||
public function getWeather() {
|
public function getWeather() {
|
||||||
$url = "https://api.openweathermap.org/data/2.5/weather?q=Berlin,DE&APPID=5bc3e1a438c58fca444c4370ac0517b2&units=metric";
|
$url = "https://api.openweathermap.org/data/2.5/weather?q=Jena,DE&APPID=5bc3e1a438c58fca444c4370ac0517b2&units=metric";
|
||||||
|
|
||||||
$ch = curl_init();
|
$ch = curl_init();
|
||||||
curl_setopt($ch, CURLOPT_URL, $url);
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
@@ -148,7 +148,7 @@ class getSpecificData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWhitelistedIPs() {
|
function getWhitelistedIPs() {
|
||||||
include_once 'DatabaseController.php';
|
include_once(__DIR__ . '/../utils/databaseMethods.php');
|
||||||
$data = DatabaseShow(
|
$data = DatabaseShow(
|
||||||
"Whitelisted",
|
"Whitelisted",
|
||||||
"`IP-ADDRESSE`"
|
"`IP-ADDRESSE`"
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
APP_ENV = "vpn"
|
||||||
|
|
||||||
|
DB_LOCAL_HOST = "127.0.0.1"
|
||||||
|
DB_LOCAL_USERNAME = "worker"
|
||||||
|
DB_LOCAL_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||||
|
DB_LOCAL_DATABASE = "DEV"
|
||||||
|
|
||||||
|
DB_VPN_HOST = "100.123.136.86"
|
||||||
|
DB_VPN_USERNAME = "worker"
|
||||||
|
DB_VPN_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||||
|
DB_VPN_DATABASE = "DEV"
|
||||||
|
|
||||||
|
DB_BACKUP_HOST = "10.233.1.241"
|
||||||
|
DB_BACKUP_USERNAME = "worker"
|
||||||
|
DB_BACKUP_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||||
|
DB_BACKUP_DATABASE = "DEV"
|
||||||
|
|
||||||
|
DB_DEV_HOST = "10.233.1.243"
|
||||||
|
DB_DEV_USERNAME = "worker"
|
||||||
|
DB_DEV_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||||
|
DB_DEV_DATABASE = "DEV"
|
||||||
|
|
||||||
|
DB_LIVE_HOST = "10.233.1.244"
|
||||||
|
DB_LIVE_USERNAME = "worker"
|
||||||
|
DB_LIVE_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||||
|
DB_LIVE_DATABASE = "DEV"
|
||||||
|
|
||||||
|
DB_ERROR_HOST = "10.233.1.244"
|
||||||
|
DB_ERROR_USERNAME = "error"
|
||||||
|
DB_ERROR_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||||
|
DB_ERROR_DATABASE = "DEV"
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require(__DIR__ . '/../utils/formatter.php');
|
||||||
|
|
||||||
|
class ApiController {
|
||||||
|
|
||||||
|
use ReponseFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method for pretty jsonsuccess output
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function jsonSuccess($data) {
|
||||||
|
return $this->formatJson("success", $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method for pretty jsonerror output
|
||||||
|
*
|
||||||
|
* @param array $error
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function jsonError ($error) {
|
||||||
|
return $this->formatJson("failed", $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get authentication key out database
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getAuthKey () {
|
||||||
|
$validTokens = DatabaseShow(
|
||||||
|
"API_Credentials",
|
||||||
|
"TOKEN"
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_column($validTokens, 'TOKEN');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url authentication method via token and optional parameter function
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
* @param string $function
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function urlAuth ($token, $function = '') {
|
||||||
|
|
||||||
|
$validTokens = $this->getAuthKey();
|
||||||
|
if (!in_array($token, $validTokens)) {
|
||||||
|
http_response_code(401);
|
||||||
|
|
||||||
|
$error = $this->formatJson("error", $token);
|
||||||
|
|
||||||
|
DatabaseInsert(
|
||||||
|
"API_LOGS",
|
||||||
|
"CLIENT_IP, TOKEN, API, FUNKTION, JSON",
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
$token,
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
$function,
|
||||||
|
$error
|
||||||
|
);
|
||||||
|
|
||||||
|
errorMail(
|
||||||
|
"API Login failed",
|
||||||
|
$error,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requestSave($function = '') {
|
||||||
|
DatabaseInsert(
|
||||||
|
"API_LOGS",
|
||||||
|
"CLIENT_IP, TOKEN, API, FUNKTION, JSON",
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
$_GET['token'],
|
||||||
|
(new getSpecificData())->getRequestURL(),
|
||||||
|
$function,
|
||||||
|
'{"status": "success"}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create new api token
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function createToken() {
|
||||||
|
return bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save created api token
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveToken() {
|
||||||
|
try {
|
||||||
|
DatabaseInsert(
|
||||||
|
"API_Credentials",
|
||||||
|
"TOKEN, BENUTZER",
|
||||||
|
$this->createToken(),
|
||||||
|
"API-User"
|
||||||
|
);
|
||||||
|
} catch (Exception $error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once(__DIR__ . '/../utils/formatter.php');
|
||||||
|
|
||||||
|
class RightsController {
|
||||||
|
|
||||||
|
use ReponseFormatter;
|
||||||
|
/**
|
||||||
|
* save new site with sitename
|
||||||
|
*
|
||||||
|
* @param string $sitename
|
||||||
|
*/
|
||||||
|
private function saveSite($sitename) {
|
||||||
|
DatabaseInsert(
|
||||||
|
"Backend_Seiten",
|
||||||
|
"DATEINAME",
|
||||||
|
$sitename
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get site id by sitename
|
||||||
|
*
|
||||||
|
* @param string $sitename
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function getSiteID($sitename) {
|
||||||
|
$id = DatabaseShow(
|
||||||
|
"Backend_Seiten",
|
||||||
|
"ID",
|
||||||
|
"DATEINAME = ?",
|
||||||
|
$sitename
|
||||||
|
);
|
||||||
|
return $id[0]['ID'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get user id
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $lastname
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function getUserID($name, $lastname) {
|
||||||
|
$id = DatabaseShow(
|
||||||
|
"Benutzer",
|
||||||
|
"ID",
|
||||||
|
"NAME = ? AND NACHNAME = ?",
|
||||||
|
$name,
|
||||||
|
$lastname
|
||||||
|
);
|
||||||
|
return $id[0]['ID'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rights Handler for saving default user is Bruno
|
||||||
|
*
|
||||||
|
* @param string $sitename
|
||||||
|
* @param string $name
|
||||||
|
* @param string $lastname
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function RightsHandler($sitename, $name = 'Bruno', $lastname = 'Eckhardt') {
|
||||||
|
try {
|
||||||
|
$sitename = strtolower($sitename);
|
||||||
|
$this->saveSite($sitename);
|
||||||
|
|
||||||
|
DatabaseInsert(
|
||||||
|
"Benutzer_Berechtigungen",
|
||||||
|
"BENUTZER_ID, SEITEN_ID",
|
||||||
|
$this->getUserID($name, $lastname),
|
||||||
|
$this->getSiteID($sitename)
|
||||||
|
);
|
||||||
|
} catch (Exception $error) {
|
||||||
|
return json_encode(
|
||||||
|
[
|
||||||
|
"status" => "failed",
|
||||||
|
"error" => $error->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return json_encode(
|
||||||
|
[
|
||||||
|
"status" => "success",
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
class UserController {
|
class UserController {
|
||||||
public function setUser($name, $nachname, $email, $passwort, $rolle, $admin, $home_ip) {
|
public function setUser($name, $nachname, $email, $passwort, $rolle, $admin, $home_ip) {
|
||||||
|
|
||||||
@@ -16,6 +17,20 @@ class UserController {
|
|||||||
$admin,
|
$admin,
|
||||||
$home_ip,
|
$home_ip,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$userID = DatabaseShow(
|
||||||
|
"Benutzer",
|
||||||
|
"ID",
|
||||||
|
"NAME = ?",
|
||||||
|
$name
|
||||||
|
);
|
||||||
|
|
||||||
|
DatabaseInsert(
|
||||||
|
"Benutzer_Berechtigungen",
|
||||||
|
"BENUTZER_ID, SEITEN_ID",
|
||||||
|
$userID[0]['ID'],
|
||||||
|
"8"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DeleteUser($name, $nachname, $email) {
|
public function DeleteUser($name, $nachname, $email) {
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$_ENV = parse_ini_file(__DIR__ . '/../config/env.ini');
|
||||||
|
|
||||||
|
include_once( __DIR__ . '/../utils/mailer.php');
|
||||||
|
include_once( __DIR__ . '/../utils/router.php');
|
||||||
|
include_once( __DIR__ . '/../classes/getData.php');
|
||||||
|
|
||||||
|
function db_connect() {
|
||||||
|
|
||||||
|
$environment = $_ENV['APP_ENV'];
|
||||||
|
|
||||||
|
if (function_exists('mysqli_report')) {
|
||||||
|
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$conn = new mysqli(
|
||||||
|
$_ENV['DB_' . strtoupper($environment) . '_HOST'],
|
||||||
|
$_ENV['DB_' . strtoupper($environment) . '_USERNAME'],
|
||||||
|
$_ENV['DB_' . strtoupper($environment) . '_PASSWORD'],
|
||||||
|
$_ENV['DB_' . strtoupper($environment) . '_DATABASE']
|
||||||
|
);
|
||||||
|
|
||||||
|
$conn->set_charset("utf8mb4");
|
||||||
|
return $conn;
|
||||||
|
|
||||||
|
} catch (mysqli_sql_exception $e) {
|
||||||
|
errorMail(
|
||||||
|
"Database Connection failed",
|
||||||
|
$e->getMessage(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
redirectToWebsite("https://blackpi.ipv64.net");
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db_connect();
|
||||||
|
?>
|
||||||
File renamed without changes.
+119
-26
@@ -1,7 +1,74 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
function DatabaseCreateTable($table, $columnsDefinition) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS `$table` ($columnsDefinition)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseShowTables() {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$tablesResult = $conn->query("SHOW TABLES");
|
||||||
|
if (!$tablesResult) {
|
||||||
|
die("Fehler beim Laden der Tabellen: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tables = [];
|
||||||
|
|
||||||
|
while ($tableRow = $tablesResult->fetch_array()) {
|
||||||
|
$tables[] = $tableRow[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseShowTableStructure($table) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$stmt = $conn->prepare("SHOW COLUMNS FROM `$table`");
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
$columns = [];
|
||||||
|
|
||||||
|
while ($column = $result->fetch_assoc()) {
|
||||||
|
$columns[] = [
|
||||||
|
"Field" => $column["Field"],
|
||||||
|
"Type" => $column["Type"],
|
||||||
|
"Null" => $column["Null"],
|
||||||
|
"Key" => $column["Key"],
|
||||||
|
"Default" => $column["Default"],
|
||||||
|
"Extra" => $column["Extra"]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
|
||||||
function DatabaseShow($table, $columns = "*", $where = "", ...$values) {
|
function DatabaseShow($table, $columns = "*", $where = "", ...$values) {
|
||||||
include_once 'DatabaseConnect.php';
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
$conn = db_connect();
|
$conn = db_connect();
|
||||||
|
|
||||||
$sql = "SELECT $columns FROM `$table`";
|
$sql = "SELECT $columns FROM `$table`";
|
||||||
@@ -26,7 +93,7 @@ function DatabaseShow($table, $columns = "*", $where = "", ...$values) {
|
|||||||
|
|
||||||
|
|
||||||
function DatabaseInsert($table, $columns, ...$values) {
|
function DatabaseInsert($table, $columns, ...$values) {
|
||||||
include_once 'DatabaseConnect.php';
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
$conn = db_connect();
|
$conn = db_connect();
|
||||||
|
|
||||||
$placeholders = rtrim(str_repeat("?,", count($values)), ",");
|
$placeholders = rtrim(str_repeat("?,", count($values)), ",");
|
||||||
@@ -45,7 +112,7 @@ function DatabaseInsert($table, $columns, ...$values) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DatabaseDelete($table, $where, ...$values) {
|
function DatabaseDelete($table, $where, ...$values) {
|
||||||
include_once 'DatabaseConnect.php';
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
$conn = db_connect();
|
$conn = db_connect();
|
||||||
|
|
||||||
$sql = "DELETE FROM `$table` WHERE $where";
|
$sql = "DELETE FROM `$table` WHERE $where";
|
||||||
@@ -63,7 +130,7 @@ function DatabaseDelete($table, $where, ...$values) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DatabaseUpdate($table, $set, $where, ...$values) {
|
function DatabaseUpdate($table, $set, $where, ...$values) {
|
||||||
include_once 'DatabaseConnect.php';
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
$conn = db_connect();
|
$conn = db_connect();
|
||||||
|
|
||||||
$sql = "UPDATE `$table` SET $set WHERE $where";
|
$sql = "UPDATE `$table` SET $set WHERE $where";
|
||||||
@@ -80,10 +147,37 @@ function DatabaseUpdate($table, $set, $where, ...$values) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFKValue($conn, $table, $key, $value, $id, &$cache) {
|
||||||
|
if ($id === null) return null;
|
||||||
|
|
||||||
|
$cacheKey = $table . "_" . $id;
|
||||||
|
|
||||||
|
if (isset($cache[$cacheKey])) {
|
||||||
|
return $cache[$cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $conn->prepare("SELECT `$value` FROM `$table` WHERE `$key` = ? LIMIT 1");
|
||||||
|
if (!$stmt) {
|
||||||
|
die("FK Prepare Fehler: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("i", $id);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$result = $stmt->get_result()->fetch_assoc();
|
||||||
|
$valueResult = $result[$value] ?? null;
|
||||||
|
|
||||||
|
$cache[$cacheKey] = $valueResult;
|
||||||
|
|
||||||
|
return $valueResult;
|
||||||
|
}
|
||||||
|
|
||||||
function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ...$values) {
|
function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ...$values) {
|
||||||
include_once 'DatabaseConnect.php';
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
$conn = db_connect();
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$cache = [];
|
||||||
|
|
||||||
$select = is_array($columns) ? implode(", ", $columns) : $columns;
|
$select = is_array($columns) ? implode(", ", $columns) : $columns;
|
||||||
|
|
||||||
$sql = "SELECT $select FROM `$table`";
|
$sql = "SELECT $select FROM `$table`";
|
||||||
@@ -94,33 +188,32 @@ function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ..
|
|||||||
$stmt = $conn->prepare($sql);
|
$stmt = $conn->prepare($sql);
|
||||||
if (!$stmt) die("Fehler beim Prepare: " . $conn->error);
|
if (!$stmt) die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
|
||||||
if (!$stmt->execute($values)) die("Fehler beim Execute: " . $stmt->error);
|
if (!empty($values)) {
|
||||||
|
$stmt->execute($values);
|
||||||
|
} else {
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
$result = $stmt->get_result();
|
$result = $stmt->get_result();
|
||||||
if (!$result) die("Fehler beim get_result: " . $stmt->error);
|
if (!$result) die("Fehler beim get_result: " . $conn->error);
|
||||||
|
|
||||||
$columnsResult = [];
|
|
||||||
while ($fieldinfo = $result->fetch_field()) {
|
|
||||||
$columnsResult[] = $fieldinfo->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
|
||||||
foreach ($rows as &$row) {
|
foreach ($rows as &$row) {
|
||||||
foreach ($fkMap as $fkColumn => $fkInfo) {
|
foreach ($fkMap as $fkColumn => $fkInfo) {
|
||||||
if (isset($row[$fkColumn]) && $row[$fkColumn] !== null) {
|
|
||||||
$fkStmt = $conn->prepare(
|
|
||||||
"SELECT {$fkInfo['value']} FROM `{$fkInfo['table']}` WHERE {$fkInfo['key']} = ?"
|
|
||||||
);
|
|
||||||
if (!$fkStmt) die("Fehler beim Prepare FK: " . $conn->error);
|
|
||||||
|
|
||||||
$fkStmt->bind_param("i", $row[$fkColumn]);
|
if (!isset($row[$fkColumn]) || $row[$fkColumn] === null) {
|
||||||
$fkStmt->execute();
|
continue;
|
||||||
$fkResult = $fkStmt->get_result();
|
|
||||||
$fkRow = $fkResult->fetch_assoc();
|
|
||||||
|
|
||||||
$row[$fkColumn] = $fkRow ? $fkRow[$fkInfo['value']] : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$row[$fkColumn] = getFKValue(
|
||||||
|
$conn,
|
||||||
|
$fkInfo['table'],
|
||||||
|
$fkInfo['key'],
|
||||||
|
$fkInfo['value'],
|
||||||
|
$row[$fkColumn],
|
||||||
|
$cache
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +257,7 @@ $Berufsschulberichte = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
$Stundenplan = [
|
$Stundenplan = [
|
||||||
"MO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
"MO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||||
"DI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
"DI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||||
"MI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
"MI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||||
"DO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
"DO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||||
@@ -172,6 +265,6 @@ $Stundenplan = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
$Berechtigungen = [
|
$Berechtigungen = [
|
||||||
"BENUTZER_ID" => ["table" => "Benutzer", "key" => "ID", "value" => "ID"],
|
"BENUTZER_ID" => ["table" => "Benutzer", "key" => "ID", "value" => "NAME"],
|
||||||
"BERECHTIGUNGEN_ID" => ["table" => "Berechtigungen", "key" => "ID", "value" => "ID"]
|
"SEITEN_ID" => ["table" => "Backend_Seiten", "key" => "ID", "value" => "DATEINAME"]
|
||||||
];
|
];
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
trait ReponseFormatter {
|
||||||
|
/**
|
||||||
|
* json pretty print method
|
||||||
|
*
|
||||||
|
* @param string $status
|
||||||
|
* @param mixed $data
|
||||||
|
* @param string $field
|
||||||
|
* @param string $extra
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function formatJson($status, $data, $field = 'extra', $extra = '') {
|
||||||
|
try {
|
||||||
|
return json_encode(
|
||||||
|
[
|
||||||
|
"status" => $status,
|
||||||
|
"data" => $data,
|
||||||
|
$field => $extra
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||||
|
);
|
||||||
|
} catch (Exception $error) {
|
||||||
|
return json_encode(
|
||||||
|
[
|
||||||
|
"status" => "error",
|
||||||
|
"data" => $error->getMessage(),
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once __DIR__ . '/databaseMethods.php';
|
||||||
|
include_once __DIR__ . '/../classes/getData.php';
|
||||||
|
|
||||||
|
function sendMail($subject, $message, $url, bool $log) {
|
||||||
|
$headers = "From: bruno.eckhardt07@gmail.com\r\n";
|
||||||
|
mail(
|
||||||
|
"bruno.eckhardt07@gmail.com",
|
||||||
|
$subject,
|
||||||
|
$message,
|
||||||
|
$headers
|
||||||
|
);
|
||||||
|
if ($log == true) {
|
||||||
|
DatabaseInsert(
|
||||||
|
"Mailing",
|
||||||
|
"DATUM, SUBJECT, MESSAGE, URL, CLIENT_IP",
|
||||||
|
(new getSpecificData())->getRequestTime(),
|
||||||
|
$subject,
|
||||||
|
$message,
|
||||||
|
$url,
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorMail($title, $error, bool $log) {
|
||||||
|
$headers = "From: bruno.eckhardt07@gmail.com\r\n";
|
||||||
|
mail(
|
||||||
|
"bruno.eckhardt07@gmail.com",
|
||||||
|
"ERROR: $title",
|
||||||
|
$error,
|
||||||
|
$headers,
|
||||||
|
);
|
||||||
|
if ($log == true) {
|
||||||
|
DatabaseInsert(
|
||||||
|
"Mailing",
|
||||||
|
"DATUM, SUBJECT, MESSAGE, URL, CLIENT_IP",
|
||||||
|
(new getSpecificData())->getRequestTime(),
|
||||||
|
"ERROR: $title",
|
||||||
|
$error,
|
||||||
|
(new getSpecificData())->getClientIP(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
function logging() {
|
||||||
|
include_once( __DIR__ . '/../classes/getData.php');
|
||||||
|
include_once( __DIR__ . '/../database/databaseConnect.php');
|
||||||
|
include_once( __DIR__ . '/../utils/databaseMethods.php');
|
||||||
|
include_once( __DIR__ . '/../utils/mailer.php');
|
||||||
|
|
||||||
|
if ((new getSpecificData())->getLoggingStatus("GENERAL_BACKEND_LOGGING")[0]['WERT'] == 'ON') {
|
||||||
|
|
||||||
|
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$ClientIP = (new getSpecificData())->getClientIP();
|
||||||
|
$UserAgent = (new getSpecificData())->getUserAgent();
|
||||||
|
$RequestURL = (new getSpecificData())->getRequestURL();
|
||||||
|
$USERCOOKIE = (new getSpecificData())->getCookie("USER");
|
||||||
|
$SessionID = (new getSpecificData())->getSessionID();
|
||||||
|
$REQUESTTIME = (new getSpecificData())->getRequestTime();
|
||||||
|
$QUERY = (new getSpecificData())->getQuery();
|
||||||
|
$LAND = (new getSpecificData())->getGeoIP($ClientIP);
|
||||||
|
$Whitelisted = (new getSpecificData())->getWhitelistedIPs();
|
||||||
|
$node = (new getSpecificData())->getServerNode((new getSpecificData())->getServerIP());
|
||||||
|
$username = $_SESSION['username'] ?? 'Unbekannt';
|
||||||
|
$website = (new getSpecificData())->getWebsite();
|
||||||
|
|
||||||
|
if ((new getSpecificData())->getLoggingStatus("DEV_BACKEND_LOGGING")[0]['WERT'] == 'OFF') {
|
||||||
|
if (in_array($ClientIP, $Whitelisted)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db_conn = db_connect();
|
||||||
|
|
||||||
|
$stmt = $db_conn->prepare("SELECT * FROM logs WHERE
|
||||||
|
`CLIENT_IP` LIKE ? and
|
||||||
|
`REQUEST_URL` = ? and
|
||||||
|
`REQUEST_URL` <> '/favicon.ico'
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmt->bind_param("ss", $ClientIP, $RequestURL);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->store_result();
|
||||||
|
|
||||||
|
if ($RequestURL !== '/favicon.ico') {
|
||||||
|
if ($stmt->num_rows > 0) { //wenn mindestens eine zeile vorhanden ist für die client ip dann führe updates durch, wenn nicht mache neue zeile
|
||||||
|
$updateCounter = $db_conn->prepare("
|
||||||
|
UPDATE logs
|
||||||
|
SET COUNTER = COUNTER + 1
|
||||||
|
WHERE `CLIENT_IP` = ?
|
||||||
|
AND `REQUEST_URL` = ?
|
||||||
|
");
|
||||||
|
$updateCounter->bind_param("ss", $ClientIP, $RequestURL);
|
||||||
|
$updateCounter->execute();
|
||||||
|
|
||||||
|
$updateLastRequest = $db_conn->prepare("
|
||||||
|
UPDATE logs
|
||||||
|
SET `LAST_REQUEST` = ?
|
||||||
|
WHERE `CLIENT_IP` = ?
|
||||||
|
AND `REQUEST_URL` = ?
|
||||||
|
");
|
||||||
|
$updateLastRequest->bind_param("sss", $REQUESTTIME, $ClientIP, $RequestURL);
|
||||||
|
$updateLastRequest->execute();
|
||||||
|
|
||||||
|
$updateCookieValue = $db_conn->prepare("
|
||||||
|
UPDATE logs
|
||||||
|
SET `USER_COOKIE_VALUE` = ?
|
||||||
|
WHERE `CLIENT_IP` = ?
|
||||||
|
AND `REQUEST_URL` = ?
|
||||||
|
");
|
||||||
|
$updateCookieValue->bind_param("sss", $USERCOOKIE, $ClientIP, $RequestURL);
|
||||||
|
$updateCookieValue->execute();
|
||||||
|
} else {
|
||||||
|
DatabaseInsert(
|
||||||
|
"logs",
|
||||||
|
"CLIENT_IP, LAND, SERVER_IP, USER_AGENT, REQUEST_URL, USER_COOKIE_VALUE, USER_SESSION_VALUE, FIRST_REQUEST, QUERY, WEBSITE, USER",
|
||||||
|
$ClientIP,
|
||||||
|
$LAND,
|
||||||
|
$node,
|
||||||
|
$UserAgent,
|
||||||
|
$RequestURL,
|
||||||
|
$USERCOOKIE,
|
||||||
|
$SessionID,
|
||||||
|
$REQUESTTIME,
|
||||||
|
$QUERY,
|
||||||
|
$website,
|
||||||
|
$username
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (mysqli_sql_exception $e) {
|
||||||
|
errorMail(
|
||||||
|
"Logging failed",
|
||||||
|
$e->getMessage(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
function redirectToWebsite(string $website): void
|
|
||||||
{
|
function redirectToWebsite(string $website) {
|
||||||
$website = trim($website);
|
$website = trim($website);
|
||||||
|
|
||||||
if ($website === '') {
|
if ($website === '') {
|
||||||
+119
-3
@@ -4,6 +4,7 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
@@ -17,14 +18,29 @@ header {
|
|||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
height: calc(100vh - 60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-header {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
padding: 15px 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 200px;
|
width: 240px;
|
||||||
background-color: #f4f4f4;
|
background-color: #f4f4f4;
|
||||||
border-right: 1px solid #ccc;
|
border-right: 1px solid #ccc;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar button {
|
.sidebar button {
|
||||||
@@ -46,7 +62,10 @@ header {
|
|||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
padding: 15px;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark Mode Styles */
|
/* Dark Mode Styles */
|
||||||
@@ -955,3 +974,100 @@ body.dark-mode .info-key {
|
|||||||
body.dark-mode .info-value {
|
body.dark-mode .info-value {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Datenbank Struktur Tabelle ===== */
|
||||||
|
|
||||||
|
.db-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
table-layout: fixed;
|
||||||
|
/* sorgt für feste Spaltenbreite */
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-table thead {
|
||||||
|
background-color: var(--btn-bg);
|
||||||
|
color: var(--btn-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-table th,
|
||||||
|
.db-table td {
|
||||||
|
padding: 12px 15px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--card-border);
|
||||||
|
|
||||||
|
/* Wichtig für gleiche Größe */
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-table tr:hover {
|
||||||
|
background-color: rgba(0, 123, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alternierende Zeilen */
|
||||||
|
.db-table tr:nth-child(even) {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark Mode */
|
||||||
|
body.dark-mode .db-table thead {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-mode .db-table tr:hover {
|
||||||
|
background-color: rgba(0, 123, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-mode .db-table tr:nth-child(even) {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar,
|
||||||
|
.content::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-thumb,
|
||||||
|
.content::-webkit-scrollbar-thumb {
|
||||||
|
background: #888;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-thumb:hover,
|
||||||
|
.content::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-mode .sidebar::-webkit-scrollbar-thumb,
|
||||||
|
body.dark-mode .content::-webkit-scrollbar-thumb {
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: schöner Dropdown Pfeil */
|
||||||
|
select {
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg fill='%23666' height='20' viewBox='0 0 20 20' width='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7l5 5 5-5z'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
background-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark Mode Pfeil */
|
||||||
|
body.dark-mode select {
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg fill='%23ccc' height='20' viewBox='0 0 20 20' width='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7l5 5 5-5z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MVC Model Generator
|
||||||
|
* Nutzung: php create_model.php <TabellenName> <Spalte1,Spalte2,...>
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (php_sapi_name() !== 'cli') {
|
||||||
|
die("Dieses Script muss über die CLI ausgeführt werden.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$tableName = $argv[1] ?? null;
|
||||||
|
$columnsInput = $argv[2] ?? null;
|
||||||
|
|
||||||
|
// Interaktive Abfrage, falls Argumente fehlen
|
||||||
|
if (!$tableName) {
|
||||||
|
echo "Tabellenname eingeben (z.B. Benutzer): ";
|
||||||
|
$handle = fopen("php://stdin", "r");
|
||||||
|
$tableName = trim(fgets($handle));
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$columnsInput) {
|
||||||
|
echo "Spalten kommagetrennt eingeben (z.B. NAME,EMAIL,ROLLE): ";
|
||||||
|
$handle = fopen("php://stdin", "r");
|
||||||
|
$columnsInput = trim(fgets($handle));
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$tableName) die("Fehler: Kein Tabellenname angegeben.\n");
|
||||||
|
|
||||||
|
// Namen formatieren
|
||||||
|
$className = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($tableName))));
|
||||||
|
$modelFile = __DIR__ . "/WEBROOT/src/MVC/Model/{$className}.php";
|
||||||
|
|
||||||
|
// Spalten aufbereiten
|
||||||
|
$columns = array_map('trim', explode(',', $columnsInput));
|
||||||
|
$columnList = implode("', '", $columns);
|
||||||
|
|
||||||
|
// Model-Inhalt generieren
|
||||||
|
$content = <<<PHP
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Model;
|
||||||
|
|
||||||
|
use MVC\Core\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model für die Tabelle '{$tableName}'
|
||||||
|
* Verfügbare Spalten: '{$columnList}'
|
||||||
|
*/
|
||||||
|
class {$className} extends Model {
|
||||||
|
protected static \$table = "{$tableName}";
|
||||||
|
|
||||||
|
// Hier kannst du spezifische Methoden für dieses Model hinzufügen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beispiel: Suche nach einer spezifischen Spalte
|
||||||
|
*/
|
||||||
|
public static function getByFirstColumn(\$value) {
|
||||||
|
return self::where("{$columns[0]} = ?", \$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
// Datei schreiben
|
||||||
|
if (!is_dir(dirname($modelFile))) {
|
||||||
|
mkdir(dirname($modelFile), 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($modelFile)) {
|
||||||
|
file_put_contents($modelFile, $content);
|
||||||
|
echo "✅ Model erstellt: WEBROOT/src/MVC/Model/{$className}.php\n";
|
||||||
|
echo "--- INFO ---\n";
|
||||||
|
echo "Tabelle: {$tableName}\n";
|
||||||
|
echo "Klasse: MVC\\Model\\{$className}\n";
|
||||||
|
echo "Spalten: " . implode(', ', $columns) . "\n";
|
||||||
|
} else {
|
||||||
|
echo "⚠️ Model {$className}.php existiert bereits.\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MVC Module Generator for Gemini CLI Project
|
||||||
|
* Nutzung: php create_module.php <name>
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (php_sapi_name() !== 'cli') {
|
||||||
|
die("Dieses Script kann nur über die Kommandozeile ausgeführt werden.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $argv[1] ?? null;
|
||||||
|
|
||||||
|
if (!$name) {
|
||||||
|
echo "Bitte gib einen Namen für das Modul an (z.B. mein_projekt): ";
|
||||||
|
$handle = fopen("php://stdin", "r");
|
||||||
|
$name = trim(fgets($handle));
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$name) {
|
||||||
|
die("Fehler: Kein Name angegeben.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namen formatieren
|
||||||
|
$key = strtolower(str_replace(' ', '_', $name));
|
||||||
|
$className = str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
|
||||||
|
$controllerFile = __DIR__ . "/WEBROOT/src/MVC/Controller/{$className}Controller.php";
|
||||||
|
$viewFile = __DIR__ . "/WEBROOT/src/MVC/View/{$key}.php";
|
||||||
|
|
||||||
|
// 1. Controller erstellen
|
||||||
|
$controllerContent = <<<PHP
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Controller;
|
||||||
|
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
// use MVC\Model\YourModel; // Importiere dein Model
|
||||||
|
|
||||||
|
class {$className}Controller extends Controller {
|
||||||
|
public function {$key}() {
|
||||||
|
// Nutze Models für den Datenbankzugriff
|
||||||
|
// \$data = YourModel::all();
|
||||||
|
|
||||||
|
return \$this->render('{$key}', [
|
||||||
|
'title' => '{$className} Modul',
|
||||||
|
'message' => 'Willkommen in deinem neuen Modul {$className}!'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
if (!file_exists($controllerFile)) {
|
||||||
|
file_put_contents($controllerFile, $controllerContent);
|
||||||
|
echo "✅ Controller erstellt: WEBROOT/src/MVC/Controller/{$className}Controller.php\n";
|
||||||
|
} else {
|
||||||
|
echo "⚠️ Controller existiert bereits.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. View erstellen
|
||||||
|
$viewContent = <<<HTML
|
||||||
|
<h2><?= htmlspecialchars(\$title) ?></h2>
|
||||||
|
<div class="card">
|
||||||
|
<p><?= htmlspecialchars(\$message) ?></p>
|
||||||
|
<p>Hier kannst du nun dein Projekt aufbauen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
if (!file_exists($viewFile)) {
|
||||||
|
file_put_contents($viewFile, $viewContent);
|
||||||
|
echo "✅ View erstellt: WEBROOT/src/MVC/View/{$key}.php\n";
|
||||||
|
} else {
|
||||||
|
echo "⚠️ View existiert bereits.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Anleitung ausgeben
|
||||||
|
echo "\n--- NÄCHSTE SCHRITTE ---\n";
|
||||||
|
echo "1. Registriere das Modul in WEBROOT/index.php beim Router:\n";
|
||||||
|
echo " \$router->add('{$key}', \\MVC\\Controller\\{$className}Controller::class, '{$key}', ['auth']);\n\n";
|
||||||
|
echo "2. Füge den Service in der Seitenverwaltung (Admin) oder in config.php hinzu:\n";
|
||||||
|
echo " ID: {$key}\n";
|
||||||
|
echo " Name: " . ucfirst($name) . "\n";
|
||||||
|
echo "------------------------\n";
|
||||||
+828
@@ -0,0 +1,828 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Ultimate MVC Project Bootstrapper (Fixed Version with Header)
|
||||||
|
* Erstellt ein voll funktionsfähiges Projekt inkl. Dev-Server, Header, DarkMode & freiem Zugriff.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (php_sapi_name() !== 'cli') {
|
||||||
|
die("Dieses Script muss über die CLI ausgeführt werden.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectName = $argv[1] ?? null;
|
||||||
|
if (!$projectName) {
|
||||||
|
echo "Bitte gib einen Projektnamen an: ";
|
||||||
|
$handle = fopen("php://stdin", "r");
|
||||||
|
$projectName = trim(fgets($handle));
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$projectName) die("Fehler: Kein Name angegeben.\n");
|
||||||
|
|
||||||
|
$baseDir = getcwd() . DIRECTORY_SEPARATOR . $projectName;
|
||||||
|
|
||||||
|
// 1. Verzeichnisstruktur erstellen
|
||||||
|
$dirs = [
|
||||||
|
'',
|
||||||
|
'WEBROOT',
|
||||||
|
'WEBROOT/api',
|
||||||
|
'WEBROOT/api/v1',
|
||||||
|
'WEBROOT/commands',
|
||||||
|
'WEBROOT/static',
|
||||||
|
'WEBROOT/src',
|
||||||
|
'WEBROOT/src/config',
|
||||||
|
'WEBROOT/src/database',
|
||||||
|
'WEBROOT/src/utils',
|
||||||
|
'WEBROOT/src/MVC',
|
||||||
|
'WEBROOT/src/MVC/Core',
|
||||||
|
'WEBROOT/src/MVC/Controller',
|
||||||
|
'WEBROOT/src/MVC/Model',
|
||||||
|
'WEBROOT/src/MVC/View',
|
||||||
|
'WEBROOT/src/MVC/Layout',
|
||||||
|
'WEBROOT/src/MVC/Layout/Templates',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
$path = $baseDir . ($dir ? DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $dir) : '');
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
mkdir($path, 0755, true);
|
||||||
|
echo "📁 Ordner erstellt: $dir\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = [];
|
||||||
|
|
||||||
|
// --- DEV SERVER: server.php ---
|
||||||
|
$files['server.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
$port = 8000;
|
||||||
|
$host = 'localhost';
|
||||||
|
$root = __DIR__ . '/WEBROOT';
|
||||||
|
echo "\n🚀 Starte MVC Dev-Server auf http://$host:$port\n";
|
||||||
|
echo "Drücke STRG+C zum Beenden.\n\n";
|
||||||
|
passthru("php -S $host:$port -t \"$root\"");
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['.htaccess'] = <<<'HTACCESS'
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteRule ^(Commands|config_backups|controller|modules)($|/.*) - [F,L,NC]
|
||||||
|
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||||
|
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
|
||||||
|
|
||||||
|
# -----------------------------------
|
||||||
|
# .php entfernen
|
||||||
|
# -----------------------------------
|
||||||
|
|
||||||
|
# browser requestet datei.php -> redirect zu /datei
|
||||||
|
RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC]
|
||||||
|
RewriteRule ^ /%1 [R=301,L]
|
||||||
|
|
||||||
|
# /datei -> datei.php
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
|
||||||
|
RewriteRule ^(.+)$ $1.php [L]
|
||||||
|
HTACCESS;
|
||||||
|
|
||||||
|
$files[$projectName . '.code-workspace'] = <<<VSCODE
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"workbench.colorTheme": "Default Dark Modern",
|
||||||
|
"workbench.colorCustomizations": {
|
||||||
|
"activityBar.background": "#ff000075",
|
||||||
|
"activityBar.foreground": "#000000",
|
||||||
|
"breadcrumb.foreground": "#FFFFFF",
|
||||||
|
},
|
||||||
|
"files.exclude": {
|
||||||
|
"*.git*": true,
|
||||||
|
"WEBROOT/config_backups": true,
|
||||||
|
"WEBROOT/static": true,
|
||||||
|
"WEBROOT/templates": true,
|
||||||
|
},
|
||||||
|
"editor.renderWhitespace": "all",
|
||||||
|
"editor.smoothScrolling": true,
|
||||||
|
"editor.fontSize": 14,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.minimap.enabled": true,
|
||||||
|
"editor.minimap.scale": 2,
|
||||||
|
"editor.minimap.side": "right",
|
||||||
|
"editor.minimap.maxColumn": 100,
|
||||||
|
"editor.minimap.renderCharacters": true,
|
||||||
|
"editor.minimap.showSlider": "always",
|
||||||
|
"files.autoSave": "off",
|
||||||
|
"files.autoSaveDelay": 3000,
|
||||||
|
"window.title": "BACKEND",
|
||||||
|
"workbench.editor.wrapTabs": true,
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"recommendations": [
|
||||||
|
"oderwat.indent-rainbow",
|
||||||
|
"pranaygp.vscode-css-peek",
|
||||||
|
"wayou.vscode-todo-highlight",
|
||||||
|
"mrmlnc.vscode-mustache",
|
||||||
|
"fabiospampinato.vscode-highlight",
|
||||||
|
"junstyle.php-cs-fixer",
|
||||||
|
"mtea.gettext-editor",
|
||||||
|
"neilbrayfield.php-docblocker",
|
||||||
|
"bmewburn.vscode-intelephense-client",
|
||||||
|
"mohamed-ali.html-css-class-completion",
|
||||||
|
"rifi2k.format-html-in-php",
|
||||||
|
"mrorz.language-gettext",
|
||||||
|
"voldemortensen.rainbow-tags",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VSCODE;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/utils/databaseMethods.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function DatabaseCreateTable($table, $columnsDefinition) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS `$table` ($columnsDefinition)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseShowTables() {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$tablesResult = $conn->query("SHOW TABLES");
|
||||||
|
if (!$tablesResult) {
|
||||||
|
die("Fehler beim Laden der Tabellen: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tables = [];
|
||||||
|
|
||||||
|
while ($tableRow = $tablesResult->fetch_array()) {
|
||||||
|
$tables[] = $tableRow[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseShowTableStructure($table) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$stmt = $conn->prepare("SHOW COLUMNS FROM `$table`");
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
$columns = [];
|
||||||
|
|
||||||
|
while ($column = $result->fetch_assoc()) {
|
||||||
|
$columns[] = [
|
||||||
|
"Field" => $column["Field"],
|
||||||
|
"Type" => $column["Type"],
|
||||||
|
"Null" => $column["Null"],
|
||||||
|
"Key" => $column["Key"],
|
||||||
|
"Default" => $column["Default"],
|
||||||
|
"Extra" => $column["Extra"]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseShow($table, $columns = "*", $where = "", ...$values) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$sql = "SELECT $columns FROM `$table`";
|
||||||
|
|
||||||
|
if (!empty($where)) {
|
||||||
|
$sql .= " WHERE $where";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute($values)) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
return $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function DatabaseInsert($table, $columns, ...$values) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$placeholders = rtrim(str_repeat("?,", count($values)), ",");
|
||||||
|
$sql = "INSERT INTO `$table` ($columns) VALUES ($placeholders)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute($values)) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseDelete($table, $where, ...$values) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$sql = "DELETE FROM `$table` WHERE $where";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute($values)) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseUpdate($table, $set, $where, ...$values) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$sql = "UPDATE `$table` SET $set WHERE $where";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$stmt->execute($values)) {
|
||||||
|
die("Fehler beim Execute: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFKValue($conn, $table, $key, $value, $id, &$cache) {
|
||||||
|
if ($id === null) return null;
|
||||||
|
|
||||||
|
$cacheKey = $table . "_" . $id;
|
||||||
|
|
||||||
|
if (isset($cache[$cacheKey])) {
|
||||||
|
return $cache[$cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $conn->prepare("SELECT `$value` FROM `$table` WHERE `$key` = ? LIMIT 1");
|
||||||
|
if (!$stmt) {
|
||||||
|
die("FK Prepare Fehler: " . $conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_param("i", $id);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$result = $stmt->get_result()->fetch_assoc();
|
||||||
|
$valueResult = $result[$value] ?? null;
|
||||||
|
|
||||||
|
$cache[$cacheKey] = $valueResult;
|
||||||
|
|
||||||
|
return $valueResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ...$values) {
|
||||||
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||||
|
$conn = db_connect();
|
||||||
|
|
||||||
|
$cache = [];
|
||||||
|
|
||||||
|
$select = is_array($columns) ? implode(", ", $columns) : $columns;
|
||||||
|
|
||||||
|
$sql = "SELECT $select FROM `$table`";
|
||||||
|
if (!empty($where)) {
|
||||||
|
$sql .= " WHERE $where";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
if (!$stmt) die("Fehler beim Prepare: " . $conn->error);
|
||||||
|
|
||||||
|
if (!empty($values)) {
|
||||||
|
$stmt->execute($values);
|
||||||
|
} else {
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
if (!$result) die("Fehler beim get_result: " . $conn->error);
|
||||||
|
|
||||||
|
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
|
||||||
|
foreach ($rows as &$row) {
|
||||||
|
foreach ($fkMap as $fkColumn => $fkInfo) {
|
||||||
|
|
||||||
|
if (!isset($row[$fkColumn]) || $row[$fkColumn] === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row[$fkColumn] = getFKValue(
|
||||||
|
$conn,
|
||||||
|
$fkInfo['table'],
|
||||||
|
$fkInfo['key'],
|
||||||
|
$fkInfo['value'],
|
||||||
|
$row[$fkColumn],
|
||||||
|
$cache
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
"rows" => $rows
|
||||||
|
];
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
// --- CONFIG: env.ini ---
|
||||||
|
$files['WEBROOT/src/config/env.ini'] = <<<INI
|
||||||
|
[database]
|
||||||
|
APP_ENV = "dev"
|
||||||
|
DB_DEV_HOST = "localhost"
|
||||||
|
DB_DEV_USERNAME = "root"
|
||||||
|
DB_DEV_PASSWORD = ""
|
||||||
|
DB_DEV_DATABASE = "mein_projekt"
|
||||||
|
INI;
|
||||||
|
|
||||||
|
// --- DB: databaseConnect.php ---
|
||||||
|
$files['WEBROOT/src/database/databaseConnect.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
function db_connect() {
|
||||||
|
$config = parse_ini_file(__DIR__ . '/../config/env.ini');
|
||||||
|
$env = strtoupper($config['APP_ENV']);
|
||||||
|
try {
|
||||||
|
$conn = new mysqli($config["DB_{$env}_HOST"], $config["DB_{$env}_USERNAME"], $config["DB_{$env}_PASSWORD"], $config["DB_{$env}_DATABASE"]);
|
||||||
|
$conn->set_charset("utf8mb4");
|
||||||
|
return $conn;
|
||||||
|
} catch (Exception $e) { return null; }
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
// --- CORE ---
|
||||||
|
$files['WEBROOT/src/MVC/Core/Controller.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
protected $view;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->view = new View(__DIR__ . '/../View');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render($template, $data = []) {
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->view->assign($key, $value);
|
||||||
|
}
|
||||||
|
return $this->view->render($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function json($data, $status = 200) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
http_response_code($status);
|
||||||
|
echo json_encode($data);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/MVC/Core/Model.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../utils/databaseMethods.php';
|
||||||
|
|
||||||
|
abstract class Model {
|
||||||
|
protected static $table;
|
||||||
|
protected static $conn;
|
||||||
|
|
||||||
|
protected static function getConnection() {
|
||||||
|
if (!self::$conn) {
|
||||||
|
self::$conn = \db_connect();
|
||||||
|
}
|
||||||
|
return self::$conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function all() {
|
||||||
|
return \DatabaseShow(static::$table, "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function find($id, $column = 'ID') {
|
||||||
|
$result = \DatabaseShow(static::$table, "*", "$column = ?", $id);
|
||||||
|
return $result[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function where($where, ...$params) {
|
||||||
|
return \DatabaseShow(static::$table, "*", $where, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function insert($data) {
|
||||||
|
$columns = implode(", ", array_keys($data));
|
||||||
|
$values = array_values($data);
|
||||||
|
return \DatabaseInsert(static::$table, $columns, ...$values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function update($data, $where, ...$whereParams) {
|
||||||
|
$sets = [];
|
||||||
|
$values = [];
|
||||||
|
foreach ($data as $column => $value) {
|
||||||
|
$sets[] = "$column = ?";
|
||||||
|
$values[] = $value;
|
||||||
|
}
|
||||||
|
$setString = implode(", ", $sets);
|
||||||
|
return \DatabaseUpdate(static::$table, $setString, $where, ...array_merge($values, $whereParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function delete($where, ...$params) {
|
||||||
|
return \DatabaseDelete(static::$table, $where, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function query($sql, ...$params) {
|
||||||
|
$conn = self::getConnection();
|
||||||
|
if (empty($params)) {
|
||||||
|
return $conn->query($sql);
|
||||||
|
}
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$types = str_repeat('s', count($params));
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->get_result();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/MVC/Core/View.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
class View {
|
||||||
|
protected $data = [];
|
||||||
|
protected $viewPath;
|
||||||
|
|
||||||
|
public function __construct($viewPath) {
|
||||||
|
$this->viewPath = $viewPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assign($key, $value) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($template) {
|
||||||
|
extract($this->data);
|
||||||
|
$file = $this->viewPath . '/' . $template . '.php';
|
||||||
|
if (file_exists($file)) {
|
||||||
|
ob_start();
|
||||||
|
include $file;
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
return "View $template not found at $file";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/MVC/Core/Router.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MVC\Core;
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
protected $routes = [];
|
||||||
|
protected $middlewares = [];
|
||||||
|
|
||||||
|
public function add($service, $controller, $method, $middlewares = []) {
|
||||||
|
$this->routes[$service] = [
|
||||||
|
'controller' => $controller,
|
||||||
|
'method' => $method,
|
||||||
|
'middlewares' => $middlewares
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatch($service) {
|
||||||
|
if (!isset($this->routes[$service])) {
|
||||||
|
throw new \Exception("Service '$service' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$route = $this->routes[$service];
|
||||||
|
|
||||||
|
// Run Middlewares
|
||||||
|
foreach ($route['middlewares'] as $middleware) {
|
||||||
|
$this->runMiddleware($middleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
$controllerClass = $route['controller'];
|
||||||
|
$method = $route['method'];
|
||||||
|
|
||||||
|
if (!class_exists($controllerClass)) {
|
||||||
|
throw new \Exception("Controller class '$controllerClass' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$controller = new $controllerClass();
|
||||||
|
|
||||||
|
if (!method_exists($controller, $method)) {
|
||||||
|
throw new \Exception("Method '$method' not found in controller '$controllerClass'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $controller->$method();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function runMiddleware($middleware) {
|
||||||
|
if ($middleware === 'admin') {
|
||||||
|
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||||
|
die("Access Denied: Admin rights required.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($middleware === 'auth') {
|
||||||
|
if (empty($_SESSION['user_logged_in'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/MVC/Layout/Layout.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
namespace MVC\Layout;
|
||||||
|
class Layout {
|
||||||
|
protected $blocks = [];
|
||||||
|
protected $template;
|
||||||
|
public function __construct($template = 'main') { $this->template = $template; }
|
||||||
|
public function setBlock($name, $content) { $this->blocks[$name] = $content; }
|
||||||
|
public function getBlock($name) { return $this->blocks[$name] ?? ''; }
|
||||||
|
public function render() {
|
||||||
|
$layoutFile = __DIR__ . '/Templates/' . $this->template . '.php';
|
||||||
|
if (file_exists($layoutFile)) { ob_start(); include $layoutFile; return ob_get_clean(); }
|
||||||
|
return "Layout template nicht gefunden.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/MVC/autoloader.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
$prefix = 'MVC\\';
|
||||||
|
$base_dir = __DIR__ . '/';
|
||||||
|
|
||||||
|
$len = strlen($prefix);
|
||||||
|
if (strncmp($prefix, $class, $len) !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relative_class = substr($class, $len);
|
||||||
|
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
||||||
|
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
// --- LAYOUT TEMPLATE ---
|
||||||
|
$files['WEBROOT/src/MVC/Layout/Templates/main.php'] = <<<'HTML'
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" class="<?= ($_SESSION['theme'] ?? 'light') === 'dark' ? 'dark-mode' : '' ?>">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title><?= $this->getBlock('title') ?></title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #ffffff;
|
||||||
|
--text: #333;
|
||||||
|
--sidebar: #f4f4f4;
|
||||||
|
--header: #007bff;
|
||||||
|
--accent: #007bff;
|
||||||
|
--border: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode {
|
||||||
|
--bg: #1a1a1a;
|
||||||
|
--text: #f4f4f4;
|
||||||
|
--sidebar: #252525;
|
||||||
|
--header: #111;
|
||||||
|
--accent: #3498db;
|
||||||
|
--border: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
transition: 0.3s;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-header {
|
||||||
|
height: 60px;
|
||||||
|
background: var(--header);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 260px;
|
||||||
|
background: var(--sidebar);
|
||||||
|
padding: 20px;
|
||||||
|
border-right: 1px solid var(--border);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 40px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-toggle {
|
||||||
|
padding: 8px 15px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-toggle:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
display: block;
|
||||||
|
padding: 12px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="main-header">
|
||||||
|
<div class="logo"><?= $this->getBlock('header_title') ?></div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<?= $this->getBlock('header_actions') ?>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="sidebar">
|
||||||
|
<?= $this->getBlock('sidebar') ?>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<?= $this->getBlock('content') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function toggleTheme() {
|
||||||
|
fetch('?service=toggle_theme').then(() => location.reload());
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
// --- CONTROLLERS ---
|
||||||
|
$files['WEBROOT/src/MVC/Controller/HomeController.php'] = <<<'PHP' <?php
|
||||||
|
namespace MVC\Controller;
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
class HomeController extends Controller {
|
||||||
|
public function index() { return $this->render('home'); }
|
||||||
|
public function toggle_theme() {
|
||||||
|
$_SESSION['theme'] = ($_SESSION['theme'] ?? 'light') === 'dark' ? 'light' : 'dark';
|
||||||
|
return $this->json(['success' => true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
$files['WEBROOT/src/MVC/Controller/TestController.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
namespace MVC\Controller;
|
||||||
|
use MVC\Core\Controller;
|
||||||
|
class TestController extends Controller {
|
||||||
|
public function service1() { return $this->render('test1', ['msg' => 'Service 1 ist bereit!']); }
|
||||||
|
public function service2() { return $this->render('test2', ['data' => ['Layout' => 'Header + Sidebar', 'Theme' => 'Auto-Toggle']]); }
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
// --- VIEWS ---
|
||||||
|
$files['WEBROOT/src/MVC/View/home.php'] = "<h1>Dashboard</h1><p>Willkommen in deinem neuen MVC-System mit integriertem Header.</p>";
|
||||||
|
$files['WEBROOT/src/MVC/View/test1.php'] = "<h1>Service 1</h1><p><?= \$msg ?></p>";
|
||||||
|
$files['WEBROOT/src/MVC/View/test2.php'] = "<h1>System Check</h1>
|
||||||
|
<ul><?php foreach(\$data as \$k => \$v): ?><li><b><?= \$k ?>:</b> <?= \$v ?></li><?php endforeach; ?></ul>";
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
$files['WEBROOT/api/v1/get.php'] = "<?php header('Content-Type: application/json'); echo json_encode(['status' => 'online']);";
|
||||||
|
|
||||||
|
// --- ENTRY POINT ---
|
||||||
|
$files['WEBROOT/index.php'] = <<<'PHP'
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/src/MVC/autoloader.php';
|
||||||
|
$service = $_GET['service'] ?? 'home';
|
||||||
|
try {
|
||||||
|
$router = new \MVC\Core\Router();
|
||||||
|
$router->add('home', \MVC\Controller\HomeController::class, 'index');
|
||||||
|
$router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme');
|
||||||
|
$router->add('test1', \MVC\Controller\TestController::class, 'service1');
|
||||||
|
$router->add('test2', \MVC\Controller\TestController::class, 'service2');
|
||||||
|
|
||||||
|
$layout = new \MVC\Layout\Layout('main');
|
||||||
|
$layout->setBlock('title', 'MVC Starter');
|
||||||
|
$layout->setBlock('header_title', '🚀 MVC Dashboard');
|
||||||
|
$layout->setBlock('header_actions', '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>');
|
||||||
|
|
||||||
|
// Sidebar Block
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<nav>
|
||||||
|
<a href="?service=home">🏠 Home</a>
|
||||||
|
<a href="?service=test1">🛠️ Test 1</a>
|
||||||
|
<a href="?service=test2">📊 Test 2</a>
|
||||||
|
</nav>
|
||||||
|
<?php
|
||||||
|
$layout->setBlock('sidebar', ob_get_clean());
|
||||||
|
|
||||||
|
$layout->setBlock('content', $router->dispatch($service));
|
||||||
|
echo $layout->render();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
http_response_code(404); echo "<h1>404</h1><p>" . $e->getMessage() . "</p>";
|
||||||
|
}
|
||||||
|
PHP;
|
||||||
|
|
||||||
|
// 3. Dateien schreiben
|
||||||
|
foreach ($files as $file => $content) {
|
||||||
|
$path = $baseDir . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $file);
|
||||||
|
file_put_contents($path, $content);
|
||||||
|
echo "📄 Datei erstellt: $file\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n✨ Projekt '$projectName' wurde mit HEADER initialisiert!\n";
|
||||||
|
echo "1. Starten: php server.php\n";
|
||||||
|
echo "2. Öffnen: http://localhost:8000\n";
|
||||||
Reference in new issue
Block a user