CLAUDE.md mit Architektur-Ueberblick ergaenzt
Dokumentiert die Punkte, die sich nur aus dem Zusammenspiel mehrerer Dateien erschliessen: Instanz-Ableitung aus dem Arbeitsverzeichnis, externe Konfiguration, Request-Ablauf ueber checkRightsNInclude(), das positionsbasierte Rechte-Schema, die bei jedem Request laufenden Schema-Migrationen sowie tote Pfade und Stolperfallen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
Kundenverwaltung für ein Ingenieurbüro: Kunden, Termine, Projekte, Angebote, Lieferscheine, Rechnungen, Zeiterfassung. Gewachsene PHP-Anwendung (seit ~2011) mit Smarty-Templates und MariaDB/MySQL. Domänenbegriffe, Bezeichner und UI-Texte sind durchgehend deutsch — das bitte beibehalten.
|
||||
|
||||
## Tooling
|
||||
|
||||
Es gibt **kein** Composer, npm, Build, Linter oder Testsuite. Abhängigkeiten liegen als Kopie in `incs/` und werden dort direkt gepflegt. Ein Update einer dieser Bibliotheken ist immer ein manueller Eingriff, kein Paketmanager-Aufruf.
|
||||
|
||||
Syntaxprüfung der eigenen Dateien:
|
||||
|
||||
```bash
|
||||
for f in $(git ls-files "*.php" | grep -vE "^(incs/(tcpdf|tcpdf_old|pChart|PHPMailer|WebAuthn|drawer|font)|smarty|smarty3|spellcheck|js)/"); do php -l "$f"; done
|
||||
```
|
||||
|
||||
Zum Ausführen braucht es einen Webserver mit PHP, eine MariaDB/MySQL-Instanz und die externe Konfigurationsdatei (siehe unten). Ohne die Konfiguration springt `index.php` in den Setup-Modus (`update/firststart.php`).
|
||||
|
||||
## Die zwei zentralen Mechanismen
|
||||
|
||||
### 1. Das Verzeichnis bestimmt die Instanz
|
||||
|
||||
`incs/constants.php` leitet aus `getcwd()` den Namen des Installationsverzeichnisses ab und baut daraus:
|
||||
|
||||
| Konstante | Wert bei Installation in `…/kunden` |
|
||||
|---|---|
|
||||
| `CONFIG_FILE` | `kunden_settings.conf` |
|
||||
| `UPLOADS_PATH` | `uploads_kunden/` |
|
||||
| `SESSION_NAME` | `KUNDENSESS` |
|
||||
|
||||
Aus **einer** Codebasis laufen dadurch mehrere getrennte Instanzen — die `uploads_dev`, `uploads_ingwa`, `uploads_kunden`, `uploads_lsg` im Baum sind die Spuren davon. Jede Instanz hat eigene Config, eigene Session, eigene Uploads.
|
||||
|
||||
**Konsequenz für alles, was per CLI/Cron läuft:** Das Arbeitsverzeichnis muss das Installationsverzeichnis sein, sonst wird die falsche (oder keine) Konfiguration geladen.
|
||||
|
||||
```bash
|
||||
cd /home/intranet/kunden && php db-backup.php
|
||||
```
|
||||
|
||||
`constants.php` kompensiert dabei nur die Aufrufe aus `ajax/` und `ajax/auto/`, indem es ein bzw. drei Ebenen hochgeht.
|
||||
|
||||
### 2. Zugangsdaten liegen außerhalb des Repos
|
||||
|
||||
`CONFIG_PATH` zeigt auf `/volume1/homes/wagner/kundenconfig/` (Linux) bzw. `c:/kundenverwaltung/config/` (Windows). Geschrieben wird die Datei von `update/rebuild-settings.php` beim Ersteinrichten.
|
||||
|
||||
Die Datei deklariert `$mysql_user`, `$mysql_pw`, `$mysql_server`, `$mysql_db`, `$firma` und weitere ausdrücklich als `global` und weist sie erst danach zu. Nur deshalb funktioniert `readsettings()` in `incs/settings.php`: Das `include` passiert im Funktionsrumpf, ohne die `global`-Zeilen blieben die Variablen dort lokal. Wer die Config-Struktur ändert, muss diese Eigenheit erhalten.
|
||||
|
||||
Zugangsdaten gehören **nie** in den Code — `db-backup.php` zeigt das Muster für Skripte außerhalb des Request-Zyklus (Config einlesen, Passwort über eine temporäre Optionsdatei an `mysqldump`, nicht per Kommandozeile).
|
||||
|
||||
## Request-Ablauf
|
||||
|
||||
`index.php` ist der einzige Einstieg für die Oberfläche:
|
||||
|
||||
1. Konstanten, Session, Konfiguration, DB-Verbindung
|
||||
2. Schema-Migration (siehe unten)
|
||||
3. `head.tpl`
|
||||
4. bei gültiger Session: `checkRightsNInclude()` aus `incs/funcs.php` — ein `switch` über `$_GET["action"]`, das Rechte prüft und den Pfad in `sites/` zurückgibt, der dann inkludiert wird
|
||||
5. `foot.tpl`
|
||||
|
||||
**Eine neue Seite anzulegen heißt: Datei in `sites/` plus ein `case` in `checkRightsNInclude()`.** Ohne den `case` ist die Seite nicht erreichbar.
|
||||
|
||||
Die Endpunkte in `ajax/` gehen **nicht** über `index.php`. Sie bootstrappen selbst über `incs/connectmysql.php` (Konstanten, Session, Config, DB, Escape-Wrapper) und rendern eigene Fragmente nach `ajax/templates_c/`.
|
||||
|
||||
## Rechtesystem
|
||||
|
||||
`$_SESSION["rights"]` ist ein **String**, keine Zahl. Die Zeichenposition benennt den Bereich, der Zeichenwert ist eine Bitmaske:
|
||||
|
||||
- Position: `RIGHTS_KUNDEN`=0, `RIGHTS_TERMINE`=1, `RIGHTS_ANGEBOTE`=2, `RIGHTS_LIEFER`=3, `RIGHTS_RECHNUNGEN`=4, `RIGHTS_SUPERADMIN`=5
|
||||
- Wert: `RIGHTS_LOOK`=1, `RIGHTS_CHANGE`=2, `RIGHTS_ADD`=4
|
||||
|
||||
Geprüft wird immer nach diesem Muster:
|
||||
|
||||
```php
|
||||
if(substr($_SESSION["rights"], RIGHTS_KUNDEN, 1) & RIGHTS_LOOK)
|
||||
```
|
||||
|
||||
## Schema-Migrationen laufen bei jedem Seitenaufruf
|
||||
|
||||
Es gibt keine SQL-Migrationsdateien. Der Stand steht in `update/thisversion/currentversion.txt` (dreistellig, aktuell `010`), die Zielversion ist der **erste** Eintrag in `$versions` in `update/versions.php`. `index.php` ruft bei jedem Request `checknewversion()` und `manageupdate()` aus `incs/update.php`; liegt die Zielversion höher, arbeitet `update_tables()` in `update/database-update.php` sich in einem `switch` von Version zu Version hoch und schreibt die neue Nummer zurück.
|
||||
|
||||
Eine Schemaänderung besteht also aus: neuer Eintrag **vorn** in `update/versions.php` und passender `case` in `update_tables()`. `update/thisversion/` muss für den Webserver schreibbar sein.
|
||||
|
||||
## Datenbankzugriff
|
||||
|
||||
Prozedurales `mysqli`. `$dbconn` wird zusätzlich in `$_SESSION["dbconn"]` abgelegt, damit `incs/mysql_ecape_wrapper.php` eine globale Funktion `mysql_escape_string()` als Shim über diese Verbindung bereitstellen kann — ein Überbleibsel der `mysql_*`-Ära, das in altem Code noch aufgerufen wird.
|
||||
|
||||
SQL wird durchgehend per String-Verkettung gebaut. Escaping ist Handarbeit über `mysqli_real_escape_string($dbconn, …)` und muss bei jeder Änderung mitgedacht werden.
|
||||
|
||||
## Fallstricke
|
||||
|
||||
- **`smarty/` ist Smarty 4.5.6, `smarty3/` ist Smarty 3.1.6.** Die Namen führen in die Irre. Alle 73 einbindenden Dateien nutzen `smarty/libs/`; `smarty3/` ist tot.
|
||||
- **`incs/fpdf.php` und `sites/makepdf_OLD.php` sind tot** und parsen ab PHP 8.0 nicht mehr (`$s{$i}`-Syntax). Der lebende PDF-Pfad ist `incs/makepdf.php` → `incs/tcpdf`. `incs/tcpdf_old` und `incs/users_old.php` sind ebenfalls Altlasten.
|
||||
- **`index.php` setzt `error_reporting(E_ERROR | E_PARSE)`.** Warnungen und Notices sind unterdrückt, und der Code verlässt sich darauf — nicht ohne Not hochdrehen, sonst überflutet es die Ausgabe.
|
||||
- **`templates_c/`, `ajax/templates_c/` und `temp/` müssen für den Webserver schreibbar sein.** Sie sind git-ignoriert und nur über `.gitkeep` im Repo vorhanden.
|
||||
- **`uploads_*/{notes,signs}` enthalten personenbezogene Kundendaten** (Rechnungen, Scans, Unterschriften) und sind bewusst git-ignoriert. Nie committen.
|
||||
- Zeilenenden werden über `.gitattributes` auf LF normalisiert.
|
||||
|
||||
## Authentifizierung
|
||||
|
||||
Klassischer Login plus WebAuthn/FIDO2. Server-Seite: `authServer.php` mit `incs/WebAuthn` (Bibliothek von Lukas Buchs) und `incs/authHelper.php`; Client-Seite `js/auth.js`. Schlüsselverwaltung über `addKey.php` und `sites/userkeys.php`.
|
||||
|
||||
## Historie
|
||||
|
||||
Das Repository wurde am 2026-08-28 neu initialisiert. Die mitgelieferte Historie von 2014 war irreparabel beschädigt und wurde verworfen — der erste Commit ist der Ausgangsstand, nicht der Projektbeginn.
|
||||
Reference in New Issue
Block a user