README aktualisiert: Cover-DB, n8n-Workflows, aktuelle API-Endpunkte
- Port 8090 korrigiert
- Alle 4 Migrationen dokumentiert (inkl. cover_image/cover_mime_type)
- Neuen API-Endpunkt GET /records/{id}/cover ergänzt
- n8n-Workflows (Haupt + Backfill) mit Ablauf beschrieben
- Projektstruktur und deploy.sh aktualisiert
- Fehlerbehebungs-Tabelle erweitert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b5366783b1
commit
edfd7105b7
@@ -14,13 +14,13 @@ Web-Oberfläche zur Anzeige und Verwaltung von Medien-Metadaten aus **PostgreSQL
|
||||
- [Voraussetzungen](#voraussetzungen)
|
||||
- [Installation auf dem Server](#installation-auf-dem-server)
|
||||
- [Konfiguration](#konfiguration)
|
||||
- [Datenbank](#datenbank)
|
||||
- [Datenbank & Migrationen](#datenbank--migrationen)
|
||||
- [n8n Workflows](#n8n-workflows)
|
||||
- [Bedienung](#bedienung)
|
||||
- [API-Referenz](#api-referenz)
|
||||
- [Projektstruktur](#projektstruktur)
|
||||
- [Entwicklung & Updates](#entwicklung--updates)
|
||||
- [Fehlerbehebung](#fehlerbehebung)
|
||||
- [Hinweise für n8n](#hinweise-für-n8n)
|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ Die Anwendung läuft als **zwei Docker-Container** auf einem Linux-Server:
|
||||
|
||||
**Produktiv-Pfad auf dem Server:** `/opt/nginx_filebot`
|
||||
|
||||
> Auf dem Entwicklungs-PC (z. B. Mac) ist **kein** Node.js oder Python für den Betrieb nötig. Code anpassen, auf den Server kopieren, dort `docker compose up -d --build` ausführen.
|
||||
> Auf dem Entwicklungs-PC ist kein Node.js oder Python für den Betrieb nötig. Code anpassen, mit `scripts/deploy.sh` auf den Server übertragen, dort `docker compose up -d --build` ausführen.
|
||||
|
||||
Für KI-Assistenten / Projekt-Kontext: siehe [`PROMPT.md`](PROMPT.md).
|
||||
|
||||
@@ -43,29 +43,30 @@ Für KI-Assistenten / Projekt-Kontext: siehe [`PROMPT.md`](PROMPT.md).
|
||||
|
||||
### Medien-Übersicht
|
||||
|
||||
- **Cover** aus `cover_url` (z. B. TVMaze-Poster) in der ersten Spalte / auf Karten
|
||||
- **Original-** und **Neu-Dateiname** immer sichtbar
|
||||
- **Cover** – primär aus der Datenbank (`cover_image` BYTEA), Fallback auf externe URL via Proxy
|
||||
- **Original-** und **Neu-Dateiname** immer sichtbar (Rename-Kontrolle)
|
||||
- Technische Spalten: Rating, Format, Auflösung, Codecs, Bitraten, Container, Erstellungsdatum
|
||||
- **Neueste Einträge zuerst** (Sortierung nach `created_at`)
|
||||
|
||||
### Filter
|
||||
|
||||
- Volltextsuche in beiden Dateinamen
|
||||
- **Button-Filter** für Videoformat, Codec und Container (nebeneinander auf Desktop, gestapelt auf Mobil)
|
||||
- **Zeitraum** „Von / Bis“ mit Kalender und Uhrzeit (HH:MM)
|
||||
- **Button-Filter** für Typ (Movie/Episode), Videoformat, Codec und Container
|
||||
- **Zeitraum** „Von / Bis" mit Kalender und Uhrzeit (HH:MM)
|
||||
- Schnellfilter in der Tabelle (clientseitig in geladenen Daten)
|
||||
|
||||
### Gesehen-Status
|
||||
|
||||
- Ungesehene Zeilen sind **hervorgehoben** (blauer Akzent)
|
||||
- **Tippen auf eine Zeile** markiert sie als gesehen oder ungesehen
|
||||
- Filter **„Nur ungesehen“** und Aktion **„Alle als gesehen“** (für aktuelle Filterauswahl)
|
||||
- Zähler **„X neu“** im Header und in den Filtern
|
||||
- Filter **„Nur ungesehen"** und Aktion **„Alle als gesehen"** (für aktuelle Filterauswahl)
|
||||
- Zähler **„X neu"** im Header
|
||||
|
||||
### Weitere Aktionen
|
||||
|
||||
- **Löschen** einzelner Einträge (Mülleimer, mit Bestätigung)
|
||||
- Responsives Layout: **Tabelle** (Desktop) / **Karten** (Mobil, ≤900px Breite)
|
||||
- **Live-Aktualisierung** alle 5 Sekunden (pausiert im Hintergrund-Tab)
|
||||
- Responsives Layout: **Tabelle** (Desktop) / **Karten** (Mobil, ≤900px)
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +78,7 @@ Für KI-Assistenten / Projekt-Kontext: siehe [`PROMPT.md`](PROMPT.md).
|
||||
▼
|
||||
┌─────────────────┐ /api/* ┌─────────────────┐
|
||||
│ nginx (web) │ ───────────────►│ FastAPI (api) │
|
||||
│ Port 8080 │ │ Port 8000 │
|
||||
│ Port 8090 │ │ Port 8000 │
|
||||
│ Vue static │ └────────┬────────┘
|
||||
└─────────────────┘ │
|
||||
▼
|
||||
@@ -92,8 +93,6 @@ Für KI-Assistenten / Projekt-Kontext: siehe [`PROMPT.md`](PROMPT.md).
|
||||
| `web` | `nginx/Dockerfile` (Node → Nginx) | Statische Dateien, Reverse Proxy |
|
||||
| `api` | `api/Dockerfile` | REST-API, DB-Zugriff |
|
||||
|
||||
Compose-Datei nutzt **relative Pfade** – immer aus `/opt/nginx_filebot` starten.
|
||||
|
||||
---
|
||||
|
||||
## Voraussetzungen
|
||||
@@ -101,34 +100,24 @@ Compose-Datei nutzt **relative Pfade** – immer aus `/opt/nginx_filebot` starte
|
||||
- Linux-Server mit **Docker** und **Docker Compose v2**
|
||||
- Erreichbare **PostgreSQL**-Instanz (im LAN, z. B. `192.168.30.186:5432`)
|
||||
- Tabelle **`n8n.movie_files`** in Datenbank **`n8n_filebot`** (anpassbar in Config)
|
||||
- Optional: Spalten `seen_at` und `cover_url` (siehe [Datenbank](#datenbank))
|
||||
- Alle SQL-Migrationen ausgeführt (siehe [Datenbank & Migrationen](#datenbank--migrationen))
|
||||
|
||||
---
|
||||
|
||||
## Installation auf dem Server
|
||||
|
||||
Ausführliche Anleitung inkl. **SMB-Gruppe** und Umzug: [`docs/DEPLOY-NEUER-SERVER.md`](docs/DEPLOY-NEUER-SERVER.md).
|
||||
Ausführliche Anleitung inkl. SMB-Gruppe und Umzug: [`docs/DEPLOY-NEUER-SERVER.md`](docs/DEPLOY-NEUER-SERVER.md).
|
||||
|
||||
### 1. Projekt ablegen
|
||||
|
||||
```bash
|
||||
# Ein User (vogto): Rechte auf ganz /opt, nicht nur ein Unterordner
|
||||
sudo groupadd -f opt-docker
|
||||
sudo usermod -aG opt-docker,docker vogto
|
||||
sudo chown vogto:opt-docker /opt && sudo chmod 2775 /opt
|
||||
sudo mkdir -p /opt/nginx_filebot
|
||||
|
||||
# Variante A: Git
|
||||
git clone <repository-url> /opt/nginx_filebot
|
||||
|
||||
# Variante B: rsync vom Entwicklungsrechner
|
||||
# rsync -avz --exclude node_modules --exclude config/config.yaml \
|
||||
# ./ user@server:/opt/nginx_filebot/
|
||||
git clone https://git.vogt.de.com/vogto/filebot-media-browser.git /opt/nginx_filebot
|
||||
```
|
||||
|
||||
### 2. Datenbank vorbereiten
|
||||
|
||||
Siehe Abschnitt [Datenbank](#datenbank) – Migrationen ausführen.
|
||||
Alle Migrationen ausführen (siehe [Datenbank & Migrationen](#datenbank--migrationen)).
|
||||
|
||||
### 3. Konfiguration anlegen
|
||||
|
||||
@@ -149,19 +138,17 @@ docker compose up -d --build
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
curl -s http://localhost:8080/api/health
|
||||
curl -s http://localhost:8090/api/health
|
||||
# Erwartung: {"status":"ok"}
|
||||
```
|
||||
|
||||
Im Browser: **`http://<Server-IP>:8080`**
|
||||
Im Browser: **`http://<Server-IP>:8090`**
|
||||
|
||||
---
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Datei: **`/opt/nginx_filebot/config/config.yaml`** (nicht versionieren, in `.gitignore`).
|
||||
|
||||
Beispiel (produktive Werte aus dem Projekt):
|
||||
Datei: **`config/config.yaml`** (nicht versioniert, in `.gitignore`).
|
||||
|
||||
```yaml
|
||||
database:
|
||||
@@ -177,88 +164,126 @@ database:
|
||||
|
||||
| Parameter | Bedeutung |
|
||||
|-----------|-----------|
|
||||
| `name` | **Datenbank** (Catalog), nicht Tabellenname |
|
||||
| `name` | Datenbank (Catalog) |
|
||||
| `table` | Tabellenname innerhalb des Schemas |
|
||||
| `db_schema` | Schema (bei euch typisch `n8n`) |
|
||||
| `schema` | Alias für `db_schema` in YAML (optional) |
|
||||
|
||||
Die API verbindet sich mit: `"n8n"."movie_files"` (in Anführungszeichen, case-sensitiv).
|
||||
| `db_schema` | Schema (typisch `n8n`) |
|
||||
|
||||
---
|
||||
|
||||
## Datenbank
|
||||
## Datenbank & Migrationen
|
||||
|
||||
### Tabelle und Schema
|
||||
### Tabelle
|
||||
|
||||
| Einstellung | Typischer Wert |
|
||||
|-------------|----------------|
|
||||
| Einstellung | Wert |
|
||||
|-------------|------|
|
||||
| Datenbank | `n8n_filebot` |
|
||||
| Schema | `n8n` |
|
||||
| Tabelle | `movie_files` |
|
||||
| Vollqualifiziert | `"n8n"."movie_files"` |
|
||||
|
||||
Tabellen prüfen:
|
||||
### Migrationen ausführen
|
||||
|
||||
Alle SQL-Dateien im Verzeichnis `sql/` der Reihe nach ausführen:
|
||||
|
||||
```bash
|
||||
docker compose exec api python /app/scripts/list-tables.py
|
||||
cd /opt/nginx_filebot
|
||||
|
||||
for f in sql/00*.sql; do
|
||||
echo "==> $f"
|
||||
psql -h 192.168.30.186 -U n8n_filebot -d n8n_filebot -f "$f"
|
||||
done
|
||||
```
|
||||
|
||||
### Migration: `seen_at`
|
||||
| Datei | Inhalt |
|
||||
|-------|--------|
|
||||
| `001_add_seen_at.sql` | Spalte `seen_at TIMESTAMPTZ` + Index |
|
||||
| `002_add_cover_url.sql` | Spalte `cover_url TEXT` |
|
||||
| `003_add_object_type.sql` | Spalte `object_type TEXT` |
|
||||
| `004_add_cover_image.sql` | Spalten `cover_image BYTEA` + `cover_mime_type TEXT` |
|
||||
|
||||
```bash
|
||||
psql -h 192.168.30.186 -U n8n_filebot -d n8n_filebot \
|
||||
-f /opt/nginx_filebot/sql/001_add_seen_at.sql
|
||||
```
|
||||
### Gesehen-Status
|
||||
|
||||
Oder manuell:
|
||||
|
||||
```sql
|
||||
ALTER TABLE n8n.movie_files
|
||||
ADD COLUMN IF NOT EXISTS seen_at TIMESTAMPTZ NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_movie_files_seen_at
|
||||
ON n8n.movie_files (seen_at);
|
||||
```
|
||||
|
||||
- `seen_at IS NULL` → **ungesehen** (hervorgehoben in der UI)
|
||||
- `seen_at IS NULL` → **ungesehen** (blau hervorgehoben)
|
||||
- gesetzt → **gesehen**
|
||||
|
||||
### Migration: `cover_url`
|
||||
### Cover-Bilder
|
||||
|
||||
- `cover_url` – externe Poster-URL (z. B. TVMaze, OMDB)
|
||||
- `cover_image` – Bild-Binärdaten direkt in der DB (BYTEA)
|
||||
- `cover_mime_type` – MIME-Typ des gespeicherten Bildes (z. B. `image/jpeg`)
|
||||
|
||||
Die UI lädt Cover **primär aus der DB** (`/api/records/{id}/cover`), Fallback auf den URL-Proxy (`/api/cover?url=…`).
|
||||
|
||||
---
|
||||
|
||||
## n8n Workflows
|
||||
|
||||
### Workflow 1: FileBot (Haupt-Workflow)
|
||||
|
||||
Wird von Filebot per Webhook ausgelöst und schreibt Medien-Metadaten in die DB. Danach läuft alle 2 Minuten ein Schedule-Trigger, der fehlende Cover-URLs nachlädt und die Cover-Bilder herunterlädt.
|
||||
|
||||
**Ablauf:**
|
||||
|
||||
```bash
|
||||
psql -h 192.168.30.186 -U n8n_filebot -d n8n_filebot \
|
||||
-f /opt/nginx_filebot/sql/002_add_cover_url.sql
|
||||
```
|
||||
Webhook (Filebot) → Insert rows (Metadaten)
|
||||
|
||||
Schedule (alle 2 Min) → Select (cover_url IS NULL)
|
||||
→ Loop → Switch (Movie / Episode)
|
||||
Movie → HTTP omdbapi → Edit Fields → Download Cover → Prepare Data → Speichere Cover in DB
|
||||
Episode → If thetvdb_id → HTTP tvmaze → Edit Fields1 → Download Cover → Prepare Data → Speichere Cover in DB
|
||||
```
|
||||
|
||||
**Wichtig für n8n:**
|
||||
- `seen_at` und `cover_image` beim INSERT/UPDATE **nicht** überschreiben
|
||||
- `cover_url` bleibt immer erhalten (auch wenn der Bild-Download fehlschlägt)
|
||||
|
||||
---
|
||||
|
||||
### Workflow 2: Cover Backfill
|
||||
|
||||
Einmaliger / regelmäßiger Workflow zum Nachfüllen von `cover_image` für Einträge, die bereits eine `cover_url` haben, aber noch kein Bild in der DB.
|
||||
|
||||
**Ablauf:**
|
||||
|
||||
```
|
||||
Manuell / Stündlich
|
||||
→ Select (cover_image IS NULL AND cover_url IS NOT NULL)
|
||||
→ Loop (Batch 5)
|
||||
→ HTTP Download (cover_url, continueOnFail)
|
||||
→ Code (Base64 extrahieren)
|
||||
→ Execute Query (UPDATE cover_image, cover_mime_type)
|
||||
→ Loop (weiter)
|
||||
```
|
||||
|
||||
**Hinweis:** Wenn der Workflow wegen Timeout abbricht (viele Einträge), beim erneuten Ausführen macht er automatisch dort weiter, wo er aufgehört hat – der SELECT lädt nur noch Einträge mit `cover_image IS NULL`.
|
||||
|
||||
Tabellengröße prüfen:
|
||||
|
||||
```sql
|
||||
ALTER TABLE n8n.movie_files
|
||||
ADD COLUMN IF NOT EXISTS cover_url TEXT NULL;
|
||||
SELECT
|
||||
COUNT(*) AS zeilen,
|
||||
COUNT(cover_image) AS mit_bild,
|
||||
pg_size_pretty(pg_total_relation_size('"n8n"."movie_files"')) AS gesamt
|
||||
FROM n8n.movie_files;
|
||||
```
|
||||
|
||||
Beispiel-URL: `https://static.tvmaze.com/uploads/images/medium_portrait/200/502332.jpg`
|
||||
(Doppelte Anführungszeichen in der DB werden in der UI bereinigt.)
|
||||
|
||||
### Weitere Spalten
|
||||
|
||||
Die API liest alle in `api/main.py` → `COLUMNS` definierten Felder (entsprechen dem n8n/Filebot-Export). Fehlende Spalten führen zu API-Fehlern – dann Spalte in PostgreSQL ergänzen oder `COLUMNS` anpassen.
|
||||
|
||||
---
|
||||
|
||||
## Bedienung
|
||||
|
||||
### Desktop
|
||||
|
||||
1. Seite öffnen → Daten werden geladen (Status „DB verbunden“).
|
||||
1. Seite öffnen → Daten werden geladen (Status „DB verbunden").
|
||||
2. Filter oben nutzen; **Aktualisieren** lädt neu, **Zurücksetzen** löscht Filter.
|
||||
3. **Live-Aktualisierung:** Alle 5 Sekunden werden die Daten still neu geladen (ohne Seiten-Reload). Im Header: „Live · HH:MM:SS“. Im Hintergrund-Tab pausiert das Polling.
|
||||
4. **Cover:** Bilder werden über `/api/cover` geladen (Proxy), falls externe Poster (z. B. TVMaze) blockiert werden.
|
||||
5. **Zeile antippen/klicken** → Gesehen-Status umschalten.
|
||||
6. **Mülleimer** → Eintrag löschen (mit Bestätigung).
|
||||
7. Spaltenköpfe klicken zum Sortieren; Schnellfilter unter der Filterleiste.
|
||||
3. **Live-Aktualisierung:** Alle 5 Sekunden werden die Daten still neu geladen. Im Hintergrund-Tab pausiert das Polling.
|
||||
4. **Zeile antippen/klicken** → Gesehen-Status umschalten.
|
||||
5. **Mülleimer** → Eintrag löschen (mit Bestätigung).
|
||||
6. Spaltenköpfe klicken zum Sortieren; Schnellfilter unter der Filterleiste.
|
||||
|
||||
### Mobil
|
||||
|
||||
- Gleiche Logik in **Kartenform** mit Cover links.
|
||||
- Filter untereinander.
|
||||
- Filter untereinander gestapelt.
|
||||
|
||||
### Gesehen-Status
|
||||
|
||||
@@ -266,81 +291,28 @@ Die API liest alle in `api/main.py` → `COLUMNS` definierten Felder (entspreche
|
||||
|--------|--------|
|
||||
| Zeile antippen (ungesehen) | `seen_at` = jetzt |
|
||||
| Zeile antippen (gesehen) | `seen_at` = NULL |
|
||||
| „Nur ungesehen“ | Zeigt nur Einträge ohne `seen_at` |
|
||||
| „Alle als gesehen“ | Setzt `seen_at` für alle **aktuell gefilterten** Einträge |
|
||||
| „Nur ungesehen" | Zeigt nur Einträge ohne `seen_at` |
|
||||
| „Alle als gesehen" | Setzt `seen_at` für alle aktuell gefilterten Einträge |
|
||||
|
||||
---
|
||||
|
||||
## API-Referenz
|
||||
|
||||
Basis-URL hinter Nginx: `http://<host>:8080/api`
|
||||
Basis-URL: `http://<host>:8090/api`
|
||||
|
||||
### `GET /health`
|
||||
|
||||
```json
|
||||
{ "status": "ok" }
|
||||
```
|
||||
|
||||
### `GET /status`
|
||||
|
||||
Diagnose: Config-Pfad, qualifizierte Tabelle, Zeilenanzahl, bei Fehler Tabellenlisten.
|
||||
|
||||
### `GET /records`
|
||||
|
||||
Query-Parameter:
|
||||
|
||||
| Parameter | Beschreibung |
|
||||
|-----------|--------------|
|
||||
| `search` | ILIKE in `file_name_original` und `file_name_new` |
|
||||
| `format` | `standard_video_format` |
|
||||
| `codec` | `video_codec_library` |
|
||||
| `container` | `container_format` |
|
||||
| `created_from` | ISO-Datum/Zeit, `created_at >=` |
|
||||
| `created_to` | ISO-Datum/Zeit, `created_at <=` |
|
||||
| `unseen_only` | `true` → nur `seen_at IS NULL` |
|
||||
| `limit`, `offset` | Pagination (Standard limit=5000) |
|
||||
|
||||
Antwort enthält u. a. `records`, `total`, `unseen_total`.
|
||||
|
||||
### `GET /records/meta`
|
||||
|
||||
Gleiche Query-Parameter wie `GET /records` (ohne Pagination). Leichtgewichtige Antwort für Auto-Refresh:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 120,
|
||||
"unseen_total": 3,
|
||||
"max_id": 456,
|
||||
"latest_created_at": "2026-06-04T12:00:00+00:00",
|
||||
"revision": "a1b2c3…"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /cover`
|
||||
|
||||
Query: `url` (HTTP(S)-Poster-URL). Liefert das Bild über die API (umgeht Hotlink-/Referrer-Sperren).
|
||||
|
||||
### `GET /filters`
|
||||
|
||||
Distinct-Werte für Format-, Codec- und Container-Buttons.
|
||||
|
||||
### `PATCH /records/{id}/seen`
|
||||
|
||||
Body:
|
||||
|
||||
```json
|
||||
{ "seen": true }
|
||||
```
|
||||
|
||||
oder `{ "seen": false }` (setzt `seen_at` auf NULL).
|
||||
|
||||
### `POST /records/mark-all-seen`
|
||||
|
||||
Markiert alle Einträge, die zu den **gleichen Query-Filtern** wie `GET /records` passen (ohne `unseen_only`).
|
||||
|
||||
### `DELETE /records/{id}`
|
||||
|
||||
Löscht einen Datensatz anhand der `id`.
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| GET | `/health` | DB-Ping |
|
||||
| GET | `/status` | Diagnose (Config, Tabellenliste bei Fehler) |
|
||||
| GET | `/columns` | Liste aller geladenen Spalten |
|
||||
| GET | `/filters` | Distinct-Werte für Filter-Buttons |
|
||||
| GET | `/records` | Datensätze; Query: `search`, `format`, `codec`, `container`, `object_type`, `created_from`, `created_to`, `unseen_only`, `limit`, `offset` |
|
||||
| GET | `/records/meta` | Leichtgewichtiger Check für Auto-Refresh (total, unseen_total, revision) |
|
||||
| GET | `/records/{id}/cover` | Cover-Bild direkt aus DB (BYTEA); 404 wenn kein Bild gespeichert |
|
||||
| GET | `/cover?url=…` | Cover-Proxy für externe URLs (umgeht Hotlink-Sperren) |
|
||||
| PATCH | `/records/{id}/seen` | Body: `{"seen": true\|false}` |
|
||||
| POST | `/records/mark-all-seen` | Alle gefilterten Einträge als gesehen markieren |
|
||||
| DELETE | `/records/{id}` | Eintrag löschen |
|
||||
|
||||
---
|
||||
|
||||
@@ -366,48 +338,57 @@ Löscht einen Datensatz anhand der `id`.
|
||||
│ └── src/
|
||||
│ ├── App.vue
|
||||
│ ├── api.ts
|
||||
│ ├── styles.css # Theme (helles Dark-UI)
|
||||
│ ├── types.ts
|
||||
│ ├── styles.css
|
||||
│ ├── components/
|
||||
│ │ ├── DataTable.vue
|
||||
│ │ ├── MobileCards.vue
|
||||
│ │ ├── FilterBar.vue
|
||||
│ │ ├── CoverImage.vue
|
||||
│ │ ├── CoverImage.vue # DB-Bild primär, URL-Proxy als Fallback
|
||||
│ │ ├── NamePair.vue
|
||||
│ │ ├── DateTimeRangeFilter.vue
|
||||
│ │ └── DeleteButton.vue
|
||||
│ ├── composables/
|
||||
│ │ └── useAutoRefresh.ts
|
||||
│ └── utils/
|
||||
│ ├── coverUrl.ts
|
||||
│ ├── datetime.ts
|
||||
│ ├── names.ts
|
||||
│ ├── objectType.ts
|
||||
│ ├── preserveScroll.ts
|
||||
│ └── seen.ts
|
||||
├── nginx/
|
||||
│ ├── Dockerfile
|
||||
│ └── nginx.conf
|
||||
├── scripts/
|
||||
│ └── deploy.sh # Einmaliges SSH-Login, alle Dateien übertragen
|
||||
└── sql/
|
||||
├── 001_add_seen_at.sql
|
||||
└── 002_add_cover_url.sql
|
||||
├── 002_add_cover_url.sql
|
||||
├── 003_add_object_type.sql
|
||||
└── 004_add_cover_image.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entwicklung & Updates
|
||||
|
||||
### Code aktualisieren
|
||||
### Dateien auf den Server übertragen
|
||||
|
||||
```bash
|
||||
cd "/Users/vogto/Documents/ClaudeCode/Webseite Filebot"
|
||||
bash scripts/deploy.sh
|
||||
```
|
||||
|
||||
Das Script baut die Container anschließend automatisch neu.
|
||||
|
||||
### Manuell (einzelne Dateien)
|
||||
|
||||
```bash
|
||||
cd /opt/nginx_filebot
|
||||
git pull # falls Git
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Nur Frontend:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build web
|
||||
```
|
||||
|
||||
Nur API:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build api
|
||||
docker compose up -d --build # alles
|
||||
docker compose up -d --build web # nur Frontend
|
||||
docker compose up -d --build api # nur API
|
||||
```
|
||||
|
||||
### Logs
|
||||
@@ -417,19 +398,6 @@ docker compose logs -f api
|
||||
docker compose logs -f web
|
||||
```
|
||||
|
||||
### Optional: lokale Entwicklung (nicht für Produktion)
|
||||
|
||||
Nur wenn bewusst gewünscht – normaler Workflow ist Server-only.
|
||||
|
||||
```bash
|
||||
# API
|
||||
cd api && pip install -r requirements.txt
|
||||
CONFIG_PATH=../config/config.yaml uvicorn main:app --reload --port 8000
|
||||
|
||||
# Frontend (proxied in vite.config.ts nach :8000)
|
||||
cd frontend && npm install && npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fehlerbehebung
|
||||
@@ -437,19 +405,17 @@ cd frontend && npm install && npm run dev
|
||||
### API-Fehler / leere Seite
|
||||
|
||||
```bash
|
||||
cd /opt/nginx_filebot
|
||||
docker compose logs api --tail 80
|
||||
curl -s http://localhost:8080/api/status | python3 -m json.tool
|
||||
curl -s http://localhost:8090/api/status | python3 -m json.tool
|
||||
```
|
||||
|
||||
| Symptom | Ursache | Lösung |
|
||||
|---------|---------|--------|
|
||||
| `relation "n8n.n8n_filebot" does not exist` | Falscher Tabellenname | `table: "movie_files"` in config.yaml |
|
||||
| `relation "public...." does not exist` | Schema falsch | `db_schema: "n8n"` |
|
||||
| Spalte `seen_at` / `cover_url` fehlt | Migration fehlt | SQL in `sql/` ausführen |
|
||||
| `API-Fehler: 503` | DB nicht erreichbar | `nc -zv <host> 5432` vom Server und aus Container |
|
||||
| 0 Einträge | Filter zu streng / leere DB | Filter zurücksetzen, `SELECT COUNT(*)` prüfen |
|
||||
| Altes Design | Cache / kein Rebuild | `docker compose up -d --build web` + Hard-Reload |
|
||||
| `relation "..." does not exist` | Falscher Tabellenname oder Schema | `table: "movie_files"`, `db_schema: "n8n"` in config.yaml |
|
||||
| Spalte fehlt (API 500) | Migration nicht ausgeführt | SQL in `sql/` ausführen |
|
||||
| `503` | DB nicht erreichbar | `nc -zv 192.168.30.186 5432` vom Server prüfen |
|
||||
| Cover fehlt | `cover_image IS NULL` | Backfill-Workflow in n8n ausführen |
|
||||
| Altes Design | Kein Rebuild | `docker compose up -d --build web` + Hard-Reload |
|
||||
|
||||
### Netzwerk vom Container zur DB
|
||||
|
||||
@@ -460,21 +426,3 @@ s = socket.create_connection(('192.168.30.186', 5432), timeout=5)
|
||||
print('OK'); s.close()
|
||||
"
|
||||
```
|
||||
|
||||
### Port ändern
|
||||
|
||||
In `docker-compose.yml` z. B. `"80:80"` oder `"8888:80"` statt `"8080:80"`.
|
||||
|
||||
---
|
||||
|
||||
## Hinweise für n8n
|
||||
|
||||
- **INSERT:** Neue Zeilen haben `seen_at = NULL` → erscheinen als „neu“.
|
||||
- **UPDATE:** `seen_at` und `cover_url` nicht mit überschreiben, wenn der Status erhalten bleiben soll.
|
||||
- Datenbank-Workflow und Tabellenname (`movie_files`, Schema `n8n`) mit der `config.yaml` abstimmen.
|
||||
|
||||
---
|
||||
|
||||
## Lizenz / Nutzung
|
||||
|
||||
Privates Heimnetz-Projekt. Anpassungen nach Bedarf; `config.yaml` mit Passwörtern nicht in öffentliche Repositories committen.
|
||||
|
||||
Reference in New Issue
Block a user