🚜 Tretbeckenreinigungs-Team — Administrations-Doku
Diese Doku beschreibt die Pflege und Wartung der Tretbeckenreinigungs-App.
Sie ergänzt die anleitung.html im Treckertreff-Paket (die nur die
Erst-Installation beschreibt).
https://tbrt.rusti.ipv64.netHosting: VM
122-g50zeiten (192.168.178.122), parallel zur StundenabrechnungDatenbank: MariaDB, Datenbank
treckertreff_db, User treckertreff_userStand: 21. Mai 2026
Architektur — wer macht was?
Die App nutzt dieselbe Infrastruktur wie die Stundenabrechnung. Eine Anfrage durchläuft folgende Stationen:
| Schritt | Komponente | Was passiert |
|---|---|---|
| 1 | Browser/Smartphone | Aufruf von https://tbrt.rusti.ipv64.net |
| 2 | DNS (ipv64.net) | Löst tbrt.rusti.ipv64.net in deine Heim-IP auf |
| 3 | FritzBox | Port 80/443 → 192.168.178.131 (NPM) |
| 4 | NPM (192.168.178.131) | Reverse-Proxy + Let's-Encrypt-SSL → weiter an 192.168.178.122:80 |
| 5 | Apache (.122) | VirtualHost tbrt.rusti.ipv64.net liefert Dateien aus /var/www/treckertreff/ |
| 6 | PHP / MariaDB | api.php spricht mit Datenbank treckertreff_db |
Datei-Layout auf .122
Alle App-Dateien liegen in /var/www/treckertreff/ und gehören www-data:www-data:
| Datei | Funktion |
|---|---|
index.html | Login-Seite mit Team-Foto |
app.html | Hauptansicht (Termin + Mitbringliste) |
api.php | Backend: Login, Termin, Kategorien, Einträge |
config.php | DB-Zugang, App-Passwort-Hash, Teilnehmer-Liste |
setup.sql | Tabellen-Struktur (für Neuinstallation) |
.htaccess | Apache-Schutzregeln |
team-foto.jpg | Foto der Truppe (Hintergrund) |
favicon.svg | Browser-Tab-Icon |
apple-touch-icon.png | iOS-Homescreen-Icon |
icon-192.png / icon-512.png | Android-Homescreen-Icons |
manifest.json | PWA-Manifest |
anleitung.html | Installations-Anleitung (statisch) |
Apache-Konfiguration
Die VirtualHost-Datei liegt in:
/etc/apache2/sites-available/treckertreff.conf
Aktiviert mit a2ensite treckertreff.conf. Wichtige Punkte:
ServerName tbrt.rusti.ipv64.netDocumentRoot /var/www/treckertreff- 3 Sicherheits-Header (
X-Frame-Options,X-Content-Type-Options,Referrer-Policy) - Logs:
/var/log/apache2/treckertreff_error.logundtreckertreff_access.log
Datenbank-Struktur
Die App nutzt eine eigene MariaDB-Datenbank treckertreff_db mit drei Tabellen:
Tabelle termin
Enthält immer genau einen Datensatz (den aktuellen Termin).
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | INT PRIMARY KEY | Auto-Inkrement |
| datum | DATE | Termin-Datum, z.B. 2026-06-16 |
| uhrzeit | TIME | Termin-Uhrzeit, z.B. 17:00:00 |
| geaendert_am | TIMESTAMP | Wann zuletzt geändert |
| geaendert_von | VARCHAR(50) | Wer hat geändert |
Tabelle kategorien
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | INT PRIMARY KEY | Auto-Inkrement |
| name | VARCHAR(100) | z.B. "Fleisch", "Brot" |
| icon | VARCHAR(20) | Emoji, z.B. 🥩 |
| reihenfolge | INT | Sortier-Reihenfolge |
| erstellt_am | TIMESTAMP |
Tabelle eintraege
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | INT PRIMARY KEY | Auto-Inkrement |
| kategorie_id | INT FK | Verweis auf kategorien |
| person | VARCHAR(50) | z.B. "Steffen" |
| beschreibung | VARCHAR(255) | z.B. "Steaks" |
| menge | VARCHAR(50) | z.B. "2 kg" (optional) |
| erstellt_am / geaendert_am | TIMESTAMP |
Foreign Key: Wenn eine Kategorie gelöscht wird, werden alle zugehörigen Einträge automatisch mit gelöscht (ON DELETE CASCADE).
App-Passwort ändern
Das Passwort wird als bcrypt-Hash in config.php gespeichert. So wechselst du es:
ssh root@192.168.178.122
PASSWORT='NeuesPasswort2026'
HASH=$(curl -s "http://localhost/api.php?aktion=hash_erzeugen&pw=$PASSWORT" \
-H "Host: tbrt.rusti.ipv64.net" \
| python3 -c "import sys,json;print(json.load(sys.stdin)['hash'])")
echo "Hash-Länge: ${#HASH}" # sollte 60 zeigen
sed -i "s|APP_PASSWORT_HASH', '[^']*'|APP_PASSWORT_HASH', '$HASH'|" \
/var/www/treckertreff/config.php
curl -s -X POST http://localhost/api.php \
-H "Host: tbrt.rusti.ipv64.net" \
-d "aktion=login&passwort=$PASSWORT"
# Erwartung: {"erfolg":true}
unset PASSWORT HASH
Personen hinzufügen / ändern
Die 6 Teilnehmer sind hart in config.php hinterlegt:
define('TEAM_MITGLIEDER', json_encode([
'Fin', 'Josef', 'Jürgen', 'Rudolf', 'Steffen', 'Thomas'
]));
Neuen Teilnehmer hinzufügen
nano /var/www/treckertreff/config.php
# Liste anpassen, z.B. 'Werner' hinzufügen
# Ctrl+O, Enter, Ctrl+X
Person umbenennen
⚠️ Wenn jemand die App schon benutzt hat und in eintraege.person steht, ist der alte Name in der Datenbank. Daher:
nano /var/www/treckertreff/config.php # neue Liste eintragen
# Alte Einträge in der DB auf neuen Namen aktualisieren
mariadb treckertreff_db -e "UPDATE eintraege SET person = 'Werner' WHERE person = 'Wolfgang';"
mariadb treckertreff_db -e "UPDATE termin SET geaendert_von = 'Werner' WHERE geaendert_von = 'Wolfgang';"
Person entfernen
Aus der Liste in config.php löschen + alte Einträge aus der DB entfernen:
mariadb treckertreff_db -e "DELETE FROM eintraege WHERE person = 'Wolfgang';"
Backup erstellen
Die App ist klein — ein Backup besteht aus 2 Sachen:
Datenbank-Backup
mariadb-dump treckertreff_db > /root/treckertreff_$(date +%Y%m%d).sql
ls -la /root/treckertreff_*.sql
Konfigurations-Backup
cp /var/www/treckertreff/config.php /root/config_$(date +%Y%m%d).php.bak
cp /etc/apache2/sites-available/treckertreff.conf /root/treckertreff.conf.bak
Vollständig: Tarball
tar czf /root/treckertreff_full_$(date +%Y%m%d).tar.gz \
/var/www/treckertreff/ \
/etc/apache2/sites-available/treckertreff.conf
mariadb-dump treckertreff_db > /root/treckertreff_db_$(date +%Y%m%d).sql
ls -la /root/treckertreff_*
scp auf den Mac herunterladen:
scp root@192.168.178.122:/root/treckertreff_* ~/Backups/
Backup einspielen
mariadb -e "DROP DATABASE IF EXISTS treckertreff_db; \
CREATE DATABASE treckertreff_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mariadb treckertreff_db < /root/treckertreff_db_20260521.sql
mariadb treckertreff_db -e "SHOW TABLES;"
cd /
tar xzf /root/treckertreff_full_20260521.tar.gz
chown -R www-data:www-data /var/www/treckertreff/
systemctl reload apache2
Alles zurücksetzen (vor neuem Treff)
Wenn ein Treff vorbei ist und du für den nächsten alles leeren willst:
# Backup nicht vergessen!
mariadb-dump treckertreff_db > /root/treckertreff_letzter_treff.sql
# Alle Einträge löschen, Kategorien und Termin bleiben
mariadb treckertreff_db -e "DELETE FROM eintraege;"
# Optional: Termin auf "in 3 Wochen" setzen
mariadb treckertreff_db -e "UPDATE termin SET datum = DATE_ADD(CURDATE(), INTERVAL 21 DAY), \
uhrzeit = '18:00:00', geaendert_von = 'System' \
WHERE id = 1;"
Code-Änderungen einspielen
Wenn ich (Claude) dir eine neue Version von app.html, api.php oder
einer anderen Datei schicke:
Aus dem Chat in den Downloads-Ordner.
Vom Mac:
cd ~/Downloads
scp app.html root@192.168.178.122:/var/www/treckertreff/
Vom Windows-PC (PowerShell):
cd $env:USERPROFILE\Downloads
scp app.html root@192.168.178.122:/var/www/treckertreff/
ssh root@192.168.178.122 "ls -la /var/www/treckertreff/app.html"
Wenn die Datei nicht www-data:www-data gehört:
ssh root@192.168.178.122 "chown www-data:www-data /var/www/treckertreff/app.html"
Cmd+Shift+R (Mac) oder Strg+F5 (Windows) — ohne das holst du dir nur die alte Version aus dem Browser-Cache.
SSL-Zertifikat erneuern
Das Zertifikat ist Let's-Encrypt und wird vom NPM auf 192.168.178.131 automatisch alle 60 Tage erneuert. Du musst nichts tun.
Falls doch mal manuell nötig:
- NPM-Web-UI öffnen:
http://192.168.178.131:81 - Hosts → Proxy Hosts →
tbrt.rusti.ipv64.net→ Edit - SSL-Tab → "Request a new SSL Certificate" → Save
Logs prüfen
Apache-Logs
| Befehl | Zweck |
|---|---|
tail -f /var/log/apache2/treckertreff_access.log | Live alle Zugriffe |
tail -f /var/log/apache2/treckertreff_error.log | Live alle Fehler |
tail -50 /var/log/apache2/treckertreff_error.log | Letzte 50 Fehler |
grep -i "fatal\|error" /var/log/apache2/treckertreff_error.log | tail -20 | Nur Fehler |
PHP-Fehler sichtbar machen
Wenn du beim Debuggen PHP-Fehler direkt im Browser sehen willst, in api.php ganz oben einfügen:
ini_set('display_errors', '1');
error_reporting(E_ALL);
⚠️ Nach dem Debuggen wieder entfernen — Produktiv-Setup zeigt keine Fehler!
API direkt testen (ohne Browser)
Sehr nützlich um Fehler einzukreisen — Browser-Cache, JavaScript etc. raus aus der Gleichung.
Login simulieren
curl -c /tmp/cookies.txt -s -X POST http://localhost/api.php \
-H "Host: tbrt.rusti.ipv64.net" \
-d "aktion=login&passwort=DEINPASSWORT"
# Erwartung: {"erfolg":true}
Daten laden
curl -b /tmp/cookies.txt -s http://localhost/api.php?aktion=alles_holen \
-H "Host: tbrt.rusti.ipv64.net" | python3 -m json.tool
Termin ändern
curl -b /tmp/cookies.txt -s -X POST http://localhost/api.php \
-H "Host: tbrt.rusti.ipv64.net" \
-d "aktion=termin_speichern&datum=2026-06-16&uhrzeit=17:00&person=Steffen"
Eintrag hinzufügen
curl -b /tmp/cookies.txt -s -X POST http://localhost/api.php \
-H "Host: tbrt.rusti.ipv64.net" \
-d "aktion=eintrag_hinzu&kategorie_id=1&person=Steffen&beschreibung=Steaks&menge=2 kg"
Datenbank-Tests
Inhalte ansehen
mariadb treckertreff_db -e "SELECT * FROM termin;"
mariadb treckertreff_db -e "SELECT * FROM kategorien;"
mariadb treckertreff_db -e "SELECT * FROM eintraege;"
Encoding prüfen (HEX-Bytes anschauen)
mariadb treckertreff_db -e "SELECT id, name, HEX(name), HEX(icon) FROM kategorien;"
Was die HEX-Werte bedeuten:
| Zeichen | UTF-8 (korrekt) | Doppelt-UTF-8 (kaputt) |
|---|---|---|
| ä | C3 A4 (2 Bytes) | C3 83 C2 A4 (4 Bytes) |
| ö | C3 B6 | C3 83 C2 B6 |
| ü | C3 BC | C3 83 C2 BC |
| 🥩 | F0 9F A5 A9 (4 Bytes) | C3 B0 C5 B8 C2 A5 C2 A9 (8 Bytes) |
Bot-Scans erkennen
Sobald die App öffentlich erreichbar ist, kommen automatisierte Scans aus dem Internet. Im Apache-Error-Log siehst du Anfragen wie:
script '/var/www/treckertreff/wp-login.php' not found
script '/var/www/treckertreff/.env.php' not found
client denied: /var/www/treckertreff/server-status
Das sind automatische Bot-Netze, die nach bekannten Sicherheits-Lücken suchen (WordPress-Login, Konfigurationsdateien etc.). Da du diese Software gar nicht installiert hast, finden sie nichts. Es ist kein Angriff auf die App selbst.
| Pfad im Log | Was der Bot sucht |
|---|---|
/wp-login.php | WordPress-Admin |
/.env / /.env.php | Konfigurations-Files mit Passwörtern |
/info.php / /phpinfo.php | PHP-Info-Seiten |
/server-status | Apache-Status-Seite |
/db_backup.sql / /backup.sql | Versehentlich öffentliche Backups |
/.git/config | Git-Repos die öffentlich sind |
Alle diese Anfragen werden mit 404 Not Found oder 403 Forbidden
abgewiesen — kein Anlass zur Sorge.
Emojis / Umlaute kaputt
Wenn Emojis als 🥩 oder Getränke angezeigt werden:
Ursache 1: Apache liefert keinen Charset-Header
curl -sI -H "Host: tbrt.rusti.ipv64.net" http://localhost/ | grep -i content-type
# Sollte zeigen: Content-Type: text/html; charset=UTF-8
Wenn charset=UTF-8 fehlt:
echo "AddDefaultCharset UTF-8" > /etc/apache2/conf-available/charset.conf
a2enconf charset
systemctl restart apache2
Ursache 2: Doppelt-UTF-8 in der Datenbank
Tritt auf wenn beim Import mit mariadb < setup.sql die Datenbank das UTF-8 als
latin1 interpretiert.
Diagnose:
mariadb treckertreff_db -e "SELECT name, HEX(name), HEX(icon) FROM kategorien;"
Wenn ä als C383C2A4 erscheint (statt C3A4), ist es doppelt kodiert.
Reparatur:
# Backup zuerst!
mariadb-dump treckertreff_db > /tmp/treckertreff_backup.sql
# name-Spalte reparieren
mariadb treckertreff_db <<'EOF'
UPDATE kategorien
SET name = CONVERT(CAST(CONVERT(name USING latin1) AS BINARY) USING utf8mb4);
EOF
# icon-Spalte reparieren
mariadb treckertreff_db <<'EOF'
UPDATE kategorien
SET icon = CONVERT(CAST(CONVERT(icon USING latin1) AS BINARY) USING utf8mb4);
EOF
# Verifizieren
mariadb treckertreff_db -e "SELECT name, HEX(name), HEX(icon) FROM kategorien;"
Login funktioniert nicht
Häufige Ursachen und Diagnose:
1. Hash-Format prüfen
python3 <<'EOF'
import re
with open('/var/www/treckertreff/config.php', 'r') as f:
content = f.read()
m = re.search(r"APP_PASSWORT_HASH'\s*,\s*'([^']+)'", content)
if m:
h = m.group(1)
print(f"Länge: {len(h)}")
print(f"Beginnt mit: {h[:7]}")
print(f"OK" if h.startswith('$2y$') and len(h) == 60 else "FALSCH")
EOF
Erwartung: Länge 60, beginnt mit $2y$10$ oder $2y$12$.
2. Hash und Passwort gehören zusammen?
HASH='HIER_DEINEN_HASH_EINSETZEN'
PASSWORT='HIER_DEIN_PASSWORT'
php -r "echo password_verify('$PASSWORT', '$HASH') ? 'PASST' : 'PASST NICHT';"
3. Brute-Force-Sperre aktiv?
Nach 5 Fehlversuchen wird die IP für 15 Min gesperrt. Wenn du dauernd "Zu viele Fehlversuche" bekommst, einfach 15 Min warten oder die PHP-Session resetten:
# Session-Daten löschen (für ALLE User!)
rm /var/lib/php/sessions/sess_*
# Browser-Cookie löschen oder Inkognito-Tab nutzen
Internal Server Error (500)
Browser zeigt "500 Internal Server Error" → PHP-Fehler. Diagnose:
tail -30 /var/log/apache2/treckertreff_error.log
Häufige Ursachen:
- DB-Zugangsdaten falsch: Prüfe
DB_PASSinconfig.php - MariaDB läuft nicht:
systemctl status mariadb - Syntax-Fehler in PHP:
php -l /var/www/treckertreff/api.php - Falsche Berechtigungen:
chown -R www-data:www-data /var/www/treckertreff/
Browser-Cache-Probleme
Symptom: Du hast was geändert, im Browser kommt aber noch die alte Version.
| Browser | Hard-Reload |
|---|---|
| Chrome / Edge (Windows) | Strg+F5 oder Strg+Shift+R |
| Chrome / Edge / Firefox (Mac) | Cmd+Shift+R |
| Firefox (Windows) | Strg+Shift+R |
| Safari (Mac) | Cmd+Option+E (Cache leeren) dann Cmd+R |
| Mobiles Safari (iOS) | App schließen, Inkognito-Tab öffnen |
| Mobile Chrome (Android) | ⋮ → Verlauf → Browserdaten löschen |
Was schützt die App?
| Schutz | Wie |
|---|---|
| Verschlüsselung | HTTPS via Let's Encrypt (NPM), Force-SSL aktiv → kein http erlaubt |
| Brute-Force | 5 Versuche → 15 Min Sperre (in config.php konfigurierbar) |
| Passwort-Speicherung | bcrypt Cost 12 (langsamer Hash, schützt vor Wörterbuchattacken) |
| Sessions | HttpOnly, Secure, SameSite=Lax — schützt vor XSS und CSRF |
| SQL-Injection | Alle DB-Zugriffe nutzen Prepared Statements |
| config.php | Per Apache <FilesMatch> blockiert |
| Clickjacking | HTTP-Header X-Frame-Options: SAMEORIGIN |
| MIME-Sniffing | HTTP-Header X-Content-Type-Options: nosniff |
| Referrer-Leak | HTTP-Header Referrer-Policy: strict-origin-when-cross-origin |
Notfall-Wiederherstellung
App ist komplett down
Schritt 1: Services prüfen:
systemctl status apache2 mariadb
Schritt 2: Logs anschauen:
tail -50 /var/log/apache2/treckertreff_error.log
tail -50 /var/log/mysql/error.log
Schritt 3: Apache-Config testen:
apache2ctl configtest
Schritt 4: Services neu starten:
systemctl restart apache2
systemctl restart mariadb
Datenbank kaputt — Backup einspielen
# Aktuelle DB als Notfall-Sicherung
mariadb-dump treckertreff_db > /root/treckertreff_kaputt.sql
# Zurücksetzen und letztes Backup einspielen
mariadb -e "DROP DATABASE treckertreff_db; \
CREATE DATABASE treckertreff_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mariadb treckertreff_db < /root/treckertreff_db_LETZTES.sql
Alles ist kaputt — VM-Rollback
Im Proxmox-Web-UI:
- VM
122-g50zeitenauswählen - Links auf "Backup"
- Letztes funktionierendes Backup auswählen
- "Zurückspielen" klicken
⚠️ Damit gehen ALLE Änderungen seit dem Backup verloren — auch auf der Stundenabrechnung!
Verwandte Dokus
webserver-doku.html— DNS-Updates, ipv64, NPManleitung.html(im Treckertreff-Paket) — Erst-Installation
Treckertreff-Doku Stand: 21.05.2026
https://tbrt.rusti.ipv64.net auf VM 122-g50zeiten