Files
rustycms/CLAUDE.md

6.6 KiB
Raw Permalink Blame History

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 12 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 24 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

./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.rsexpand_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

Asset-/Bild-URL-Felder (generisch halten)

  • Keine Heuristik: Die Admin-UI soll String-Felder nicht anhand von Feldname oder Beschreibung (z.B. „Image URL“) als Bild-URL interpretieren. Das wäre nicht generisch.
  • Explizites Widget: Bildvorschau + Asset-Picker nur anzeigen, wenn im Schema für das Feld explizit widget: "imageUrl" (oder "assetUrl") gesetzt ist. Implementierung: ContentForm.tsx Entscheidung nur über def.widget === "imageUrl", nicht über isImageUrlField() mit Name/Description.
  • Typ img: Bleibt generisch (kein Widget auf src) → normales Textfeld. Wo gewollt, kann ein eigener Typ oder ein Feld mit widget: "imageUrl" die Bild-UI bekommen.

Axum Routing

Literale Routen haben Vorrang vor Wildcard-Routen — Reihenfolge egal, Axum löst korrekt auf:

.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())