Add a Publisher abstraction and queued job to publish from the UI
Introduce Publisher/PublisherManager (WordPress + Reddit implementations) so publishing logic is shared between the CLI, the queue, and future network integrations. Add a "Publish" button on the Posts page that dispatches PublishPostJob instead of blocking the request — Reddit's publisher shells out to the existing Playwright sidecar and can take tens of seconds, so it can't run synchronously in a web request. Adds a `target` field (e.g. subreddit) and `publishing` status to Post, plus a `publish_error` field so failures surface in the UI. Refactors posts:publish to delegate to WordPressPublisher without changing its CLI output contract (existing tests pass unchanged). Also fixes two bugs found along the way: UpdatePostRequest was silently dropping `published_url` from PATCH requests (never in its validation rules, despite AI_API.md documenting it), and three Livewire index-page tests were broken by the earlier auth change (never re-run after adding the `auth` middleware) since they hit routes via HTTP without a logged-in user. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,10 +5,10 @@ Usage:
|
||||
python3 posts.py list [--status STATUS] [--site-id ID] [--social-account-id ID] [--due]
|
||||
python3 posts.py get POST_ID
|
||||
python3 posts.py create --site-id ID --type TYPE --body BODY
|
||||
[--social-account-id ID] [--title TITLE]
|
||||
[--social-account-id ID] [--title TITLE] [--target TARGET]
|
||||
[--status STATUS] [--scheduled-at DATETIME]
|
||||
python3 posts.py update POST_ID [--site-id ID] [--social-account-id ID]
|
||||
[--type TYPE] [--title TITLE] [--body BODY]
|
||||
[--type TYPE] [--title TITLE] [--body BODY] [--target TARGET]
|
||||
[--status STATUS] [--scheduled-at DATETIME]
|
||||
[--published-url URL]
|
||||
python3 posts.py delete POST_ID
|
||||
@@ -22,7 +22,7 @@ import argparse
|
||||
import _client
|
||||
|
||||
TYPES = ["twitter", "instagram", "facebook", "reddit", "tiktok", "linkedin", "youtube", "blog", "other"]
|
||||
STATUSES = ["draft", "scheduled", "published", "failed"]
|
||||
STATUSES = ["draft", "scheduled", "publishing", "published", "failed"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -46,6 +46,7 @@ def main() -> None:
|
||||
create_parser.add_argument("--body", required=True)
|
||||
create_parser.add_argument("--social-account-id", type=int)
|
||||
create_parser.add_argument("--title")
|
||||
create_parser.add_argument("--target", help="Network-specific destination, e.g. a subreddit for Reddit posts")
|
||||
create_parser.add_argument("--status", choices=STATUSES)
|
||||
create_parser.add_argument("--scheduled-at", help="ISO 8601 datetime, e.g. 2026-07-20T09:00:00")
|
||||
|
||||
@@ -56,6 +57,7 @@ def main() -> None:
|
||||
update_parser.add_argument("--type", choices=TYPES)
|
||||
update_parser.add_argument("--title")
|
||||
update_parser.add_argument("--body")
|
||||
update_parser.add_argument("--target", help="Network-specific destination, e.g. a subreddit for Reddit posts")
|
||||
update_parser.add_argument("--status", choices=STATUSES)
|
||||
update_parser.add_argument("--scheduled-at", help="ISO 8601 datetime")
|
||||
update_parser.add_argument("--published-url")
|
||||
@@ -85,6 +87,7 @@ def main() -> None:
|
||||
"type": args.type,
|
||||
"title": args.title,
|
||||
"body": args.body,
|
||||
"target": args.target,
|
||||
"status": args.status,
|
||||
"scheduled_at": args.scheduled_at,
|
||||
}
|
||||
@@ -98,6 +101,7 @@ def main() -> None:
|
||||
"type": args.type,
|
||||
"title": args.title,
|
||||
"body": args.body,
|
||||
"target": args.target,
|
||||
"status": args.status,
|
||||
"scheduled_at": args.scheduled_at,
|
||||
"published_url": args.published_url,
|
||||
|
||||
Reference in New Issue
Block a user