feat(admin): WordPress→DB sync for scheduled slots

Adds sync_db_from_wordpress() that treats WordPress as source of truth:
- future posts: update scheduled_publish_at to WP's actual date
- draft posts: clear scheduled_publish_at (not yet scheduled)
- published posts: mark article as 'published' in DB
- trashed/deleted posts: clear wp_post_id + wp_post_url + slot so article
  can be re-processed

Exposed via POST /admin/wp-sync with a sync button on the schedule page.
Run after any manual rescheduling in WordPress to bring DB back in sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OliverGiertz 2026-04-10 08:53:44 +00:00
parent 8676ace102
commit 2d02b56b65
3 changed files with 160 additions and 0 deletions

View file

@ -931,6 +931,28 @@ def admin_transition_article(request: Request, article_id: int, target_status: s
return _dashboard_redirect(msg=f"Ungueltiger Statuswechsel fuer Artikel #{article_id}", msg_type="error")
@router.post("/admin/wp-sync")
def admin_wp_sync(request: Request):
"""Sync scheduled_publish_at and WP references in the DB from WordPress."""
user = _admin_user(request)
if not user:
return RedirectResponse(url="/admin/login", status_code=303)
try:
from .wordpress import sync_db_from_wordpress
stats = sync_db_from_wordpress()
msg = (
f"WP-Sync abgeschlossen: "
f"{stats['slot_updated']} Slots aktualisiert, "
f"{stats['slot_cleared_draft']} Slots geleert (Draft), "
f"{stats['marked_published']} als veröffentlicht markiert, "
f"{stats['wp_reference_cleared']} WP-Referenzen gelöscht (Papierkorb), "
f"{stats['already_in_sync']} bereits synchron."
)
return RedirectResponse(url=f"/admin/schedule?msg={msg}&type=success", status_code=303)
except Exception as exc:
return RedirectResponse(url=f"/admin/schedule?msg=Sync fehlgeschlagen: {exc}&type=error", status_code=303)
@router.post("/admin/articles/{article_id}/retry")
def admin_retry_article(request: Request, article_id: int):
"""Reset a failed article to 'new' so the pipeline picks it up on next run."""