Two bugs caused multiple articles to land on the same publish slot: 1. main.py: asyncio.create_task() returned immediately, allowing a second pipeline trigger (N8N + Telegram /run or two N8N calls) to start a second concurrent run. Added asyncio.Lock (_pipeline_lock) so any second trigger while the pipeline is running is rejected immediately. 2. scheduler.py: reserve_publish_slot() read the list of occupied slots and wrote the new slot in two separate DB connections. Concurrent threads could both see the same "free" slot before either committed its write. Fixed by wrapping the entire read-find-write cycle in a threading.Lock (_slot_lock) and a single DB connection, so the slot check and the slot assignment are atomic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| app | ||
| data | ||
| static | ||
| templates | ||
| tests | ||
| .env.example | ||
| __init__.py | ||
| README.md | ||
| requirements-test.txt | ||
| requirements.txt | ||
Backend Skeleton (FastAPI)
Dieses Verzeichnis enthaelt das technische Grundgeruest fuer den Rebuild von rss-news.
Start (lokal)
python -m venv .venv
source .venv/bin/activate
pip install -r backend/requirements.txt
uvicorn backend.app.main:app --reload --port 8501
Admin UI
- Login:
http://127.0.0.1:8501/admin/login - Dashboard:
http://127.0.0.1:8501/admin/dashboard
Environment
- Datei:
backend/.env - Vorlage:
backend/.env.example
Endpoints
GET /health- HealthcheckPOST /auth/login- Login mit Admin-UserPOST /auth/logout- LogoutGET /auth/me- Aktiver UserGET /api/protected- Geschuetzter Test-EndpointGET /api/pipeline/status- Basisstatus inkl. DatensatzzaehlerGET /api/sources- QuellenlistePOST /api/sources- Quelle anlegenGET /api/sources/{source_id}/policy-check- Policy-Pruefung fuer QuelleGET /api/feeds- FeedlistePOST /api/feeds- Feed anlegenGET /api/feeds/{feed_id}/policy-check- Policy-Pruefung fuer FeedGET /api/runs- Import-/Job-Runs anzeigenGET /api/runs/{run_id}- Detailansicht eines RunsPOST /api/runs- Run startenPOST /api/runs/{run_id}/finish- Run abschliessenGET /api/articles- Artikel anzeigenGET /api/articles/{article_id}- ArtikeldetailPOST /api/articles/upsert- Artikel idempotent anlegen/aktualisierenPOST /api/articles/{article_id}/transition- Statuswechsel nach Workflow-RegelnPOST /api/articles/{article_id}/review- Review-Entscheidung (approve/reject)POST /api/ingestion/run- Feed-Ingestion starten (optional pro Feed)
Datenbank
- SQLite-Datei unter
backend/data/rss_news.db - Tabellen werden beim App-Start initialisiert.
- Tabellen:
sources,feeds,runs,articles - Dedupe-Strategie Artikel:
source_url->(feed_id, source_article_id)->source_hash
Policy-Enforcement
- Ingestion blockiert Feeds automatisch, wenn die zugeordnete Quelle nicht policy-konform ist.
- Mindestanforderungen:
risk_level=green,terms_url,license_name,last_reviewed_at,is_enabled=1. - Pro importiertem Artikel wird ein
attribution-Block inmeta_jsongespeichert.
Review-Workflow
- Statuskette:
new -> review -> approved -> published - Ablehnung im Review setzt auf
rewrite - Ungueltige Statuswechsel werden per API blockiert
Verifikation
python -m unittest backend.tests.test_db_repositories
python -m unittest backend.tests.test_ingestion
python -m unittest backend.tests.test_api_auth
CI / Online-Auswertung
- GitHub Actions Workflow:
.github/workflows/test.yml - Fuehrt Tests inkl. Coverage auf Push/PR gegen
mainaus.
Hetzner Smoketest
BASE_URL="https://news.vanityontour.de" \
APP_ADMIN_USERNAME="admin" \
APP_ADMIN_PASSWORD="..." \
bash scripts/smoke_backend.sh
Hinweis
Passwort-Hashing und CSRF/Rate-Limit sind als naechste Ausbaustufe vorgesehen.