fb_posts: edit and repost through the publisher's signed reschedule

POST /fbposts/{id}/reschedule {message, scheduled_for}. The site derives
the per-post signature from the stored cancel link and asks the publisher
to delete the post on Facebook and redraft the queue file; the row goes
to drafted with no Facebook id and no cancel link until the publisher's
next cycle reports the new one. Times must be at least 15 minutes out,
with an offset. An explicit null now clears fb_post_id on upsert; an
absent one still keeps it. tests/smoke_admin.py 160 -> 162.
This commit is contained in:
2026-09-04 19:57:40 -04:00
parent ea22ed53e7
commit b84fc7f064
3 changed files with 64 additions and 1 deletions
+5
View File
@@ -410,6 +410,11 @@ check("a scheduled post past its time reads as published; a future one stays sch
check("open-only list drops the past one and keeps the future one",
[p["id"] for p in S.list_fb_posts(include_done=False) if p["id"].startswith("pack-73/20")] == ["pack-73/2036-01-01-new"])
check("cancelled beats the clock", S.fb_state({"status": "cancelled", "scheduled_for": "2020-01-01T00:00:00+00:00"}) == "cancelled")
rd = S.upsert_fb_post({"id": "pack-73/2036-01-01-new", "status": "drafted", "message": "edited", "fb_post_id": None, "cancel_url": None})
check("a redraft clears the facebook id and cancel link", rd["status"] == "drafted" and rd["fb_post_id"] is None and rd["cancel_url"] is None)
rd2 = S.upsert_fb_post({"id": "pack-73/2036-01-01-new", "status": "scheduled", "message": "edited", "fb_post_id": "141_NEW"})
rd3 = S.upsert_fb_post({"id": "pack-73/2036-01-01-new", "status": "scheduled", "message": "edited again"})
check("a report without an id keeps the id", rd2["fb_post_id"] == "141_NEW" and rd3["fb_post_id"] == "141_NEW")
check("fbposts caps: read for leaders, ingest admin-only and scopable", "fbposts:read" in I.CAPS["leader"] and "fbposts:ingest" in I.CAPS["admin"]
and "fbposts:ingest" not in I.KEY_UNSCOPABLE)
con = S.connect(); con.execute("DELETE FROM fb_posts"); con.commit(); con.close()