trypost/app/Actions/Post/CreatePost.php
Paulo Castellano d8149ef058
Remove the automations module (#333)
* Remove the automations module

Drops the visual workflow builder end to end: actions, node runners,
commands, jobs, models, observers, policy, resources, requests, routes,
broadcast channel, scheduler entries, Horizon supervisor, Vue pages and
components, canvas undo/redo history, CodeEditor, translations, factories
and tests.

A new migration rewrites posts.created_via = 'automation' to 'web' and
drops the five automation tables. The CreatedVia::Automation enum case is
removed. The 2026-08-21 social-account identity migration now skips its
automation node repointing when the automations table no longer exists,
so it stays re-runnable after the drop.

Orphaned dependencies removed: simplepie/simplepie, @vue-flow/*,
codemirror and @codemirror/*.

* Drop the orphaned common.beta translation key

* Remove automation leftovers: ResolvableUrl rule, feed fixtures, useShortcut, nav badge

* Drop the unused chart wrapper and @unovis packages

* Address review: keep the shipped migration untouched, drop the dead previewOnly chain

- Restore 2026_08_21 migration to exactly what production ran; the
  rehearsal test now recreates the automations table it expects instead.
- Mark the drop migration's down() irreversible like its siblings.
- Simplify DropAutomationTablesMigrationTest to the sibling shape.
- Remove previewOnly / aiGenerateVariants: the only caller that set the
  prop was the deleted automation Generate node.
- Run pint over lang/*/common.php after the beta key removal.

* Exercise the drop migration against the real automation tables and their FKs

* Drop DuplicateIdentityRehearsalTest: it re-ran a frozen migration that reads the removed automations table
2026-09-05 11:03:23 -03:00

115 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Actions\Post;
use App\Enums\Post\CreatedVia;
use App\Enums\Post\Status as PostStatus;
use App\Models\Post;
use App\Models\User;
use App\Models\Workspace;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class CreatePost
{
/**
* Create a Post with optional platform selection.
*
* `platforms[]` enables specific social accounts. Each entry takes
* `social_account_id` and an optional `content_type` (defaults to the
* platform's default). Accounts not listed remain disabled but are still
* created via SyncPostPlatforms so the user can toggle them later in the
* editor.
*
* `label_ids[]` are attached after creation so the same set of UUIDs
* works for REST, MCP, and web callers.
*
* `created_via` records which entry point created the post (web, mcp, or api). Analytical only — null when omitted.
*
* @param array{
* content?: ?string,
* media?: array<int, mixed>,
* date?: ?string,
* scheduled_at?: ?string,
* created_via?: ?CreatedVia,
* platforms?: array<int, array{social_account_id: string, content_type?: string, meta?: array<string, mixed>}>,
* label_ids?: array<int, string>
* } $data
*/
public static function execute(Workspace $workspace, User $user, array $data): Post
{
$scheduledAt = self::resolveScheduledAt($data);
$post = DB::transaction(function () use ($workspace, $user, $data, $scheduledAt): Post {
$post = $workspace->posts()->create([
'user_id' => $user->id,
'content' => data_get($data, 'content', ''),
'media' => data_get($data, 'media', []),
'status' => PostStatus::Draft,
'created_via' => data_get($data, 'created_via'),
'scheduled_at' => $scheduledAt,
]);
SyncPostPlatforms::execute($post);
foreach (data_get($data, 'platforms', []) as $platformData) {
$accountId = data_get($platformData, 'social_account_id');
if (! $accountId) {
continue;
}
$updates = ['enabled' => true];
if ($contentType = data_get($platformData, 'content_type')) {
$updates['content_type'] = $contentType;
}
$meta = data_get($platformData, 'meta');
if (is_array($meta) && $meta !== []) {
$existing = $post->postPlatforms()
->where('social_account_id', $accountId)
->first();
if ($existing) {
$updates['meta'] = array_filter(
array_merge($existing->meta ?? [], $meta),
fn (mixed $value): bool => $value !== null,
);
}
}
$post->postPlatforms()
->where('social_account_id', $accountId)
->update($updates);
}
if ($labelIds = data_get($data, 'label_ids')) {
$post->labels()->sync($labelIds);
}
return $post;
});
return $post;
}
/**
* @param array<string, mixed> $data
*/
private static function resolveScheduledAt(array $data): ?Carbon
{
if ($scheduledAt = data_get($data, 'scheduled_at')) {
return Carbon::parse($scheduledAt)->utc();
}
$date = data_get($data, 'date');
if (blank($date)) {
return null;
}
return Carbon::parse($date, 'UTC')->setTime(9, 0)->utc();
}
}