kryten-webqueue 0.48.0__tar.gz → 0.49.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/CHANGELOG.md +50 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/PKG-INFO +1 -1
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/deploy/nginx-queue.conf +6 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/DATABASE_ARCHITECTURE_PLAN.md +23 -7
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-cutover.md +61 -3
- kryten_webqueue-0.49.0/docs/postgres-migration/HANDOFF-repository-port-status.md +510 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/PRD-postgres-migration.md +3 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/SPEC-Sortie-6-tests-cutover-release.md +9 -2
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/__init__.py +121 -22
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_catalog.py +17 -6
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_catalog_db.py +24 -0
- kryten_webqueue-0.49.0/kryten_webqueue/catalog/db/_pg_base_domain.py +157 -0
- kryten_webqueue-0.49.0/kryten_webqueue/catalog/db/_pg_catalog_db.py +1128 -0
- kryten_webqueue-0.49.0/kryten_webqueue/catalog/db/_pg_jobs_db.py +318 -0
- kryten_webqueue-0.49.0/kryten_webqueue/catalog/db/_pg_queue_db.py +782 -0
- kryten_webqueue-0.49.0/kryten_webqueue/catalog/db/_pg_users_db.py +265 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_playlists.py +7 -3
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/sql/001_initial_schema.sql +1 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/config.py +35 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/pyproject.toml +1 -1
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_multi_db_connection.py +62 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_postgres_config_and_engine.py +36 -0
- kryten_webqueue-0.49.0/tests/test_postgres_domain_crud.py +624 -0
- kryten_webqueue-0.49.0/tests/test_postgres_live.py +87 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/.github/workflows/gitleaks.yml +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/.gitignore +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/.gitleaks.toml +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/.pre-commit-config.yaml +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/AGENT-WORKFLOW-GUIDE.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/AGENTS.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/Containerfile +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/README.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/config.example.json +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/deploy/podman/webqueue/webqueue-app.container +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/deploy/podman/webqueue/webqueue.network +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/Log for incorrect promo insertion problem.txt +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/PRD_TMDB_LOCAL_INDEX.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/PUBLIC_API.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/SPEC-Visual-UX-Sprint-August-2026.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/SPEC_ADMIN_ITEM_EDITOR.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/SPEC_EMOTE_REHOST.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/SPEC_TMDB_LOCAL_INDEX.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/SPEC-Sortie-1-db-config-and-pool.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/SPEC-Sortie-2-connection-layer-port.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/SPEC-Sortie-3-fts5-to-tsvector.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/SPEC-Sortie-4-tmdb-index-database.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/postgres-migration/SPEC-Sortie-5-etl-migration.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/sqlite-domain-separation/PRD-sqlite-domain-separation.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/sqlite-domain-separation/SPEC-Sortie-1-schema-partitioning-and-config.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/sqlite-domain-separation/SPEC-Sortie-2-multi-db-connection-layer.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/sqlite-domain-separation/SPEC-Sortie-3-cross-domain-query-decoupling.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/docs/sqlite-domain-separation/SPEC-Sortie-4-etl-split-script-and-validation.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/app.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/auth/device_keys.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_base_domain.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_blackouts.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_connection.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_devices.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_feedback.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_fetch_queue.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_jobs_db.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_motd.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_people.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_queue.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_queue_db.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_users_db.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/engine.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/schemas/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/schemas/catalog_schema.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/schemas/jobs_schema.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/schemas/queue_schema.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/db/schemas/users_schema.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/report.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/identify.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/tags.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/_schema.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/_textmatch.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/_ttscrape.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/builder.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/coverage.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/catalog/tmdb_index/index.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/blackout_scan.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/job_scheduler.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/log_capture.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/motd_publish.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/migrate_sqlite_to_pg.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/motd/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/motd/builder.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/motd/render.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/completion.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_moderation.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_motd.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/devices.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/public_api.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/scripts/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/scripts/split_databases.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/admin-motd.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/admin-playlists.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/admin-promos.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/admin-schedules.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/admin.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/keybindings.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/admin/motd.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/catalog/edit_item_modal.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/motd/channel_z.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/user/link_device.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/templates/user/my_list.html +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/scripts/AGENTS.md +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/scripts/patch_mediacms_bulk_facets.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/scripts/patch_mediacms_tags.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/scripts/split_databases.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/scripts/trigger_enrich.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/__init__.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_catalog_blackout.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_catalog_delete.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_classify.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_concurrency_split.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_database_config_and_schemas.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_device_linking.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_fetch_queue_pacing.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_identify_step.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_job_logs_and_queue_visibility.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_motd_publish.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_people_facets.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_postgres_pruner.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_providers.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_queue_preview_fallback.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_recently_played_hide.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_search_facets.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_title_normalization.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_tmdb_index.py +0 -0
- {kryten_webqueue-0.48.0 → kryten_webqueue-0.49.0}/tests/test_watchlist.py +0 -0
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.49.0] - 2026-09-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Postgres repository port complete (Sortie 2/3)**: all ~140 SQLite domain methods
|
|
8
|
+
across `catalog`/`queue`/`jobs`/`users` are now implemented for the `asyncpg`/Postgres
|
|
9
|
+
backend (`_pg_catalog_db.py`, `_pg_queue_db.py`, `_pg_jobs_db.py`, `_pg_users_db.py`),
|
|
10
|
+
wired into `Database.__init__` for `database.backend == "postgres"`. `search()`/
|
|
11
|
+
`search_count()` use a hybrid `tsvector` + `pg_trgm` two-CTE query in place of SQLite's
|
|
12
|
+
FTS5. Live-tested end-to-end against a disposable `webqueue_test` database on
|
|
13
|
+
`chandra-1` (`tests/test_postgres_domain_crud.py`).
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **`Database` facade never dispatched cross-domain orchestration methods (`browse`,
|
|
18
|
+
`search`, `get_item`, `is_restricted`, etc.) for the Postgres backend** — these all
|
|
19
|
+
checked `layout == "partitioned"` only, never `backend == "postgres"`, so a Postgres
|
|
20
|
+
`Database` would silently fall through to the SQLite-monolith code path. Introduced
|
|
21
|
+
`Database._domain_dispatch` (`layout == "partitioned" or backend == "postgres"`) and
|
|
22
|
+
updated every dispatch check to use it.
|
|
23
|
+
- **`rotate_playlist_item_to_bottom` silently failed on every real play completion** in
|
|
24
|
+
both partitioned SQLite and Postgres: it queried a bare `catalog` table from within the
|
|
25
|
+
`queue`-only connection/schema, which doesn't exist there (`OperationalError`/
|
|
26
|
+
`UndefinedTableError`), swallowed by a bare `except Exception` in
|
|
27
|
+
`queue/completion.py`. Added an `is_partitioned` flag to skip the same-connection
|
|
28
|
+
lookup, and a `Database.rotate_playlist_item_to_bottom` facade override that resolves
|
|
29
|
+
`friendly_token -> manifest_url` via the `catalog` domain first, mirroring the existing
|
|
30
|
+
`_get_reserved_tokens()` pattern.
|
|
31
|
+
|
|
32
|
+
## [0.48.1] - 2026-09-23
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- **Production 500 on `/catalog/browse` in partitioned SQLite mode**: `get_tags()` (facet
|
|
37
|
+
counts) unconditionally ran the monolith reserved-item exclusion subquery referencing
|
|
38
|
+
`saved_playlist_items`/`saved_playlists`, tables that live in `queue.sqlite3`, not
|
|
39
|
+
`catalog.sqlite3`. Every browse/search page load crashed with
|
|
40
|
+
`sqlite3.OperationalError: no such table: saved_playlist_items` once the site was
|
|
41
|
+
cut over to `layout="partitioned"`. `get_tags` now accepts `exclude_tokens`/
|
|
42
|
+
`is_partitioned`, and the `Database` facade resolves reserved/blackout exclusions via
|
|
43
|
+
the `queue` domain before calling it, matching the existing `browse`/`search` pattern.
|
|
44
|
+
- **Reserved-item exclusion leak in partitioned mode**: `queue.get_reserved_media_ids()`
|
|
45
|
+
returns raw `media_id` values, which are usually CyTube manifest URLs, not bare
|
|
46
|
+
`friendly_token`s. `browse`, `browse_count`, `search`, and `search_count` were comparing
|
|
47
|
+
these raw values directly against `catalog.friendly_token`, so immutable/promo-pool
|
|
48
|
+
items whose playlist entry stored a manifest URL were **not excluded** from public
|
|
49
|
+
results. Added `catalog.resolve_friendly_tokens()` to resolve either shape back to
|
|
50
|
+
`friendly_token`, and all four call sites now use it via a shared
|
|
51
|
+
`Database._get_reserved_tokens()` helper.
|
|
52
|
+
|
|
3
53
|
## [0.48.0] - 2026-09-23
|
|
4
54
|
|
|
5
55
|
### Added
|
|
@@ -13,8 +13,13 @@ server {
|
|
|
13
13
|
|
|
14
14
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
|
15
15
|
|
|
16
|
+
# Upstream application backend:
|
|
17
|
+
# - Local grindhouse service: http://127.0.0.1:2010
|
|
18
|
+
# - Cross-network Chandra-1 Podman container: http://chandra-1.local:2010
|
|
19
|
+
|
|
16
20
|
# WebSocket upgrade
|
|
17
21
|
location /ws {
|
|
22
|
+
# Use http://chandra-1.local:2010 when deployed to Chandra-1 Podman pod
|
|
18
23
|
proxy_pass http://127.0.0.1:2010;
|
|
19
24
|
proxy_http_version 1.1;
|
|
20
25
|
proxy_set_header Upgrade $http_upgrade;
|
|
@@ -28,6 +33,7 @@ server {
|
|
|
28
33
|
|
|
29
34
|
# API + pages
|
|
30
35
|
location / {
|
|
36
|
+
# Use http://chandra-1.local:2010 when deployed to Chandra-1 Podman pod
|
|
31
37
|
proxy_pass http://127.0.0.1:2010;
|
|
32
38
|
proxy_http_version 1.1;
|
|
33
39
|
proxy_set_header Connection "";
|
|
@@ -133,18 +133,29 @@ Upon completion of Part 1, the partitioned domain model directly maps to Postgre
|
|
|
133
133
|
- `tmdb.*` — Persistent, queryable local TMDB dump index.
|
|
134
134
|
|
|
135
135
|
```
|
|
136
|
+
┌─────────────────────────────────────────┐
|
|
137
|
+
│ grindhouse.local Host │
|
|
138
|
+
│ │
|
|
139
|
+
│ ┌─────────────────────────────────┐ │
|
|
140
|
+
│ │ nginx proxy │ │
|
|
141
|
+
│ │ queue.dropsugar.co (SSL) │ │
|
|
142
|
+
│ └────────────────┬────────────────┘ │
|
|
143
|
+
└────────────────────┼────────────────────┘
|
|
144
|
+
│ LAN Reverse Proxy
|
|
145
|
+
│ http://chandra-1.local:2010
|
|
146
|
+
▼
|
|
136
147
|
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
|
137
148
|
│ chandra-1 Host System │
|
|
138
149
|
│ │
|
|
139
150
|
│ ┌──────────────────────────────────────────────────────────────────────────────────┐ │
|
|
140
|
-
│ │
|
|
151
|
+
│ │ Podman Container: webqueue-app │ │
|
|
141
152
|
│ │ │ │
|
|
142
|
-
│ │ ┌─────────────────────────────┐
|
|
143
|
-
│ │ │ kryten-webqueue-app │
|
|
144
|
-
│ │ │ (FastAPI + uvicorn)
|
|
145
|
-
│ │ │ SQLAlchemy 2.0 (asyncpg) │
|
|
146
|
-
│ │ └──────────────┬──────────────┘
|
|
147
|
-
│
|
|
153
|
+
│ │ ┌─────────────────────────────┐ │ │
|
|
154
|
+
│ │ │ kryten-webqueue-app │ │ │
|
|
155
|
+
│ │ │ (FastAPI + uvicorn:2010) │ │ │
|
|
156
|
+
│ │ │ SQLAlchemy 2.0 (asyncpg) │ │ │
|
|
157
|
+
│ │ └──────────────┬──────────────┘ │ │
|
|
158
|
+
│ └─────────────────┼───────────────────────────────────────────┬────────────────────┘ │
|
|
148
159
|
│ │ │ │
|
|
149
160
|
│ ▼ ▼ │
|
|
150
161
|
│ ┌─────────────────────────────────────┐ ┌──────────────────────────────────────┐ │
|
|
@@ -156,6 +167,11 @@ Upon completion of Part 1, the partitioned domain model directly maps to Postgre
|
|
|
156
167
|
└────────────────────────────────────────────────────────────────────────────────────────┘
|
|
157
168
|
```
|
|
158
169
|
|
|
170
|
+
The public hostname `https://queue.dropsugar.co/` terminates TLS via Let's Encrypt Nginx on
|
|
171
|
+
`grindhouse.local`. During the Chandra-1 cutover, the Nginx reverse proxy configuration on
|
|
172
|
+
`grindhouse.local` (`/etc/nginx/sites-available/queue.conf`) is updated to forward HTTP and
|
|
173
|
+
WebSocket traffic across the local network to `http://chandra-1.local:2010`.
|
|
174
|
+
|
|
159
175
|
### 2.2 Core Technical Specifications
|
|
160
176
|
|
|
161
177
|
1. **ORM & Driver**: **SQLAlchemy 2.0 with `asyncpg`** (`create_async_engine`, `async_sessionmaker`, `AsyncSession`).
|
|
@@ -80,15 +80,73 @@ sudo systemctl start webqueue-app.service
|
|
|
80
80
|
sudo systemctl status webqueue-app.service --no-pager
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
Verify local health inside `chandra-1`:
|
|
84
84
|
```bash
|
|
85
85
|
curl -I http://127.0.0.1:2010/auth/login
|
|
86
86
|
curl -s http://127.0.0.1:2010/queue/next-schedule
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
### Step 6: Update Grindhouse Nginx Reverse Proxy Across Network
|
|
90
|
+
The public website `https://queue.dropsugar.co/` terminates SSL via Let's Encrypt Nginx on `grindhouse.local`. Update its upstream to proxy across the LAN to `chandra-1.local:2010`.
|
|
91
|
+
|
|
92
|
+
On `grindhouse.local`:
|
|
93
|
+
1. Edit `/etc/nginx/sites-available/queue.conf`:
|
|
94
|
+
```nginx
|
|
95
|
+
# WebSocket upgrade
|
|
96
|
+
location /ws {
|
|
97
|
+
proxy_pass http://chandra-1.local:2010;
|
|
98
|
+
proxy_http_version 1.1;
|
|
99
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
100
|
+
proxy_set_header Connection "upgrade";
|
|
101
|
+
proxy_set_header Host $host;
|
|
102
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
103
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
104
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
105
|
+
proxy_read_timeout 86400;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# API + pages
|
|
109
|
+
location / {
|
|
110
|
+
proxy_pass http://chandra-1.local:2010;
|
|
111
|
+
proxy_http_version 1.1;
|
|
112
|
+
proxy_set_header Connection "";
|
|
113
|
+
proxy_set_header Host $host;
|
|
114
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
115
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
116
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
2. Test and reload Nginx:
|
|
121
|
+
```bash
|
|
122
|
+
sudo nginx -t
|
|
123
|
+
sudo systemctl reload nginx
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 7: Public Smoke Testing & Verification
|
|
127
|
+
Test public endpoints through the reverse proxy:
|
|
128
|
+
```bash
|
|
129
|
+
curl -I https://queue.dropsugar.co/auth/login
|
|
130
|
+
curl -s https://queue.dropsugar.co/queue/next-schedule
|
|
131
|
+
```
|
|
132
|
+
Monitor logs on `chandra-1`:
|
|
133
|
+
```bash
|
|
134
|
+
sudo journalctl -u webqueue-app.service -f
|
|
135
|
+
```
|
|
136
|
+
|
|
89
137
|
---
|
|
90
138
|
|
|
91
139
|
## 3. Rollback & Forward Repair Policy
|
|
92
140
|
|
|
93
|
-
- **Pre-Cutover Failure**: If ETL validation fails
|
|
94
|
-
|
|
141
|
+
- **Pre-Cutover Failure**: If ETL validation fails, container startup fails on `chandra-1`, or initial smoke tests fail before Nginx is updated:
|
|
142
|
+
1. Do not update or revert Nginx on `grindhouse.local` (`proxy_pass http://127.0.0.1:2010;`).
|
|
143
|
+
2. Restart `kryten-webqueue.service` on `grindhouse.local`:
|
|
144
|
+
```bash
|
|
145
|
+
sudo systemctl start kryten-webqueue.service
|
|
146
|
+
```
|
|
147
|
+
3. The SQLite databases remain untouched and consistent.
|
|
148
|
+
- **Traffic Rollback (if Nginx was already switched but app has issues)**:
|
|
149
|
+
1. Revert `/etc/nginx/sites-available/queue.conf` on `grindhouse.local` to point back to `http://127.0.0.1:2010`.
|
|
150
|
+
2. Reload Nginx: `sudo nginx -t && sudo systemctl reload nginx`.
|
|
151
|
+
3. Start `kryten-webqueue.service` on `grindhouse.local`.
|
|
152
|
+
- **Post-Cutover Forward Repair**: Once production traffic is accepted by PostgreSQL (user logins, new spend requests, play completions), recovery is a **forward repair in PostgreSQL**. The retained SQLite snapshot is preserved read-only for forensics.
|