RustyCMS: file-based headless CMS — API, Admin UI (content, types, assets), Docker/Caddy, image transform; only demo type and demo content in version control
Made-with: Cursor
This commit is contained in:
118
CLAUDE.md
Normal file
118
CLAUDE.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 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.
|
||||
|
||||
---
|
||||
|
||||
## 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()`)
|
||||
Reference in New Issue
Block a user