- Added new documentation references for access layer governance in CLAUDE.md, including multi-tenancy and endpoint audit guidelines. - Updated ACCESS_LAYER_AND_GOVERNANCE_PLAN.md to include cursor and heuristic checks for access layer compliance. - Enhanced ACCESS_LAYER_ENDPOINT_AUDIT.md to clarify endpoint visibility and governance requirements, including exemptions for certain routers. - Introduced library_content_visible_to_profile function in club_tenancy.py to streamline visibility checks for library content. - Updated exercise progression graphs router to utilize the new visibility function, improving access control. - Bumped application version to 0.8.27 and updated changelog to reflect these changes.
172 lines
5.3 KiB
Markdown
172 lines
5.3 KiB
Markdown
# 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
|
||
<div className="form-row">
|
||
<label className="form-label">Feldname *</label>
|
||
<input
|
||
type="text"
|
||
className="form-input"
|
||
value={formData.field}
|
||
onChange={(e) => updateFormField('field', e.target.value)}
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
// ✅ Textarea
|
||
<div className="form-row">
|
||
<label className="form-label">Beschreibung</label>
|
||
<textarea
|
||
className="form-input"
|
||
rows={3}
|
||
value={formData.description}
|
||
onChange={(e) => updateFormField('description', e.target.value)}
|
||
/>
|
||
</div>
|
||
|
||
// ✅ Select
|
||
<div className="form-row">
|
||
<label className="form-label">Status</label>
|
||
<select
|
||
className="form-input"
|
||
value={formData.status}
|
||
onChange={(e) => updateFormField('status', e.target.value)}
|
||
>
|
||
<option value="active">Aktiv</option>
|
||
<option value="inactive">Inaktiv</option>
|
||
</select>
|
||
</div>
|
||
|
||
// ❌ NICHT: Inline-Layout mit Label links
|
||
// Nur für Ausnahmen (kurze Werte mit Einheit) nutzen:
|
||
<div className="form-row--inline">
|
||
<label className="form-label">Dauer</label>
|
||
<input type="number" className="form-input" value={duration} />
|
||
<span className="form-unit">min</span>
|
||
</div>
|
||
```
|
||
|
||
**Regeln:**
|
||
- Label ist `<label>` mit Klasse `.form-label` (nicht `<div>`)
|
||
- Input/Textarea/Select nutzen `.form-input`
|
||
- Volle Breite (100%), linksbündig
|
||
- Pflichtfelder mit `*` im Label kennzeichnen
|
||
- Inline-Variante (`.form-row--inline`) nur für Ausnahmen (Zahlen mit Einheit)
|
||
|
||
## Git & Deployment
|
||
|
||
### 1. Nie direkt auf main pushen
|
||
Immer über Pull Request in Gitea: develop → main
|
||
|
||
### 2. develop Branch nie löschen
|
||
Er ist permanent – nicht nach Merge löschen.
|
||
|
||
### 3. .env nie committen
|
||
Steht in .gitignore – nie entfernen.
|
||
|
||
### 4. Commit-Message Format
|
||
```
|
||
feat: neues Feature
|
||
fix: Bugfix
|
||
refactor: Umbau ohne Funktionsänderung
|
||
docs: Dokumentation
|
||
ci: CI/CD Änderungen
|
||
chore: Maintenance
|
||
```
|