10 Commits
Author SHA1 Message Date
ki 7a105d8e45 fix(db): lock init_db() to prevent "table already exists" race
CI / test (push) Has been cancelled
Beim Cold-Start eines Containers mit persistentem Volume (DB ist nicht
leer) liefen die gunicorn-Worker parallel in init_db() und beide
versuchten CREATE TABLE. Einer crashed mit 'table genres already exists',
der andere startete.

Symmetrisch zum seed-Lock: init_db() jetzt auch in FileLock gewrapped.
Zusaetzlich *.init.lock in .gitignore.

Verifiziert mit 2 Workern + bestehender DB: beide Container starten
sauber, kein Crash, 8 items erhalten.
v0.5.3-beta
2026-07-22 00:52:54 +02:00
ki 9a4909f083 chore(rebrand): rename WatchStack -> Quivio
CI / test (push) Has been cancelled
Quivio (Kofferwort aus Quire + Video) ersetzt den Arbeitstitel WatchStack.
Es ist origineller, frei auf PyPI/npm, und passt perfekt zur Doppelnatur
der App: Buecher (Quire) + Serien (Video).

Geaendert:
- Python-Paket app/ -> quivio/ (+ alle Imports angepasst)
- DB-File watchstack.db -> quivio.db
- Docker-Image: quivio:0.5.1-beta, Container-Name quivio
- Compose-Services: quivio, quivio-local
- ENV-Vars: QUIVIO_VERSION, QUIVIO_PORT, Volume quivio-data
- Frontend-Logo "W" -> "Q", Titel "Quivio", Footer
- Favicon "Q" (Georgia serif fuer klassischen Look)
- Logger-Name: watchstack -> quivio
- README, NOTICE, dist/README, data/README: Quivio
- CI-Workflow: testet quivio/ Pfade
- build-and-push.sh: lokaler Tag "quivio:VERSION"
- service-Feld in /api/health: quivio
- OpenAPI title: Quivio

Bugfix (gefunden beim Renaming):
- seed.py hatte versteckten "from app.database import DB_PATH" — gefixt

Verifiziert end-to-end:
- Python-Import OK
- Backend: service=quivio, version=0.5.1-beta, 8 unique items
- Docker-Build OK, Push in Gitea-Registry OK (neuer sha256-Digest)
- Container: Quivio 0.5.1-beta ready (logger), HTML ohne WatchStack-Rest
- OpenAPI title=Quivio
- 8 unique items nach cold start (Race-Fix haelt)
2026-07-22 00:42:09 +02:00
ki fe291ba8ec chore(compose): pull image from Gitea Container Registry
CI / test (push) Has been cancelled
Default: Image wird aus git.pkop.de/vibecode/watchlist:<version>
gepullt. Version via WATCHSTACK_VERSION ueberschreibbar, liest sonst
aus app/__init__.py.

Opt-in Profile "local" baut das Image lokal aus dem Dockerfile
(Dev-Workflow, Port 8001 damit Registry-Variante parallel laeuft).

docker compose up -d                       # Registry-Image
docker compose --profile local up -d --build  # lokal
WATCHSTACK_VERSION=0.6.0 docker compose pull && up -d

Verifiziert: pull rc=0, up rc=0, health -> version=0.5.1-beta.
2026-07-22 00:16:32 +02:00
ki dbb92cb1ef chore(release): v0.5.1-beta — push Docker image to Gitea Container Registry
CI / test (push) Has been cancelled
- bin/build-and-push.sh: baut + pusht Image in die Gitea-Registry
  unter git.pkop.de/vibecode/watchlist:<version>
- bin/get-version.py: liest __version__ aus app/__init__.py
  (single source of truth, kein Quoting-Wahnsinn in Bash)
- bin/build-image.sh + write-manifest.sh nutzen den neuen Helper
- dist/README.md: erklärt die Registry-Policy (kein Tar im Repo)
- load-image.sh entfernt — Tars gibt's nicht mehr
- .gitignore: dist/*.tar + manifest.json ausschließen,
  dist/README.md bleibt dokumentiert
- docker-compose.yml: Hinweis auf Registry-Image
- CI-Workflow: vereinfacht, Push läuft lokal (idempotent)
- Version auf 0.5.1-beta gebumpt

Verifiziert end-to-end:
  bin/build-and-push.sh          -> Image in Gitea-Registry
  docker rmi ...                 -> lokal weg
  docker pull ...                -> aus Registry gezogen
  docker run ...                 -> startet, version 0.5.1-beta, 8 unique Items
2026-07-22 00:00:43 +02:00
ki 8c6faf984f chore: ignore *.seed.lock files
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled
2026-07-21 23:42:11 +02:00
ki ccf90d3e46 fix(seed): prevent duplicate seeding under multi-worker gunicorn
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled
When the beta image runs with --workers > 1 (default: 2), every worker
calls lifespan() in parallel. Each worker sees an empty DB, then
seed_if_empty() runs concurrently, producing 2x (or Nx) demo entries.

Fix: wrap the seed operation in a process-wide FileLock. Even if a
worker loses the race it re-checks the table count inside the critical
section and returns without re-seeding.

Verified by cold-starting a 4-worker container: 8 items (was: 32).
2026-07-21 23:40:57 +02:00
ki 0bca2eaaa2 chore: beta release v0.5.0-beta
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled
- Bump version to 0.5.0-beta
- Add Apache-2.0 LICENSE (official text) + NOTICE
- Add Docker support: multi-stage Dockerfile + docker-compose
  * non-root user, tini PID 1, gunicorn + uvicorn workers
  * healthcheck, OCI labels, persistent /data volume
- Add GitHub Actions CI (lint/import-check + Docker smoke-test)
- Backend polish:
  * modern lifespan handler (replaces deprecated on_event)
  * explicit sort validation (400 instead of 500)
  * title min_length=1 schema validation
  * configurable data dir + http timeout via env
  * avg_rating helper extracted (DRY)
- Frontend polish:
  * beta badge in title, footer with live version
  * loading spinner on initial fetch
  * updated README, badges, config table, roadmap
2026-07-21 23:34:01 +02:00
ki 56a8d5d302 fix(crud): set end_date for books too when progress hits total
Previously only series received an end_date when progress reached
max. Books now behave consistently.
2026-07-21 23:17:50 +02:00
ki a73e28e51a Add run.sh quickstart script 2026-07-21 23:12:33 +02:00
ki 67f09363f3 Initial commit: WatchStack v0.1.0
- FastAPI + SQLite backend with REST API
- Polymorphic Media model (book + series)
- Cover lookup via Open Library
- Vanilla HTML/CSS/JS frontend (MAL-inspired)
- Dark theme, status tabs, drawer detail, stats modal
- 8 seed demo entries
2026-07-21 23:11:14 +02:00