trypost/app/Actions/Post/UpdatePost.php
Paulo Castellano ceb7b92b74 feat: PostPlatform enum, failure email, DB indexes, rate limiting, tests
Publishing improvements:
- Create PostPlatformStatus enum (Pending, Publishing, Published, Failed)
- Update PostPlatform model, jobs, factories to use enum
- Add PostPublishFailed email notification when post fails to publish
- Maizzle template + blade for failure email with platform details
- PublishPost job: add $tries=3, $backoff=30, failed() method
- Fix broadcast event to serialize enum status value

Security:
- Add rate limiting (throttle:6,1) on social connect endpoints
- Fix MediaController::reorder IDOR vulnerability
- Fix Connect.vue broken import (storeStep2 -> storeConnect)
- Fix UpdatePost data_get() consistency

Database:
- Add composite index on post_platforms (post_id, enabled)
- Add index on post_platforms (social_account_id)

Tests:
- Add 3 tests for profile photo upload/delete
- Add 2 tests for media reorder (including IDOR check)
- Fix publish tests for PostPlatformStatus enum
- Add Mail::fake() to publish tests

Cleanup:
- Remove unused AppHeader.vue and AppHeaderLayout.vue
- Remove dead BillingController methods

All 733 tests passing.
2026-03-30 16:11:38 -03:00

76 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Actions\Post;
use App\Enums\Post\Status as PostStatus;
use App\Jobs\PublishPost;
use App\Models\Post;
use App\Models\Workspace;
use Carbon\Carbon;
class UpdatePost
{
/**
* @return array{post: Post, action: string|null}
*/
public static function execute(Workspace $workspace, Post $post, array $data): array
{
if ($post->status === PostStatus::Published) {
return ['post' => $post, 'action' => 'already_published'];
}
$scheduledAt = $post->scheduled_at;
if (data_get($data, 'scheduled_at')) {
$scheduledAt = Carbon::parse(data_get($data, 'scheduled_at'), $workspace->timezone)->utc();
}
$status = data_get($data, 'status', $post->status);
$post->update([
'status' => $status === 'publishing' ? PostStatus::Publishing : $status,
'synced' => data_get($data, 'synced', $post->synced),
'scheduled_at' => $scheduledAt,
]);
if (array_key_exists('label_ids', $data)) {
$post->labels()->sync(data_get($data, 'label_ids', []));
}
$post->postPlatforms()->update(['enabled' => false]);
foreach (data_get($data, 'platforms', []) as $platformData) {
$updateData = [
'enabled' => true,
'content' => data_get($platformData, 'content'),
];
if (data_get($platformData, 'content_type') !== null) {
$updateData['content_type'] = data_get($platformData, 'content_type');
}
if (data_get($platformData, 'meta') !== null) {
$postPlatform = $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first();
$updateData['meta'] = array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta'));
}
$post->postPlatforms()
->where('id', data_get($platformData, 'id'))
->update($updateData);
}
if ($status === 'publishing') {
$post->update(['scheduled_at' => now()]);
PublishPost::dispatch($post);
return ['post' => $post, 'action' => 'publishing'];
}
if ($status === 'scheduled') {
return ['post' => $post, 'action' => 'scheduled'];
}
return ['post' => $post, 'action' => null];
}
}