Files
muninn/app/Http/Controllers/Api/WebhookController.php
T

257 lines
8.7 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Jobs\PublishPostJob;
use App\Models\Post;
use App\Models\Site;
use App\Models\SocialAccount;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class WebhookController extends Controller
{
/**
* Create a new post via webhook.
*
* POST /api/webhooks/posts
*
* Required headers:
* - X-Muninn-Token: Your webhook token (from .env)
*
* Request body:
* {
* "site_name": "My Blog", // Site name to find the site
* "type": "blog", // Post type (required)
* "title": "Post Title", // Optional title
* "body": "Post content", // Required
* "target": "subreddit-name", // Optional target (e.g., subreddit)
* "image_url": "https://...", // Optional image URL
* "status": "draft", // Optional status (defaults to draft)
* "scheduled_at": "2026-07-28...", // Optional scheduled time
* "social_accounts": ["account1", "account2"] // Optional array of social account usernames
* }
*/
public function createPost(Request $request)
{
// Verify webhook token
$validToken = config('services.muninn.webhook_token');
$providedToken = $request->header('X-Muninn-Token');
if ($validToken && $providedToken !== $validToken) {
return response()->json([
'success' => false,
'error' => 'Invalid or missing webhook token',
], 401);
}
$validator = Validator::make($request->all(), [
'type' => ['required', 'string', 'in:' . implode(',', Post::TYPES)],
'body' => ['required', 'string'],
'title' => ['nullable', 'string', 'max:255'],
'target' => ['nullable', 'string', 'max:255'],
'image_url' => ['nullable', 'url', 'max:2048'],
'status' => ['nullable', 'string', 'in:' . implode(',', Post::STATUSES)],
'scheduled_at' => ['nullable', 'date'],
'site_name' => ['nullable', 'string'],
'social_accounts' => ['nullable', 'array'],
'social_accounts.*' => ['string'],
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'error' => 'Validation failed',
'errors' => $validator->errors(),
], 422);
}
$validated = $validator->validated();
// Find site by name if provided
$siteId = null;
if ($validated['site_name'] ?? null) {
$site = Site::where('name', $validated['site_name'])->first();
if ($site) {
$siteId = $site->id;
}
}
// Create the post
$post = Post::create([
'site_id' => $siteId,
'type' => $validated['type'],
'title' => $validated['title'] ?? null,
'body' => $validated['body'],
'target' => $validated['target'] ?? null,
'image_url' => $validated['image_url'] ?? null,
'status' => $validated['status'] ?? 'draft',
'scheduled_at' => $validated['scheduled_at'] ?? null,
]);
// Link social accounts by username if provided
if ($validated['social_accounts'] ?? null) {
foreach ($validated['social_accounts'] as $username) {
$account = SocialAccount::where('username', $username)->first();
if ($account) {
$post->socialAccounts()->attach($account->id);
}
}
}
return response()->json([
'success' => true,
'post' => [
'id' => $post->id,
'type' => $post->type,
'status' => $post->status,
'created_at' => $post->created_at->toIso8601String(),
],
], 201);
}
/**
* Trigger publishing for a post via webhook.
*
* POST /api/webhooks/posts/{post}/publish
*
* Required headers:
* - X-Muninn-Token: Your webhook token
*/
public function publishPost(Request $request, Post $post)
{
// Verify webhook token
$validToken = config('services.muninn.webhook_token');
$providedToken = $request->header('X-Muninn-Token');
if ($validToken && $providedToken !== $validToken) {
return response()->json([
'success' => false,
'error' => 'Invalid or missing webhook token',
], 401);
}
// Check if post can be published
$publisherManager = app(\App\Support\Publishing\PublisherManager::class);
if (! $publisherManager->resolve($post)) {
return response()->json([
'success' => false,
'error' => "No publisher available for post type '{$post->type}'",
], 400);
}
// Set status to publishing and dispatch job
$post->update(['status' => 'publishing', 'publish_error' => null]);
PublishPostJob::dispatch($post->id);
return response()->json([
'success' => true,
'message' => 'Publish job dispatched',
'post_id' => $post->id,
]);
}
/**
* Get post status via webhook.
*
* GET /api/webhooks/posts/{post}/status
*
* Required headers:
* - X-Muninn-Token: Your webhook token
*/
public function getPostStatus(Request $request, Post $post)
{
// Verify webhook token
$validToken = config('services.muninn.webhook_token');
$providedToken = $request->header('X-Muninn-Token');
if ($validToken && $providedToken !== $validToken) {
return response()->json([
'success' => false,
'error' => 'Invalid or missing webhook token',
], 401);
}
return response()->json([
'success' => true,
'post' => [
'id' => $post->id,
'type' => $post->type,
'status' => $post->status,
'title' => $post->title,
'published_url' => $post->published_url,
'publish_error' => $post->publish_error,
'created_at' => $post->created_at->toIso8601String(),
'updated_at' => $post->updated_at->toIso8601String(),
'scheduled_at' => $post->scheduled_at?->toIso8601String(),
],
]);
}
/**
* List posts with optional filters via webhook.
*
* GET /api/webhooks/posts
*
* Query params:
* - type: Filter by post type
* - status: Filter by status
* - site_id: Filter by site
* - limit: Limit number of results (default 20)
*
* Required headers:
* - X-Muninn-Token: Your webhook token
*/
public function listPosts(Request $request)
{
// Verify webhook token
$validToken = config('services.muninn.webhook_token');
$providedToken = $request->header('X-Muninn-Token');
if ($validToken && $providedToken !== $validToken) {
return response()->json([
'success' => false,
'error' => 'Invalid or missing webhook token',
], 401);
}
$query = Post::with(['site', 'socialAccounts'])->orderByDesc('created_at');
if ($request->has('type')) {
$query->where('type', $request->input('type'));
}
if ($request->has('status')) {
$query->where('status', $request->input('status'));
}
if ($request->has('site_id')) {
$query->where('site_id', $request->input('site_id'));
}
$limit = $request->input('limit', 20);
$posts = $query->take($limit)->get();
return response()->json([
'success' => true,
'count' => $posts->count(),
'posts' => $posts->map(fn ($post) => [
'id' => $post->id,
'type' => $post->type,
'status' => $post->status,
'title' => $post->title,
'body' => $post->body,
'published_url' => $post->published_url,
'publish_error' => $post->publish_error,
'site' => $post->site ? ['id' => $post->site->id, 'name' => $post->site->name] : null,
'social_accounts' => $post->socialAccounts->map(fn ($sa) => ['id' => $sa->id, 'username' => $sa->username, 'network' => $sa->network]),
'created_at' => $post->created_at->toIso8601String(),
'updated_at' => $post->updated_at->toIso8601String(),
'scheduled_at' => $post->scheduled_at?->toIso8601String(),
]),
]);
}
}