# RustyCMS – AI Context > **Für AI-Tools**: Diese Datei (CLAUDE.md) wird von Claude Code gelesen. > Cursor liest `.cursor/rules/project.mdc`. > Beide Dateien haben identischen Inhalt — beim Ändern bitte **beide** aktualisieren. --- ## Arbeitsweise: Erklärungen beim Implementieren Beim Umsetzen von Aufgaben soll die AI **kurz und nachvollziehbar erklären, was sie tut**: 1. **Ziel und Vorgehen**: Zu Beginn (oder im ersten Schritt) in 1–2 Sätzen sagen, **was** umgesetzt wird und **wie** (welche Dateien, welches Muster). 2. **Schritte sichtbar machen**: Bei mehreren Änderungen z. B. kurz benennen: „Als Nächstes: …“, „Dann: …“, damit der Ablauf klar ist. 3. **Ungewöhnliche Entscheidungen begründen**: Wenn etwas von Konventionen abweicht (andere Lib, anderer Ordner, Workaround), **warum** (z. B. „Damit Tailwind-Klassen zuverlässig greifen“). 4. **Am Ende zusammenfassen**: In 2–4 Sätzen: Was wurde geändert, wo es liegt, was der Nutzer prüfen kann. Die Erklärungen sollen **knapp** bleiben, aber so formuliert sein, dass jemand den Gedankengang und die Änderungen nachvollziehen kann, ohne alles selbst zu lesen. --- ## Projektstruktur ``` rustycms/ ├── src/ # Rust API │ ├── api/ │ │ ├── handlers.rs # AppState + alle Content-Handler │ │ ├── response.rs # format_references, expand/collapse_asset_urls │ │ ├── assets.rs # Asset-Endpoints (Upload, Serve, Delete, Folders) │ │ └── routes.rs # Axum-Router │ ├── schema/ │ │ └── validator.rs # normalize_reference_arrays (single + array refs) │ └── store/ # FileStore / SqliteStore ├── types/ # Schema-Definitionen (*.json5) ├── content/ # Inhalte als JSON5-Dateien │ └── assets/ # Bild-Assets (mit Unterordnern) └── admin-ui/ # Next.js Admin UI (Port 3001) ├── src/components/ # React-Komponenten ├── src/lib/api.ts # API-Client └── messages/ # i18n (en.json, de.json) ``` ## Dev starten ```bash ./dev.sh # API (Port 3000) + Admin UI (Port 3001) cd admin-ui && npm run dev # nur Admin UI cargo run # nur API ``` ## Konfiguration Alle Optionen per Env-Variable (`.env.example` als Vorlage) oder CLI-Flag: | Env-Variable | CLI-Flag | Default | Zweck | |---|---|---|---| | `RUSTYCMS_TYPES_DIR` | `--types-dir` | `./types` | Schema-Definitionen | | `RUSTYCMS_CONTENT_DIR` | `--content-dir` | `./content` | Content-Dateien | | `RUSTYCMS_BASE_URL` | — | `http://host:port` | Öffentliche API-URL (für Asset-URLs) | | `RUSTYCMS_API_KEY` | — | unset | Auth für POST/PUT/DELETE | | `RUSTYCMS_LOCALES` | — | unset | z.B. `de,en` (erstes = Default) | | `RUSTYCMS_STORE` | — | `file` | `file` oder `sqlite` | | `RUSTYCMS_CORS_ORIGIN` | — | `*` | Erlaubte CORS-Origin | | `RUSTYCMS_CACHE_TTL_SECS` | — | `60` | Response-Cache TTL (0 = aus) | ## Asset-URL-Strategie Assets werden immer mit **relativem Pfad** auf Disk gespeichert: ``` src: "/api/assets/ordner/datei.jpg" ``` Die API expandiert beim Ausliefern automatisch abhängig vom Kontext: - **GET mit `_resolve`** (Frontend-Consumer): relativ → `https://api.example.com/api/assets/...` - **GET ohne `_resolve`** (Admin UI bearbeitet): Pfad bleibt relativ - **POST/PUT** (Speichern): absolute URLs werden vor dem Schreiben kollabiert Implementierung: `src/api/response.rs` → `expand_asset_urls()` / `collapse_asset_urls()` ## Collection-Hierarchie (wichtig!) | Collection | Zweck | Pflichtfelder | |---|---|---| | `img` | Rohes Bild-Asset | `src` (relativer Pfad), `description` | | `image` | Layout-Komponente | `name`, `img` (Referenz auf `img`-Collection) | **Regel:** `postImage` → referenziert `img` direkt. `row1Content` / `row2Content` etc. → erwarten `image`-Einträge, **nicht** `img`. Workflow beim Hinzufügen eines Bildes zu einer Content-Row: 1. `img`-Eintrag erstellen (`src: "/api/assets/..."`) 2. `image`-Eintrag erstellen (referenziert den `img`-Eintrag) 3. `image`-Slug in `rowXContent` des Posts eintragen ## Referenz-Handling ### ReferenceOrInlineField (Admin UI) - `value` ist String → Reference-Modus (Slug-Picker) - `value` ist Objekt ohne `_slug` → Inline-Modus (Felder direkt eingeben) - `value` ist Objekt mit `_slug` → aufgelöste Referenz vom API → wird automatisch zu Reference-Modus normalisiert Normalisierung: `admin-ui/src/components/ReferenceOrInlineField.tsx` via `normalizedValue` memo. ### Normalisierung beim Schreiben (Rust) `validator::normalize_reference_arrays()` konvertiert vor dem Speichern: - Einzelne `reference`/`referenceOrInline`-Felder: `{_slug: "foo", ...}` → `"foo"` - Array-Items: gleiche Normalisierung per Element - Aufgerufen in `create_entry` und `update_entry` ## Admin UI Konventionen - **i18n**: next-intl, Cookie-basiert. Neue Keys immer in **beiden** Dateien eintragen: `admin-ui/messages/en.json` + `messages/de.json` - **Tailwind v4**: `@plugin "tailwindcss-animate"` (nicht `@import`) - **Kein Dark Mode**: `@media (prefers-color-scheme: dark)` wurde entfernt, keine `dark:`-Klassen - **Scroll**: `html, body { height: 100%; overflow: hidden }` — Sidebar scrollt unabhängig von der Seite - **Neue Komponente**: immer prüfen ob Übersetzungs-Namespace in beiden Message-Dateien vorhanden ## Axum Routing Literale Routen haben Vorrang vor Wildcard-Routen — Reihenfolge egal, Axum löst korrekt auf: ```rust .route("/api/assets/folders", get(...).post(...)) // matcht vor: .route("/api/assets/*path", get(...).delete(...)) ``` ## Rust-spezifisch - `clap` braucht Feature `"env"` für Env-Var-Support bei CLI-Args - `AppState` in `src/api/handlers.rs` — alle neuen geteilten Ressourcen hier hinzufügen - Hot-Reload: Änderungen in `types/` werden automatisch geladen (kein Neustart nötig) - Cache wird bei Schreib-Operationen invalidiert (`cache.invalidate_collection()`)