Add Docker deployment and per-player secret-link viewers.

Each player gets an isolated SQLite viewer via a unique URL without login, with landing page warnings to save the link and compose-based hosting for sharing with others.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-19 16:06:13 +02:00
parent fbc2deec45
commit f51f166fa1
14 changed files with 589 additions and 53 deletions
+61 -13
View File
@@ -8,7 +8,8 @@ Lokaler Web-Viewer für Backups des Android-Spiels **Idle Fantasy**. Parst `fant
- **Inventar** mit Textsuche, Kategorie-Filtern, Sortierung und gruppierten Tabellen
- **SQLite-Verlauf** mehrere Backups importieren, Snapshots vergleichen, Coins-/Level-Charts
- **Import** per CLI oder Upload im Browser
- Läuft nur lokal (`127.0.0.1`)
- **Multi-User** ohne Login jeder Spieler erhält einen eigenen Viewer über einen geheimen Link
- **Docker** für Betrieb auf einem Server
- **i18n** Englisch als Standard/Fallback, Deutsch optional; automatische Browser-Sprache oder manuelle Auswahl in der Sidebar
## Voraussetzungen
@@ -32,7 +33,31 @@ pip install -r requirements.txt
python app.py fantasyidler_save.json
```
Der Browser öffnet sich automatisch unter `http://127.0.0.1:5000`.
Der Browser öffnet sich automatisch unter `http://127.0.0.1:5000/v/local/`.
### Docker (für andere Spieler hosten)
```powershell
docker compose up -d --build
```
Der Viewer ist dann unter `http://localhost:5000` erreichbar:
1. Startseite → **Meinen Viewer erstellen**
2. Persönlichen Link speichern (Bookmark) **ohne Link sind die Daten nicht wiederherstellbar** (kein Login)
3. Backups im Browser importieren
Daten liegen im Docker-Volume `viewer-data` (`/data/viewers/<id>.db`).
```powershell
# Logs
docker compose logs -f
# Stoppen
docker compose down
```
Umgebungsvariable `DATA_DIR` (Standard in Docker: `/data`) legt den Speicherort fest.
### Weitere Optionen
@@ -43,13 +68,31 @@ python app.py --import backup2.json
# Anderen Port, Browser nicht öffnen
python app.py fantasyidler_save.json --port 8080 --no-browser
# Eigene SQLite-Datenbank
# Eigene SQLite-Datenbank (Legacy, ein Datei-Modus)
python app.py --db data\meine_history.db fantasyidler_save.json
# Server für Netzwerk/Docker binden
python app.py --host 0.0.0.0 --no-browser
```
### Backups im Browser importieren
Im Tab **Inventar** (Sidebar unten): **Backup importieren** wählt eine `.json`-Datei. Duplikate (gleicher Datei-Hash) werden übersprungen.
Sidebar unten: **Backup importieren** wählt eine `.json`-Datei. Duplikate (gleicher Datei-Hash) werden übersprungen.
## Multi-User (ohne Login)
Jeder Viewer hat eine eigene SQLite-Datenbank unter `data/viewers/<viewer_id>.db`.
| Route | Beschreibung |
|-------|--------------|
| `GET /` | Startseite neuen Viewer anlegen |
| `POST /api/viewers` | Erstellt Viewer, liefert `{ viewer_id, url }` |
| `GET /v/<viewer_id>/` | Persönliches Dashboard |
| `GET /v/<viewer_id>/api/...` | API für diesen Viewer |
Die `viewer_id` ist ein zufälliges Token (URL-safe). Wer den Link kennt, hat Zugriff es gibt kein Passwort und keine Wiederherstellung bei verlorenem Link.
Lokale CLI-Nutzung nutzt standardmäßig den Viewer `local` (`/v/local/`).
## Sprache / i18n
@@ -64,28 +107,33 @@ Im Tab **Inventar** (Sidebar unten): **Backup importieren** wählt eine `.js
```
idle-fantasy-viewer/
├── app.py # Flask-Server und CLI
├── viewers.py # Viewer-IDs und Isolation
├── parser.py # Save parsen und normalisieren
├── categories.py # Item-Kategorien (Heuristiken)
├── db.py # SQLite Snapshots, Diff, Timeline
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── static/
│ ├── i18n.js # Locale-Laden, t(), Fallback en
│ ├── locales/ # en.json, de.json
│ ├── landing.js # Startseite
│ └── app.js # Dashboard-UI
├── templates/ # HTML
└── data/ # history.db (wird angelegt, gitignored)
└── data/ # viewers/*.db (gitignored)
```
## API (lokal)
## API
| Endpunkt | Beschreibung |
|----------|--------------|
| `GET /` | Dashboard |
| `GET /api/snapshot/latest` | Neuester normalisierter Save |
| `GET /api/snapshots` | Alle Snapshots |
| `GET /api/snapshots/<älter>/diff/<neuer>` | Vergleich zweier Backups |
| `GET /api/timeline` | Zeitreihe für Charts |
| `POST /api/import` | JSON-Upload oder `{"path": "..."}` |
| `GET /` | Startseite |
| `POST /api/viewers` | Neuen Viewer erstellen |
| `GET /v/<id>/api/snapshot/latest` | Neuester Save des Viewers |
| `GET /v/<id>/api/snapshots` | Alle Snapshots |
| `GET /v/<id>/api/snapshots/<älter>/diff/<neuer>` | Vergleich |
| `GET /v/<id>/api/timeline` | Zeitreihe für Charts |
| `POST /v/<id>/api/import` | JSON-Upload |
## Save-Format
@@ -93,7 +141,7 @@ Die Backup-Datei enthält u. a. doppelt JSON-kodierte Felder (`skillLevels`, `in
## Hinweise
- `data/history.db` speichert importierte Snapshots lokal; nicht mit ins Repo committen (steht in `.gitignore`).
- `data/viewers/` speichert pro Spieler eine SQLite-Datei; nicht mit ins Repo committen (steht in `.gitignore`).
- Der Viewer ist ein inoffizielles Hilfstool, nicht mit dem Spiel verbunden.
## Robustheit bei Spiel-Updates