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(), ]), ]); } }