Compare commits

..
Author SHA1 Message Date
Bruno Eckhardt b2b4ac2124 new endpoint for workreports and moved reports to schoolreports 2026-05-19 15:13:53 +02:00
Bruno Eckhardt f7fd4ed183 request saves 2026-05-19 10:37:45 +02:00
Bruno Eckhardt a73b2ec13f fixes and new api entpoints for lessons and reports 2026-05-19 10:10:43 +02:00
Bruno Eckhardt dce0a0caed htaccess 2026-05-19 07:17:17 +02:00
Bruno Eckhardt 3d8c4291be fix error msg 2026-05-11 14:16:41 +02:00
Bruno Eckhardt a0afeabb27 maybe hotfix 2026-05-11 13:18:05 +02:00
Bruno Eckhardt 96c27a8722 changes 2026-05-11 13:14:16 +02:00
Bruno Eckhardt 4478f7e82f error logging 2026-05-11 13:09:04 +02:00
Bruno Eckhardt c1f3c3ea4e new function get berufsschulberichte 2026-05-11 12:54:37 +02:00
Bruno Eckhardt 2a8564d3bf caching for api 2026-05-11 12:53:15 +02:00
Tower 14f4143207 fix 2026-04-10 21:43:17 +02:00
Tower 53b4bd3423 fixes 2026-04-08 16:53:49 +02:00
Bruno Eckhardt 386bef9094 refresh fixes 2026-04-08 16:34:06 +02:00
Bruno Eckhardt 4e52aa468f fix: minus temperaturen 2026-03-27 06:50:19 +01:00
Bruno Eckhardt f4b65903f7 store error mails in Mailing Table 2026-03-26 08:10:57 +01:00
Bruno Eckhardt 29a1be5d22 enable and disable error logging in errorMailing 2026-03-26 07:52:59 +01:00
Bruno Eckhardt 51bc1f7876 home info htmlspecialchars and right fixes 2026-03-25 08:46:38 +01:00
Bruno Eckhardt 4e0fcfea3f dark and white mode for dropdown 2026-03-25 07:45:24 +01:00
Bruno Eckhardt 63373ac90c dropdown 2026-03-25 07:45:03 +01:00
Bruno Eckhardt 966ed2690c new: auf und abbau von stunden möglich 2026-03-25 07:28:36 +01:00
Bruno Eckhardt 51ce63b0f0 logging error mailing 2026-03-23 15:45:10 +01:00
Bruno Eckhardt d9f98176c5 login as guest 2026-03-23 10:35:46 +01:00
Bruno Eckhardt 2aa48a2601 fixes 2026-03-23 08:37:43 +01:00
Bruno Eckhardt 2bd315efcd feat: api error mailing 2026-03-23 08:18:04 +01:00
Bruno Eckhardt d862ed1102 login tester for mailing 2026-03-23 08:17:02 +01:00
Bruno Eckhardt 5cc4fd0d10 fix 2026-03-16 15:07:44 +01:00
Bruno Eckhardt 24cb1be31f just a test 2026-03-16 13:25:30 +01:00
Bruno Eckhardt 955744ee7e hotfix 2026-03-16 13:22:42 +01:00
Bruno Eckhardt 88f01fe156 update: error mailing 2026-03-16 13:14:32 +01:00
Tower 0c189487c6 more spelling fixes 2026-03-15 21:13:07 +01:00
Tower 50934501ab spelling fixes 2026-03-15 17:46:45 +01:00
Tower 54c77e4651 online bruno can change overtime 2026-03-15 17:41:47 +01:00
Tower 351ec88131 updated config 2026-03-15 17:41:23 +01:00
Tower 7ac6a3cd49 update 2026-03-15 17:41:09 +01:00
Tower 236fe1fd7f new user right system 2026-03-15 17:40:59 +01:00
Tower 4b30f00872 update: client ip logging when mail 2026-03-15 17:36:47 +01:00
Tower e0a4f9ff77 update 2026-03-15 12:32:19 +01:00
Tower ae5beb7ff3 new: print berichte 2026-03-15 12:31:12 +01:00
Bruno Eckhardt 614531394d test 2026-03-13 13:31:26 +01:00
Bruno Eckhardt 773d48f6dd fix: error mailing 2026-03-13 13:12:49 +01:00
Bruno Eckhardt b98c8f39d7 update: enable or disable logging when mail sending 2026-03-13 12:59:35 +01:00
Bruno Eckhardt c286ec3aa2 hopefully fixed use 2026-03-13 12:22:26 +01:00
Bruno Eckhardt 05086a12f5 update: error mailing when cannot connect to DB 2026-03-13 12:19:01 +01:00
Bruno Eckhardt 9f76387eec update: new rows and data 2026-03-13 12:18:40 +01:00
Bruno Eckhardt d2e076e683 fix: wrong var name 2026-03-13 11:30:13 +01:00
Bruno Eckhardt 80184ed2f0 fixes 2026-03-13 11:29:01 +01:00
Bruno Eckhardt 8e2270c674 hotfix 2026-03-13 11:01:13 +01:00
Bruno Eckhardt 53eb3d4bc2 feature: Mailing 2026-03-13 10:35:29 +01:00
Bruno Eckhardt 527e783c75 new scrollbar 2026-03-10 12:17:37 +01:00
Tower 6bcdf3f9e3 feature: scrollable services 2026-03-09 22:29:03 +01:00
Tower e58bfb6bb1 new: domain administration 2026-03-09 22:23:46 +01:00
Tower fc665213b3 new config 2026-03-09 22:06:51 +01:00
Tower d9ae978837 fix: weather sql query and remove htmlspecialchars 2026-03-09 22:02:22 +01:00
Tower 0559317875 update: new berichte 2026-03-09 22:01:54 +01:00
Tower 865320a75c deleting old things 2026-03-09 22:01:14 +01:00
Bruno Eckhardt 35dcb96e5d fix: database connections 2026-03-09 12:49:53 +01:00
Bruno 037396fec0 api hotfix logging; 2026-03-03 10:10:21 +01:00
Bruno d916632d1b new table service 2026-03-02 11:49:41 +01:00
Tower 182997b3fb hotfix and closoed groups 2026-03-01 20:14:49 +01:00
Tower 306e3b9c28 fixes and new api tester service 2026-03-01 19:37:27 +01:00
Tower 24a36b020d fix 2026-02-27 16:48:41 +01:00
Tower e3dae4c853 fix 2026-02-27 15:28:23 +01:00
Tower 66fd469c3a add htaccess but new dockerfile needed 2026-02-27 15:25:42 +01:00
Tower 0846dea367 update: api tests 2026-02-27 15:15:36 +01:00
Tower 2511b94616 test 2026-02-23 21:33:31 +01:00
31 changed files with 2459 additions and 379 deletions

No files matched your search

+2 -1
View File
@@ -1,2 +1,3 @@
WEBROOT/config_backups/*
WEBROOT/static/*
WEBROOT/static/*
WEBROOT/sitemap.xml
-73
View File
@@ -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
+19
View File
@@ -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]
+54
View File
@@ -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";
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
include_once( __DIR__ . '/../../controller/DatabaseController.php');
include_once( __DIR__ . '/../../controller/DataController.php');
include_once( __DIR__ . '/../../controller/ApiController.php');
include_once(__DIR__ . '/../../modules/Mail.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;
}
+113
View File
@@ -0,0 +1,113 @@
<?php
include_once( __DIR__ . '/../../controller/DatabaseController.php' );
include_once( __DIR__ . '/../../controller/DataController.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;
+37
View File
@@ -0,0 +1,37 @@
<?php
include_once(__DIR__ . '/../../controller/DatabaseController.php');
include_once(__DIR__ . '/../../controller/DataController.php');
include_once(__DIR__ . '/../../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
*/
+37
View File
@@ -0,0 +1,37 @@
<?php
include_once(__DIR__ . '/../../controller/DatabaseController.php');
include_once(__DIR__ . '/../../controller/DataController.php');
include_once(__DIR__ . '/../../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
*/
+36
View File
@@ -0,0 +1,36 @@
<?php
include_once(__DIR__ . '/../../controller/DatabaseController.php');
include_once(__DIR__ . '/../../controller/DataController.php');
include_once(__DIR__ . '/../../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
*/
+128 -14
View File
@@ -17,8 +17,18 @@ $serviceGroups = array (
array (
0 => 'home_info',
1 => 'links',
2 => 'berichte_arbeit',
3 => 'berichte_arbeit_gen',
),
),
'Ausbildung' =>
array (
'label' => 'Ausbildung',
'defaultOpen' => true,
'services' =>
array (
0 => 'print_static',
1 => 'zeiterfassung',
2 => 'berichte_arbeit_gen',
3 => 'berichte',
),
),
'Logs' =>
@@ -37,13 +47,25 @@ $serviceGroups = array (
'System' =>
array (
'label' => 'Verwaltung',
'defaultOpen' => true,
'defaultOpen' => false,
'services' =>
array (
0 => 'services_control',
1 => 'user_control',
2 => 'variablen_control',
3 => 'data_info',
0 => 'data_info',
1 => 'services_control',
2 => 'user_control',
3 => 'variablen_control',
),
),
'Test' =>
array (
'label' => 'Dev / Test',
'defaultOpen' => false,
'services' =>
array (
0 => 'api_tester',
1 => 'weather_chart',
2 => 'tabellen',
3 => 'domains',
),
),
);
@@ -55,7 +77,25 @@ $services = array (
'active' => true,
'adminOnly' => false,
),
'data_info' =>
'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' =>
array (
'name' => 'Data Info',
'active' => true,
@@ -63,7 +103,7 @@ $services = array (
),
'links' =>
array (
'name' => 'Externe Links',
'name' => 'Links',
'active' => true,
'adminOnly' => false,
),
@@ -95,7 +135,7 @@ $services = array (
array (
'name' => 'Persönliche Logs',
'active' => true,
'adminOnly' => false,
'adminOnly' => true,
),
'services_control' =>
array (
@@ -103,15 +143,15 @@ $services = array (
'active' => true,
'adminOnly' => true,
),
'berichte_arbeit' =>
'zeiterfassung' =>
array (
'name' => 'Arbeitsberichte',
'name' => 'Zeit Erfassung',
'active' => true,
'adminOnly' => false,
),
'berichte_arbeit_gen' =>
array (
'name' => 'Arbeitsberichte PDF',
'name' => 'Berichte PDF',
'active' => true,
'adminOnly' => false,
),
@@ -119,7 +159,7 @@ $services = array (
array (
'name' => 'Benutzer',
'active' => true,
'adminOnly' => false,
'adminOnly' => true,
),
'variablen_control' =>
array (
@@ -127,6 +167,24 @@ $services = array (
'active' => 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,
),
);
$linkGroups = array (
@@ -261,4 +319,60 @@ $linkButtons = array (
'adminOnly' => false,
'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',
),
);
+102
View File
@@ -0,0 +1,102 @@
<?php
class ApiController {
/**
* method for pretty jsonsuccess output
*
* @param array $data
* @return string
*/
public function jsonSuccess($data) {
return json_encode(
[
'status' => 'success',
'data' => $data,
],
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
);
}
/**
* method for pretty jsonerror output
*
* @param array $error
* @return string
*/
public function jsonError ($error) {
echo json_encode(
[
'status' => 'failed',
'data' => 'none',
'error' => $error
],
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
);
}
/**
* 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 = json_encode([
"error" => "Unauthorized",
"debug_received_token" => $token,
]);
echo $this->jsonError($error);
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"}',
);
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
class getSpecificData {
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();
curl_setopt($ch, CURLOPT_URL, $url);
+44 -17
View File
@@ -1,9 +1,13 @@
<?php
include_once __DIR__ . '/../modules/Mail.php';
include_once __DIR__ . '/../controller/RoutesController.php';
include_once 'DataController.php';
function db_connect() {
$servers = [
'dev' => [
'hostaddr' => '10.233.1.29',
'local' => [
'hostaddr' => '127.0.0.1',
'username' => 'worker',
'password' => 'bBHofurUJWKzPE8Z',
'database' => 'DEV',
@@ -14,35 +18,58 @@ function db_connect() {
'password' => 'bBHofurUJWKzPE8Z',
'database' => 'DEV',
],
'backup' => [
'hostaddr' => '10.233.1.241',
'username' => 'worker',
'password' => 'bBHofurUJWKzPE8Z',
'database' => 'DEV',
],
'dev' => [
'hostaddr' => '10.233.1.243',
'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',
'error' => [
'hostaddr' => '10.233.1.244',
'username' => 'error',
'password' => 'bBHofurUJWKzPE8Z',
'database' => 'DEV',
]
];
$enviroment = 'live';
$enviroment = 'vpn';
$conn = new mysqli(
$servers[$enviroment]['hostaddr'],
$servers[$enviroment]['username'],
$servers[$enviroment]['password'],
$servers[$enviroment]['database'],
);
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
if ($conn->connect_error) {
throw new Exception('Connection Error: ' . $conn->connect_error);
try {
$conn = new mysqli(
$servers[$enviroment]['hostaddr'],
$servers[$enviroment]['username'],
$servers[$enviroment]['password'],
$servers[$enviroment]['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;
}
$conn->set_charset("utf8mb4");
return $conn;
}
?>
+114 -21
View File
@@ -1,5 +1,72 @@
<?php
function DatabaseCreateTable($table, $columnsDefinition) {
include_once '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 '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 '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 'DatabaseConnect.php';
$conn = db_connect();
@@ -80,10 +147,37 @@ function DatabaseUpdate($table, $set, $where, ...$values) {
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 'DatabaseConnect.php';
$conn = db_connect();
$cache = [];
$select = is_array($columns) ? implode(", ", $columns) : $columns;
$sql = "SELECT $select FROM `$table`";
@@ -94,33 +188,32 @@ function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ..
$stmt = $conn->prepare($sql);
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();
if (!$result) die("Fehler beim get_result: " . $stmt->error);
$columnsResult = [];
while ($fieldinfo = $result->fetch_field()) {
$columnsResult[] = $fieldinfo->name;
}
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) {
$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]);
$fkStmt->execute();
$fkResult = $fkStmt->get_result();
$fkRow = $fkResult->fetch_assoc();
$row[$fkColumn] = $fkRow ? $fkRow[$fkInfo['value']] : null;
if (!isset($row[$fkColumn]) || $row[$fkColumn] === null) {
continue;
}
$row[$fkColumn] = getFKValue(
$conn,
$fkInfo['table'],
$fkInfo['key'],
$fkInfo['value'],
$row[$fkColumn],
$cache
);
}
}
@@ -164,7 +257,7 @@ $Berufsschulberichte = [
];
$Stundenplan = [
"MO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
"MO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
"DI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
"MI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
"DO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
@@ -172,6 +265,6 @@ $Stundenplan = [
];
$Berechtigungen = [
"BENUTZER_ID" => ["table" => "Benutzer", "key" => "ID", "value" => "ID"],
"BERECHTIGUNGEN_ID" => ["table" => "Berechtigungen", "key" => "ID", "value" => "ID"]
"BENUTZER_ID" => ["table" => "Benutzer", "key" => "ID", "value" => "NAME"],
"SEITEN_ID" => ["table" => "Backend_Seiten", "key" => "ID", "value" => "DATEINAME"]
];
+59 -47
View File
@@ -3,10 +3,15 @@
function logging() {
include_once 'DataController.php';
include_once 'DatabaseConnect.php';
include_once 'DatabaseController.php';
include_once __DIR__ . '/../modules/Mail.php';
if ((new getSpecificData())->getLoggingStatus("GENERAL_BACKEND_LOGGING")[0]['WERT'] == 'ON') {
include_once 'DatabaseConnect.php';
include_once 'DatabaseController.php';
include_once 'DataController.php';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$ClientIP = (new getSpecificData())->getClientIP();
$UserAgent = (new getSpecificData())->getUserAgent();
@@ -39,52 +44,59 @@ function logging() {
$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();
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
);
$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");
} catch (mysqli_sql_exception $e) {
errorMail(
"Logging failed",
$e->getMessage(),
true,
);
throw $e;
}
}
}
+14
View File
@@ -16,6 +16,20 @@ class UserController {
$admin,
$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) {
+46 -33
View File
@@ -1,11 +1,10 @@
<?php
ob_start();
// Vor allen includes, vor jeglicher Ausgabe
session_set_cookie_params([
'lifetime' => 0, // bis Browser geschlossen
'path' => '/', // Gültig für ganze Domain
'domain' => '', // aktuelle Domain
'secure' => false, // true nur für HTTPS
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
@@ -16,20 +15,35 @@ if (empty($_SESSION['user_logged_in'])) {
exit;
}
include 'config.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_once 'config.php';
include_once 'controller/CookieController.php';
include_once 'controller/DatabaseConnect.php';
include_once 'controller/DatabaseController.php';
include_once 'controller/DataController.php';
include_once 'controller/LogController.php';
include_once 'controller/UserController.php';
include_once 'modules/ConsoleLog.php';
// include 'controller/RoutesController.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;
// 1. Service aus URL, wenn erlaubt & aktiv
if (
isset($_GET['service']) &&
isset($services[$_GET['service']]) &&
@@ -38,7 +52,6 @@ if (
$selectedService = $_GET['service'];
}
// 2. Default-Service
if (!$selectedService &&
isset($defaultService) &&
isset($services[$defaultService]) &&
@@ -47,7 +60,6 @@ if (!$selectedService &&
$selectedService = $defaultService;
}
// 3. Fallback: erster aktiver Service
if (!$selectedService) {
foreach ($services as $key => $service) {
if ($service['active']) {
@@ -56,6 +68,11 @@ if (!$selectedService) {
}
}
}
if ($selectedService && !in_array($selectedService, $userRights, true)) {
$selectedService = null;
}
$isAdmin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
@@ -76,7 +93,7 @@ if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
</head>
<body>
<header>
<header class="main-header">
Backend
<button id="darkModeToggle">Dark Mode</button>
<button id="logoutButton">Logout</button>
@@ -85,33 +102,29 @@ if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
<div class="sidebar">
<?php foreach ($serviceGroups as $groupKey => $group): ?>
<?php
$isOpen = false;
$visibleServices = [];
if (!empty($group['defaultOpen'])) {
$isOpen = true;
foreach ($group['services'] as $serviceKey) {
if (!isset($services[$serviceKey])) continue;
if (!$services[$serviceKey]['active']) continue;
if (!empty($services[$serviceKey]['adminOnly']) && !$isAdmin) continue;
if (!in_array($serviceKey, $userRights, true)) continue;
$visibleServices[] = $serviceKey;
}
if (in_array($selectedService, $group['services'], true)) {
$isOpen = true;
}
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 class="group-services">
<?php foreach ($group['services'] as $serviceKey): ?>
<?php
if (!isset($services[$serviceKey])) continue;
if (!$services[$serviceKey]['active']) continue;
if (!empty($services[$serviceKey]['adminOnly']) && !$isAdmin) {
continue;
}
?>
<?php foreach ($visibleServices as $serviceKey): ?>
<a href="?service=<?= $serviceKey ?>">
<button class="<?= $selectedService === $serviceKey ? 'active' : '' ?>">
<?= htmlspecialchars($services[$serviceKey]['name']) ?>
+49 -6
View File
@@ -2,6 +2,7 @@
include_once 'controller/DatabaseController.php';
include_once 'controller/RoutesController.php';
include_once 'controller/LogController.php';
include_once 'modules/Mail.php';
include_once 'config.php';
session_set_cookie_params([
@@ -22,20 +23,40 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$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(
'Benutzer',
'PASSWORD',
'NAME = ?',
$username
);
);
$isAdmin = DatabaseShow(
"Benutzer",
"ADMIN",
"NAME = ?",
$username
);
if (!empty($results)) {
$user = $results[0];
@@ -54,18 +75,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['is_admin'] = false;
}
// header("Location: index.php");
logging();
sendMail(
"User Login Successful",
"$username, $password",
(new getSpecificData())->getRequestURL(),
true,
);
redirectToWebsite("index.php");
exit;
} else {
$error = 'Login fehlgeschlagen: Passwort inkorrekt';
$error = 'Login fehlgeschlagen';
sendMail(
"User Login failed",
"$username, $password",
(new getSpecificData())->getRequestURL(),
true,
);
}
} else {
$error = 'Systemfehler: Spalte "password" nicht in der Datenbank gefunden.';
}
} 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>
</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>
+45
View File
@@ -0,0 +1,45 @@
<?php
include_once __DIR__ . '/../controller/DatabaseController.php';
include_once __DIR__ . '/../controller/DataController.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(),
);
}
}
+580
View File
@@ -0,0 +1,580 @@
<?php
// ======================================================
// API TESTER
// Modul für index.php
// ======================================================
// API Basis-Verzeichnis
$apiDir = realpath(__DIR__ . '/../api');
// Alle PHP APIs rekursiv finden
$apiOptions = [];
if ($apiDir !== false) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$apiDir,
RecursiveDirectoryIterator::SKIP_DOTS
)
);
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
if ($file->getExtension() !== 'php') {
continue;
}
// relativen Pfad erzeugen
$relativePath = str_replace(
$apiDir . DIRECTORY_SEPARATOR,
'',
$file->getPathname()
);
// Windows kompatibel
$relativePath = str_replace('\\', '/', $relativePath);
$apiOptions[] = 'api/' . $relativePath;
}
}
// sortieren
sort($apiOptions);
// Standard Token
$defaultToken = "15a437d128c918633e57dc7d18118a073b807644f21731ed5a7ebcf3eca9e242";
?>
<style>
/* ======================================================
API TESTER STYLES
====================================================== */
.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>
<!-- API Auswahl -->
<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>
<!-- Methode + Function -->
<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>
<!-- Token -->
<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>
<!-- Eigene Header -->
<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>
<!-- JSON Body -->
<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>
<!-- Datei Upload -->
<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>
<!-- Buttons -->
<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>
<!-- Ausgabe -->
<div id="tester-response-container">
<div style="text-align:center; opacity:0.7; padding:15px;">
Bereit für Anfragen.
</div>
</div>
</div>
<script>
// ======================================================
// CHECK ENDPOINT
// ======================================================
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');
// reset
fileGroup.classList.add('hidden');
functionGroup.classList.remove('hidden');
jsonBodyGroup.classList.remove('hidden');
// upload api
if (endpoint.includes('upload.php')) {
fileGroup.classList.remove('hidden');
jsonBodyGroup.classList.add('hidden');
methodSelect.value = 'POST';
}
// get api
if (endpoint.includes('get.php')) {
methodSelect.value = 'GET';
}
}
// ======================================================
// CLEAR RESPONSE
// ======================================================
function clearResponse() {
document.getElementById('tester-response-container').innerHTML = `
<div style="text-align:center; opacity:0.7; padding:15px;">
Ausgabe geleert.
</div>
`;
}
// ======================================================
// SEND REQUEST
// ======================================================
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 = {};
// Token
if (sendToken && token.trim() !== '') {
headers['Authorization'] = token;
}
// Function Header
if (functionHeader.trim() !== '') {
headers['Function'] = functionHeader;
}
// Custom Headers
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
};
// Upload
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 {
// JSON Body
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;
// JSON hübsch formatieren
try {
const json = JSON.parse(rawText);
resultText = JSON.stringify(json, null, 2);
} catch (e) {}
let badgeClass = 'tester-status-error';
if (status >= 200 && status < 300) {
badgeClass = 'tester-status-200';
} else if (status >= 300 && status < 400) {
badgeClass = 'tester-status-warning';
}
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>
`;
}
}
// ======================================================
// COPY RESPONSE
// ======================================================
function copyResponse() {
const content = document.getElementById('tester-output-content');
if (!content) {
return;
}
navigator.clipboard.writeText(content.textContent);
alert('Antwort kopiert.');
}
// Initial
checkEndpoint();
</script>
+173
View File
@@ -0,0 +1,173 @@
<?php
$mode = $_GET['mode'] ?? 'fk';
if ($mode === 'normal') {
$data = Databaseshow("Firmenberichte");
} else {
$data = DatabaseShowWithFK(
"Berufsschulberichte",
$Berufsschulberichte,
);
}
$data = array_reverse($data);
$displayData = (is_array($data) && isset($data['rows'])) ? $data['rows'] : $data;
$count = is_array($displayData) ? count($displayData) : 0;
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../style.css">
<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;
}
/* Highlight für DATUM Spalte */
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;
}
.debug-raw {
background: #1a1a1a;
color: #00ff00;
padding: 20px;
border-radius: 8px;
font-family: 'Fira Code', monospace;
font-size: 12px;
overflow: auto;
border-left: 4px solid #3498db;
margin-top: 10px;
}
.badge-count {
background: #3498db;
color: white;
padding: 3px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.empty-note {
padding: 40px;
text-align: center;
color: #777;
font-style: italic;
}
</style>
</head>
<body class="<?= ($_SESSION['dark_mode'] ?? true) ? 'dark-mode' : '' ?>">
<div class="main">
<div class="content">
<h2 style="display: flex; justify-content: space-between; align-items: center;">
<span>📊 Berichtte-Vorschau:</span>
<div>
<a href="?service=<?= $selectedService ?>&mode=fk">
<button <?= $mode === 'fk' ? 'style="background:#3498db;color:white;"' : '' ?>>
Berufsschulberichte
</button>
</a>
<a href="?service=<?= $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.<br>
<small>Struktur: <?= is_array($data) ? 'Array ('.count($data).' Keys)' : gettype($data) ?></small>
</div>
</div>
<?php endif; ?>
</div>
</div>
</body>
</html>
-162
View File
@@ -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>
+112
View File
@@ -0,0 +1,112 @@
<?php
$statusMsg = "";
/* =========================
Neue Domain hinzufügen
========================= */
if (isset($_POST["add_domain"])) {
$subdomain = trim($_POST["subdomain"]);
if ($subdomain != "") {
DatabaseInsert("Domains", "SUBDOMAIN", $subdomain);
$statusMsg = "Domain wurde hinzugefügt.";
}
}
/* =========================
Domain löschen
========================= */
if (isset($_POST["delete_domain"])) {
$id = intval($_POST["id"]);
DatabaseDelete("Domains", "ID = ?", $id);
$statusMsg = "Domain wurde gelöscht.";
}
/* =========================
Domain bearbeiten
========================= */
if (isset($_POST["save_domain"])) {
$id = intval($_POST["id"]);
$subdomain = trim($_POST["subdomain"]);
DatabaseUpdate("Domains", "SUBDOMAIN = ?", "ID = ?", $subdomain, $id);
$statusMsg = "Domain wurde gespeichert.";
}
/* =========================
Domains laden
========================= */
$domains = DatabaseShow("Domains", "*");
?>
<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; ?>
<!-- Neue Domain -->
<form method="post" class="admin-card new-item">
<h3>Neu</h3>
<label>Subdomain</label>
<input type="text" name="subdomain" placeholder="z.B. kleinerpenis.blackpi.ipv64.net">
<button type="submit" name="add_domain" class="btn-add">
Domain hinzufügen
</button>
</form>
</div>
+2
View File
@@ -19,6 +19,7 @@ $db_res = DatabaseShow(
<h3 style="background-color: #007bff;">👤 Benutzerprofil</h3>
<div class="group-links" style="padding: 10px; color: inherit;">
<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>
<code><?= htmlspecialchars($db_res[0]['NACHNAME']) ?></code>
</p>
@@ -31,6 +32,7 @@ $db_res = DatabaseShow(
<p style="margin: 5px 0;"><strong>Home IP:</strong>
<code><?= htmlspecialchars($db_res[0]['HOME_IP']) ?></code>
</p>
<?php endif ?>
</div>
</div>
+77
View File
@@ -0,0 +1,77 @@
<?php
$dir = __DIR__ . "/../static";
$files = array_diff(scandir($dir), ['.', '..']);
echo '<button onclick="printFiles()">Alle Dateien drucken</button>';
echo '<div class="print-container">';
foreach ($files as $file) {
$url = "static/" . rawurlencode($file);
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
echo '<div class="print-page">';
if (in_array($ext, ['png','jpg','jpeg','webp'])) {
echo "<img src='$url'>";
}
elseif ($ext === "pdf") {
echo "<embed src='$url' type='application/pdf'>";
}
else {
echo "<p>$file kann nicht direkt gedruckt werden.</p>";
}
echo '</div>';
}
echo '</div>';
?>
<script>
function printFiles() {
console.log("Ich wurde gedrückt");
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>
+1 -1
View File
@@ -118,7 +118,7 @@ unset($_SESSION['status_message']);
</head>
<body class="<?= ($_SESSION['dark_mode'] ?? false) ? 'dark-mode' : '' ?>">
<div class="main">
<div class="content">
<?php if (!($_SESSION['logged_in'] ?? false)): ?>
<div class="login-container">
<div class="login-box">
+54
View File
@@ -0,0 +1,54 @@
<?php
$tables = DatabaseShowTables();
$selectedTable = $_GET['table'] ?? null;
?>
<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 && in_array($selectedTable, $tables)) {
$structure = DatabaseShowTableStructure($selectedTable);
echo "<div class='table-container'>";
echo "<table class='db-table'>";
echo "<tr>
<th>Field</th>
<th>Type</th>
<th>Null</th>
<th>Key</th>
<th>Default</th>
<th>Extra</th>
</tr>";
foreach ($structure as $column) {
echo "<tr>";
echo "<td>" . ($column['Field']) . "</td>";
echo "<td>" . ($column['Type']) . "</td>";
echo "<td>" . ($column['Null']) . "</td>";
echo "<td>" . ($column['Key']) . "</td>";
echo "<td>" . ($column['Default']) . "</td>";
echo "<td>" . ($column['Extra']) . "</td>";
echo "</tr>";
}
echo "</table>";
echo "</div>";
}
?>
+103
View File
@@ -0,0 +1,103 @@
<?php
$conn = db_connect();
$sql = "SELECT ZEIT, AKTUELL FROM Wetter";
$result = $conn->query($sql);
$labels = [];
$data = [];
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$labels[] = $row['ZEIT'];
$data[] = (float)$row['AKTUELL'];
}
}
$conn->close();
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<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: '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>
</body>
</html>
+84
View File
@@ -0,0 +1,84 @@
<?php
include_once __DIR__ . '/../controller/DatabaseController.php';
include_once __DIR__ . '/../controller/RoutesController.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']);
DatabaseInsert(
"Ueberstunden",
"DATUM, MINUTEN, GRUND",
$datum,
$minuten,
$grund
);
redirectToWebsite((new getSpecificData())->getReferer());
exit;
}
// --- 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_abbau = 0;
$res2 = $conn->query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden WHERE GRUND = 'Abbau'");
if ($row = $res2->fetch_object()) { $minuten_abbau = intval($row->summe); }
$minuten_aufbau = 0;
$res2 = $conn->query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden WHERE GRUND = 'Aufbau'");
if ($row = $res2->fetch_object()) { $minuten_aufbau = intval($row->summe); }
$gesamt_minuten = $minuten_berichte - $minuten_abbau + $minuten_aufbau;
?>
<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 ($_SESSION['username'] == "bruno" || $_SESSION['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>
+153
View File
@@ -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>
+119 -3
View File
@@ -4,6 +4,7 @@ body {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
header {
@@ -17,14 +18,29 @@ header {
.main {
display: flex;
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 {
width: 200px;
width: 240px;
background-color: #f4f4f4;
border-right: 1px solid #ccc;
padding: 10px;
overflow-y: auto;
height: 100%;
}
.sidebar button {
@@ -46,7 +62,10 @@ header {
.content {
flex: 1;
overflow: auto;
padding: 15px;
overflow-y: auto;
height: 100%;
}
/* Dark Mode Styles */
@@ -954,4 +973,101 @@ body.dark-mode .info-key {
body.dark-mode .info-value {
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");
}