All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Features: - Gallery upload, delete, reorder (drag & drop) - Preview images in admin panel - Same upload flow as events (WebP conversion via sharp) - Complete system documentation explaining: - Architecture (Frontend/Backend/Volume) - Upload flow (Multipart → Sharp → WebP → Storage) - Why SQLite works on Fly.io (persistent volumes) - Troubleshooting guide Admin now has full CRUD for both Events and Gallery!
221 lines
6.3 KiB
Markdown
221 lines
6.3 KiB
Markdown
# 🎯 Gallus Pub CMS - System-Erklärung
|
|
|
|
## 📐 Architektur-Überblick
|
|
|
|
```
|
|
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
│ Frontend │ │ Backend CMS │ │ Fly.io Volume │
|
|
│ (Astro SSG) │◄────────┤ (Fastify API) │◄────────┤ /app/data/ │
|
|
│ gallus-pub.ch │ fetch │ cms.gallus-pub.ch│ mount │ - SQLite DB │
|
|
│ │ │ │ │ - images/ │
|
|
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
```
|
|
|
|
## 🔄 Wie funktioniert der Upload-Flow?
|
|
|
|
### 1. **Admin lädt Bild hoch** (admin.astro)
|
|
```
|
|
User wählt Bild → uploadImage() → POST /api/gallery/upload
|
|
```
|
|
|
|
### 2. **Backend verarbeitet Upload** (backend/src/routes/gallery.ts)
|
|
```javascript
|
|
// Line 60-134 in gallery.ts
|
|
1. Empfange Multipart-File
|
|
2. Validiere Mimetype (nur images/*)
|
|
3. Lese Stream in Buffer
|
|
4. sharp() konvertiert zu WebP:
|
|
- Auto-rotate (EXIF)
|
|
- Resize auf max 1600px
|
|
- WebP quality 82%
|
|
5. Speichere in /app/data/images/gallery/
|
|
6. Erstelle DB-Eintrag mit imageUrl
|
|
7. Return imageUrl an Frontend
|
|
```
|
|
|
|
### 3. **Bilder werden serviert** (backend/src/index.ts)
|
|
```javascript
|
|
// Line 63-69
|
|
fastifyStatic → /static/ → /app/data/
|
|
```
|
|
|
|
Beispiel:
|
|
- Bild liegt in: `/app/data/images/gallery/miyma9zc-8he1di.webp`
|
|
- URL ist: `/images/gallery/miyma9zc-8he1di.webp`
|
|
- Wird serviert als: `https://cms.gallus-pub.ch/static/images/gallery/miyma9zc-8he1di.webp`
|
|
|
|
### 4. **Frontend zeigt Bilder** (src/pages/index.astro)
|
|
```javascript
|
|
// Line 30-43
|
|
1. Fetch von /api/gallery/public
|
|
2. Map imageUrl: `${API_BASE}${img.imageUrl}`
|
|
3. Result: https://cms.gallus-pub.ch/static/images/gallery/xyz.webp
|
|
```
|
|
|
|
## 💾 Warum funktioniert SQLite mit Fly.io?
|
|
|
|
**Fly.io Volumes** sind persistente Speicher:
|
|
- Gemountet als: `/app/data/`
|
|
- Konfiguration in: `backend/fly.toml` (Line 40-42)
|
|
- Bleibt bei Restarts/Deploys erhalten
|
|
|
|
```toml
|
|
[mounts]
|
|
source = "gallus_data"
|
|
destination = "/app/data"
|
|
```
|
|
|
|
### Was liegt wo?
|
|
|
|
```
|
|
/app/data/
|
|
├── gallus_cms.db # SQLite Datenbank
|
|
├── gallus_cms.db-wal # Write-Ahead Log
|
|
├── gallus_cms.db-shm # Shared Memory
|
|
└── images/
|
|
├── events/
|
|
│ ├── event_karaoke.webp
|
|
│ └── ...
|
|
└── gallery/
|
|
├── Gallery1.webp
|
|
└── ...
|
|
```
|
|
|
|
## 🔒 Datenfluss im Detail
|
|
|
|
### Event erstellen:
|
|
```
|
|
1. Admin: Bild auswählen + Formular ausfüllen
|
|
2. uploadImage(file) → /api/gallery/upload
|
|
↓
|
|
3. Backend:
|
|
- Sharp konvertiert → WebP
|
|
- Speichert in /app/data/images/gallery/
|
|
- Returnt: { image: { imageUrl: "/images/gallery/xyz.webp" } }
|
|
↓
|
|
4. Admin: POST /api/events
|
|
Body: { title, date, description, imageUrl: "/images/gallery/xyz.webp" }
|
|
↓
|
|
5. Backend:
|
|
- INSERT INTO events (imageUrl = "/images/gallery/xyz.webp")
|
|
↓
|
|
6. Frontend: GET /api/events/public
|
|
- Fetcht Events aus DB
|
|
- Mapped imageUrl zu voller URL
|
|
- Zeigt an: <img src="https://cms.gallus-pub.ch/static/images/gallery/xyz.webp">
|
|
```
|
|
|
|
### Warum separate Uploads für Gallery?
|
|
|
|
Events nutzen den Gallery-Upload, weil:
|
|
- Beide brauchen WebP-Konvertierung
|
|
- Beide nutzen gleichen Storage
|
|
- Vermeidet Code-Duplikation
|
|
- Gallery kann eigenständige Bildergalerie haben
|
|
|
|
## 🎨 Admin-Panel Features
|
|
|
|
### Events-Verwaltung:
|
|
- ✅ Event erstellen (mit Bild-Upload)
|
|
- ✅ Events auflisten
|
|
- ✅ Events löschen
|
|
- ✅ Reihenfolge ändern (Drag & Drop)
|
|
- ✅ Events veröffentlichen/verstecken (isPublished)
|
|
|
|
### Gallery-Verwaltung: (NEU!)
|
|
- ✅ Bild hochladen
|
|
- ✅ Gallery auflisten
|
|
- ✅ Bilder löschen
|
|
- ✅ Reihenfolge ändern (Drag & Drop)
|
|
- ✅ Bilder veröffentlichen/verstecken (isPublished)
|
|
|
|
### Publish:
|
|
- Git-Integration (für statische Seiten-Updates)
|
|
- Commit & Push zu Repository
|
|
|
|
## 🚀 Deployment-Prozess
|
|
|
|
### Was passiert beim Deploy?
|
|
|
|
1. **Woodpecker CI** triggered bei Push zu `main`
|
|
2. **Frontend Deploy** (gallus-pub):
|
|
- Build Astro SSG
|
|
- Deploy zu Fly.io
|
|
|
|
3. **Backend Deploy** (gallus-cms-backend):
|
|
- Docker Build:
|
|
```dockerfile
|
|
# Backend-Code kompilieren
|
|
npm run build → dist/
|
|
|
|
# Migrierte Bilder einpacken
|
|
COPY backend/data/images → /app/migration-images
|
|
|
|
# Migration-Script kopieren
|
|
COPY migrate-production.js → /app/
|
|
```
|
|
- Deploy zu Fly.io
|
|
- **Volume bleibt erhalten** (SQLite DB + hochgeladene Bilder)
|
|
|
|
4. **Nach erstem Deploy**: Migration ausführen
|
|
```bash
|
|
fly ssh console -a gallus-cms-backend
|
|
node migrate-production.js
|
|
```
|
|
- Kopiert Bilder: `/app/migration-images/` → `/app/data/images/`
|
|
- Befüllt DB mit Event/Gallery-Einträgen
|
|
|
|
## 🔍 Troubleshooting
|
|
|
|
### "Bilder werden nicht angezeigt"
|
|
|
|
**Prüfe:**
|
|
1. Backend logs: `fly logs -a gallus-cms-backend`
|
|
2. Bild existiert: `fly ssh console -a gallus-cms-backend`
|
|
```bash
|
|
ls -la /app/data/images/gallery/
|
|
```
|
|
3. DB-Eintrag korrekt:
|
|
```bash
|
|
sqlite3 /app/data/gallus_cms.db
|
|
SELECT * FROM gallery_images;
|
|
```
|
|
4. Static-Route funktioniert:
|
|
```bash
|
|
curl https://cms.gallus-pub.ch/static/images/gallery/xyz.webp
|
|
```
|
|
|
|
### "SQLite locked" Fehler
|
|
|
|
- Nur ein Writer zur Zeit erlaubt
|
|
- Bei hohem Traffic: Wechsel zu PostgreSQL empfohlen
|
|
- Für Gallus Pub: ausreichend (wenig Writes)
|
|
|
|
### "Volume voll"
|
|
|
|
```bash
|
|
fly volumes list -a gallus-cms-backend
|
|
fly volumes extend <volume-id> -s <new-size>
|
|
```
|
|
|
|
## 📁 Wichtige Dateien
|
|
|
|
| Datei | Zweck |
|
|
|-------|-------|
|
|
| `backend/src/routes/gallery.ts` | Gallery-Upload & CRUD |
|
|
| `backend/src/routes/events.ts` | Events CRUD + Public API |
|
|
| `backend/src/index.ts` | Static File Serving |
|
|
| `backend/migrate-production.js` | Initiale Daten-Migration |
|
|
| `src/pages/admin.astro` | Admin-Interface |
|
|
| `src/pages/index.astro` | Frontend (fetched von API) |
|
|
| `backend/fly.toml` | Backend Fly.io Config |
|
|
| `fly.toml` | Frontend Fly.io Config |
|
|
|
|
## 🎯 Nächste Schritte
|
|
|
|
1. ✅ Gallery-Verwaltung implementiert
|
|
2. ⏳ Migration ausführen (nach Deploy)
|
|
3. ⏳ Testen: Bild hochladen → Frontend anzeigen
|
|
4. 📋 Optional: Image-Editing Features
|
|
5. 📋 Optional: Bulk-Upload für Gallery
|