Files
Aether/CLAUDE.md
T
marcandClaude Opus 5 471679ff64 refactor(repo): deploy/ heißt jetzt build/ – der Name sagte das Gegenteil
"deploy" klang nach Ausrollen, drin steht das Gegenteil: das Bau-Rezept
und das Compose, das aus dem Quelltext baut. Ausgerollt wird mit
docker-compose.example.yml im Wurzelverzeichnis. Marc ist genau darüber
gestolpert.

Zwei Dinge mussten dafür vorher fest werden, sonst hätte das Umbenennen
die Entwicklungsdatenbank gekostet: Compose leitet den Projektnamen aus
dem Ordnernamen ab und hätte unter "build" ein neues, leeres Projekt mit
neuem, leerem Volume angelegt. Der Projektname steht deshalb jetzt im
Compose (name: aether), und das Volume trägt seinen alten Namen
ausdrücklich weiter (deploy_postgres_data) – unschön, aber die Daten
bleiben.

Ebenfalls angepasst: .gitignore. Dort stand deploy/.env; nach dem
Umbenennen wäre build/.env nur noch von der allgemeinen .env-Zeile
gedeckt gewesen.

Nachgeprüft nach dem Umzug: Abbild baut, Stapel startet, 113 Dateien in
der Datenbank wie vorher, Volume unverändert deploy_postgres_data,
GET / → 200.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 09:24:09 +02:00

167 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Aether – Projektkonventionen für Claude Code
## Projekt-Überblick
Self-hosted Cloud-Alternative zu Nextcloud. Go-Backend + React-Frontend, modularer Monolith.
Projektplan: `cloud-projekt-plan.md`
## Monorepo-Struktur
```
aether/
├── backend/ # Go – modularer Monolith
│ ├── cmd/aether/ # main.go – Einstiegspunkt
│ ├── internal/ # Domänen-Pakete (nie von außen importieren)
│ │ ├── auth/ # Login, Sessions, CSRF, Argon2id, OIDC-User-Anlage
│ │ ├── storage/ # Storage-Interface + LocalFS (atomar, SHA-256)
│ │ ├── files/ # Datei-/Ordner-CRUD, Trash, Versionen, Favoriten
│ │ ├── sharing/ # Öffentliche Share-Links
│ │ ├── admin/ # Admin-Panel: User, Gruppen(-mitgliedschaft), Settings/Theming
│ │ ├── oidc/ # OIDC-Login (optional, via .env)
│ │ ├── avatar/ # Profilbild: zuschneiden, ablegen, ausliefern
│ │ ├── webdav/ # WebDAV-Endpoint /dav/ (Basic Auth – nur App-Passwörter)
│ │ ├── acl/ # Interne Freigaben an User/Gruppen (Rechteauflösung)
│ │ ├── audit/ # Audit-Log (Schreibpfad; Endpunkte liegen in admin/)
│ │ ├── gc/ # Blob-GC: Platte gegen DB abgleichen
│ │ ├── thumbnails/ # Vorschaubild-Generierung
│ │ ├── groups/ # LEERER STUB – Gruppenordner liegen in files/groupfolder.go
│ │ ├── documents/ # Volltext: OCR-Warteschlange + Arbeiter (Phase 6, Teil 1)
│ │ ├── tags/ # Ordnungsmerkmale einer Datei: Etiketten, Belegdatum, Absender
│ │ └── office/ # WOPI-Host: Collabora/OnlyOffice, ein Adapter fuer beide
│ ├── db/migrations/ # SQL-Migrations – Spiegelkopie (siehe Migrations-Regeln!)
│ └── pkg/ # Hilfspakete: config, middleware, response, db (embed migrations)
├── frontend/ # React + TS + Vite + Tailwind
│ └── src/
│ ├── components/
│ ├── pages/
│ ├── api/ # Typed API-Client
│ └── hooks/
├── build/ # Dockerfile + Compose zum Bauen aus dem Quelltext
└── CLAUDE.md
```
## Backend-Konventionen (Go)
- **Module-Pfad**: `aether`
- **Go-Version**: 1.25 (siehe `go.mod`)
- **Router**: `github.com/go-chi/chi/v5`
- **DB-Treiber**: `github.com/jackc/pgx/v5` (pgxpool für Connection-Pool)
- **Migrations**: `github.com/golang-migrate/migrate/v4`
- **Logging**: `github.com/rs/zerolog` – strukturiert, immer mit Kontext (`log.Ctx(ctx)`)
- **Hashing**: `golang.org/x/crypto/argon2` (Argon2id) – App-Passwörter dagegen SHA-256 (zufälliges Token, jeder WebDAV-Request authentifiziert einzeln)
- **Sessions**: `github.com/gorilla/sessions` mit securecookie
### Paket-Regeln
- Alle Domänen-Pakete unter `internal/` – kein Import von außen
- Keine zirkulären Abhängigkeiten; Dependency-Richtung: `cmd → internal/* → pkg/*`
- Handler in `internal/<domain>/handler.go`, Business-Logic in `internal/<domain>/service.go`, DB in `internal/<domain>/store.go`
- Interfaces für alle externen Abhängigkeiten (Storage, DB) → leicht mockbar
### Code-Stil
- `gofmt` + `goimports` – immer formatiert
- Fehler nie ignorieren (`_`), immer wrappen mit `fmt.Errorf("context: %w", err)`
- Keine Magic-Numbers – Konstanten mit sprechenden Namen
- HTTP-Handler signieren: `func (h *Handler) MethodName(w http.ResponseWriter, r *http.Request)`
- Middleware-Chain: Auth → CSRF → RateLimit → Handler
### Tests
- Testdatei neben der Quelldatei: `auth_test.go` neben `auth.go`
- Integrationstests mit echtem Postgres via `testcontainers-go`
- Pflicht-Tests: Path-Traversal, Upload-Abbruch, Checksum-Verifikation, Soft-Delete-Recovery
- `go test ./...` muss grün sein vor jedem Commit
## Frontend-Konventionen (React + TS)
- **Bundler**: Vite
- **Styling**: Tailwind CSS + CSS-Variablen für Design-Tokens
- **Testing**: Vitest + Testing Library; E2E: Playwright
- **API-Client**: typed fetch-Wrapper unter `src/api/`
### Design-Tokens (CSS-Variablen)
Alle Farben laufen über CSS-Custom-Properties:
```css
:root {
--color-primary: ...;
--color-accent: ...;
--color-background: ...;
--color-surface: ...;
--color-text: ...;
}
```
Admin setzt globale Tokens; User-Override nur für eigene Palette (Scope: `[data-user-theme]`).
### Komponenten-Regeln
- Komponenten in `PascalCase.tsx`
- Kein `dangerouslySetInnerHTML` mit User-Input (XSS)
- API-Calls nur über `src/api/`-Funktionen, nie direkt `fetch` in Komponenten
## Sicherheits-Pflichten
- Path-Traversal: jeden Dateipfad kanonisieren und gegen Storage-Root prüfen
- CSRF: Double-Submit-Cookie oder synchronizer-Token
- Rate-Limiting: Login-Endpunkt + Admin-Login strenger
- Auth-Check: jede Route serverseitig prüfen – nie Frontend vertrauen
- SQL: ausschließlich parametrisierte Queries (pgx unterstützt das nativ)
- Uploads: außerhalb Webroot, keine ausführbaren Rechte, MIME-Prüfung
- WebDAV: **nur App-Passwörter**, nie das Kontopasswort – es ist zugleich der Admin-Zugang
- Kein Zugriff auf fremde IDs → `404`, nicht `403`: sonst verrät die Antwort die Existenz
- Listen und Suche schneiden den erreichbaren Bereich **in der Abfrage** zu, nie nachträglich im Handler
- Hochgeladene Dateien nur für eine Positivliste harmloser Typen `inline` ausliefern (sonst läuft fremdes HTML im eigenen Ursprung)
## Datenintegrität (nicht verhandelbar)
1. **Atomares Schreiben**: Temp-Datei → `fsync` → `rename`. Niemals in-place.
2. **SHA-256**: beim Upload berechnen + in DB; beim Download verifizieren.
3. **Soft-Delete only**: kein hartes Löschen. Bytes erst per GC entfernen.
4. **DB-Commit nach Blob-Write**: Metadaten erst committen, wenn Blob erfolgreich.
## Migrations-Regeln
- Dateien: `000001_init.up.sql` / `000001_init.down.sql`
- **Jede Migration in BEIDE Verzeichnisse legen:** `backend/pkg/db/migrations/` (wird ins Binary embedded und beim Start ausgeführt) UND `backend/db/migrations/` (Spiegelkopie). Beide müssen identisch sein.
- Keine destruktiven Änderungen ohne explizites Down-Migration
- Migration beim App-Start automatisch ausführen (via `migrate.Up()`)
- Aktueller Stand: bis `000019_user_avatar` (Profilbild eines Kontos)
## Commit-Regeln
Format: `<typ>(<scope>): <kurze Beschreibung>`
Typen: `feat`, `fix`, `test`, `refactor`, `docs`, `chore`, `security`
Beispiele:
- `feat(auth): add argon2id password hashing`
- `test(storage): add checksum verification test`
- `security(admin): enforce server-side admin check on all routes`
Kein Commit ohne grüne Tests.
## Reverse Proxy & Proxy-Awareness
- **Kein Proxy-Container** in docker-compose.yml – TLS und Domain gehören zum Wirt,
nicht ins Projekt. Nichts hier soll einen mitgelieferten Proxy voraussetzen.
- App lauscht auf reinem HTTP (`APP_LISTEN_ADDR`). TLS = externer Proxy.
- `X-Forwarded-For` / `X-Forwarded-Proto` werden **nur** von `TRUSTED_PROXIES` (IPs/CIDRs) akzeptiert.
- Echte Client-IP immer über `middleware.RealIP(r)` abrufen – niemals `r.RemoteAddr` direkt.
- `Secure`-Cookie-Flag: über `middleware.IsHTTPS(r)` setzen (berücksichtigt Forwarded-Proto).
- `PUBLIC_URL` für OIDC-Redirects, WOPI-Callbacks und Share-Links verwenden.
- HSTS gehört auf den Edge-Proxy, **nicht** in die App-Header.
## Docker & Secrets
- **Backend immer via Docker starten/laufen lassen (`docker compose -f build/docker-compose.yml ...`), nie `go run`.** Der Host hat kein Go-Toolchain installiert; Builds/Compile-Checks laufen über das `golang:1.25-alpine`-Image. Migrations + DB-Verbindung sind auf die Compose-Umgebung ausgelegt.
- Secrets **nur** über `.env` / Compose-Env – nie im Image oder Repo
- Multi-Stage-Build: Builder-Stage (Go-Compiler) → Alpine final
- `.env` ist in `.gitignore`; `.env.example` zeigt alle Keys ohne Werte
## Phasen-Stopp
Nach Phase 1: **STOPP** – Zusammenfassung + Phase-2-Vorschlag, erst nach OK weitermachen.