# Coding Rules – Mitai Jinkendo Diese Regeln IMMER befolgen. Sie basieren auf Erfahrungen aus der Entwicklung. ## Backend ### 1. Auth und Mandantenkontext (Shinkan) **Jeder geschützte Endpoint braucht Auth.** Sofern der Endpoint **Vereinsdaten**, **visibility/club_id** oder **mandanten-gefilterte Listen** betrifft, zusätzlich **`TenantContext`** — nicht nur `require_auth` allein. ```python from tenant_context import TenantContext, get_tenant_context @router.get("/beispiel") def beispiel(tenant: TenantContext = Depends(get_tenant_context)): pid = tenant.profile_id role = tenant.global_role club_ctx = tenant.effective_club_id # kann None sein (z. B. Plattform-Admin) ``` - **Bibliotheks-/Planungslisten:** Filter wie bestehende Module (`library_content_visibility_sql` oder gleiche Leseprüfung); keine vollständige Tabelle für normale Nutzer. - **Schreiben:** `assert_valid_governance_visibility` aus `club_tenancy`, wenn `visibility` / `club_id` gesetzt werden. - **Dokumentation:** Änderungen in `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md` festhalten. - **Ausnahmen** (z. B. reiner Login, globale Kataloge): Kommentar `# ACCESS_LAYER exempt: …` und ggf. Eintrag in `backend/scripts/check_access_layer_hints.py`. Reine Plattform-Admin-Router (ohne Vereinskontext) können bei Bedarf weiter `Depends(require_auth)` nutzen — dann im Audit als „Plattform“ kennzeichnen. ### 2. Profile-ID aus TenantContext oder Session — nie aus freiem Header ```python pid = tenant.profile_id # ✅ bei Depends(get_tenant_context) # oder session['profile_id'] nur wenn Endpoint ausdrücklich ohne TenantContext (Ausnahme dokumentieren) # Nicht: request.headers.get('X-Profile-Id') ❌ ``` ### 3. bcrypt für Passwörter ```python from auth import hash_pin, verify_pin hashed = hash_pin(plain_password) # ✅ # Nicht: hashlib.sha256(...) ❌ ``` ### 4. PostgreSQL-Syntax ```python cur.execute("SELECT * FROM t WHERE id = %s AND active = true", (id,)) # Nicht: ? und active = 1 (SQLite-Syntax) ``` ### 5. Rate Limiting für sensitive Endpoints ```python from slowapi import Limiter @router.post("/sensitive") @limiter.limit("5/minute") def sensitive(request: Request, ...): ``` ### 6. Universal CSV Import / Admin-Vorlagen Neues **Import-Zielmodul**, Änderungen an **`csv_parser`**, Executor, DB-`source`/`CHECK`, oder System-CSV-Vorlagen: - Pflichtlektüre und Checkliste: **`.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** - Keine zweite DB-Connection im Importpfad; Zeilenfehler ohne „aborted transaction“ (SAVEPOINT-Muster wo nötig) - Admin Create/Update von Systemvorlagen: Validierung über `validate_csv_template` nicht umgehen ## Frontend ### 1. api.js für alle API-Calls ```javascript await api.listWeight() // ✅ await fetch('/api/weight') // ❌ kein Token ``` ### 2. Fehlerbehandlung in async Funktionen ```javascript try { const data = await api.meinEndpoint() } catch(e) { setError(e.message) // api.js wirft bereits Error mit detail-Text } ``` ### 3. Kein TypeScript Das Projekt nutzt bewusst kein TypeScript – keine .ts/.tsx Dateien erstellen. ### 4. Keine neuen npm-Pakete ohne Absprache Erst fragen, dann installieren. ### 5. CSS-Variablen statt Hardcoded-Farben ```javascript // ✅ Richtig: style={{color: 'var(--accent)'}} // ❌ Falsch: style={{color: '#1D9E75'}} ``` ### 6. Formular-Standard (VERBINDLICH ab 2026-04-22) **Alle neuen Formulare verwenden den Standard-Stil:** ```jsx // ✅ Standard: Label oben, volle Breite, linksbündig