Commit Graph
20 Commits
Author SHA1 Message Date
m105andClaude Sonnet 5 4b3af4cc89 Make the image optional for WordPress posts too
Some blog posts just don't have one. WordPressPublisher no longer
rejects publishing a type=blog post without image_url — it uploads
and attaches it as the featured image when present, and simply skips
that step (no featured_media set) otherwise.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 20:07:47 +03:00
m105andClaude Sonnet 5 42abc658f2 Add an image field, required for WordPress posts
Posts get a nullable image_url. WordPressPublisher now downloads it
and uploads it to WordPress's media endpoint before creating the
post, setting it as featured_media — and rejects publishing (without
touching status) if a blog post has no image set, mirroring the
existing title requirement. Every other network treats it as optional.

Not enforced at save time, same as title — you can still draft an
incomplete WordPress post, it just can't be published until an image
is set. Exposed through the UI form (with a thumbnail/""missing""
indicator in the table), the JSON API, and the Python CLI wrapper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 20:03:25 +03:00
m105andClaude Sonnet 5 fd185ab813 Guard PublishPostJob against duplicate/stale dispatches
Skip if the post is no longer status=publishing when the job runs, so
a stray duplicate (e.g. clicking Publish twice while no worker was
running) can't publish the same post to WordPress a second time.

Also root-caused a stuck-in-"publishing" post reported live: the
queue worker started manually last session was a bare background
process, not a real service, and had died. Replaced it with a
systemd unit (muninn-queue-worker.service, enabled + running) so
the Publish button keeps working across reboots/restarts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 19:29:44 +03:00
m105andClaude Sonnet 5 9278d2e8a0 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>
2026-07-21 14:04:45 +03:00
Doar Mihai f7b71684f1 test: cover credential-clearing and trailing-slash URL edge cases
Also fixes a missing trailing newline in SiteResource.php.
2026-07-19 17:05:01 +03:00
Doar Mihai c2ef6d9986 feat: manage cms_type and WordPress credentials in Sites UI 2026-07-19 16:58:42 +03:00
Doar Mihai 336436bdc7 feat: add posts:publish command for WordPress REST API publishing 2026-07-19 16:54:38 +03:00
Doar Mihai 5501b93686 feat: expose cms_type/WordPress credentials and published_url in API responses 2026-07-19 16:51:15 +03:00
Doar MihaiandClaude Sonnet 5 80423301c4 feat: add published_url column to posts
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 16:48:03 +03:00
Doar Mihai 30095519e4 feat: add cms_type and WordPress credential fields to sites 2026-07-19 16:43:13 +03:00
Doar MihaiandClaude Sonnet 5 e0b8f2f924 feat: add full CRUD Posts API
Adds JSON API for Posts (index/show/store/update/destroy) so an AI
agent can draft posts, read what's due for publishing, and report
back publish/fail status. Unlike the existing read-only Sites/Accounts
APIs, this is full CRUD since the app itself never publishes anything.

- PostController with status/site_id/social_account_id/due filters
- StorePostRequest/UpdatePostRequest validate against Post::TYPES/STATUSES
- PostResource shapes the JSON response
- Route::apiResource('posts', ...) registered with ->names('api.posts')
  to avoid colliding with the existing web posts.index route name
- store() calls ->fresh() after create() so the response reflects the
  DB-level default status ('draft') instead of a stale in-memory null

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 17:22:16 +03:00
Doar MihaiandClaude Sonnet 5 be8e6e997f feat: add Posts Livewire CRUD page
Adds the Posts index page (list/create/edit/delete) following the
existing Sites/SocialAccounts pattern, and extends the shared nav
layout with a Posts link.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 17:13:50 +03:00
Doar MihaiandClaude Sonnet 5 f746c5d6a0 feat: add Post model, migration, and factory
Adds the Post model belonging to Site (required, cascade-deletes with
the site) and SocialAccount (nullable, nulled out on account delete).
Foundation for the Scheduled Posts feature; later tasks build the
Livewire CRUD UI and API on top of this model.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 17:07:48 +03:00
Doar MihaiandClaude Sonnet 5 5c24b6fbe0 feat: add read-only Social Accounts API
Add GET /api/accounts (list with optional ?network= and ?type= filters) and
GET /api/accounts/{socialAccount} (show) endpoints. Include plain-text password
field for AI agent consumption.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 15:25:56 +03:00
Doar MihaiandClaude Sonnet 5 20c33f1c23 feat: add read-only Sites API
Add GET /api/sites endpoint for listing sites with optional name filter,
and GET /api/sites/{site} for showing individual sites. Includes API
resource, controller, routes registration, and comprehensive tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 15:12:44 +03:00
Doar MihaiandClaude Sonnet 5 cee5b1976b feat: add Social Accounts Livewire CRUD page
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 15:05:55 +03:00
Doar Mihai e75a846a97 feat: add Sites Livewire CRUD page 2026-07-18 14:57:47 +03:00
Doar MihaiandClaude Sonnet 5 137a15f5ff feat: add SocialAccount model, migration, and factory
Implement SocialAccount model with enum fields for network and type,
along with migration and factory. Includes NETWORKS and TYPES constants
for dropdown usage in Livewire forms and validation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 14:50:00 +03:00
Doar MihaiandClaude Sonnet 5 889ac9b7f9 feat: add Site model, migration, and factory
Implements Task 2: creates the Site model with fillable name, url, and
description fields; the corresponding database migration; and a factory
for generating test data. Includes TDD tests verifying model creation
with and without optional description.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 14:45:55 +03:00
Doar MihaiandClaude Sonnet 5 900e4f0309 Initial commit: Laravel skeleton with sites & accounts spec/plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 14:35:53 +03:00