fix: API scheduled_at validation, redirect allowlist, UUID model_id, UpdatePost transaction, safe resolveModel

This commit is contained in:
Paulo Castellano 2026-04-01 13:21:22 -03:00
parent d19a5c7bcf
commit 0bca140cd9
7 changed files with 44 additions and 26 deletions

View file

@ -11,6 +11,7 @@
use App\Models\Workspace;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
class UpdatePost
{
@ -40,30 +41,32 @@ public static function execute(Workspace $workspace, Post $post, array $data): a
$post->labels()->sync(data_get($data, 'label_ids', []));
}
$post->postPlatforms()->update(['enabled' => false]);
DB::transaction(function () use ($post, $data) {
$post->postPlatforms()->update(['enabled' => false]);
foreach (data_get($data, 'platforms', []) as $platformData) {
$updateData = [
'enabled' => true,
'content' => data_get($platformData, 'content'),
];
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();
if ($postPlatform) {
$updateData['meta'] = array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta'));
if (data_get($platformData, 'content_type') !== null) {
$updateData['content_type'] = data_get($platformData, 'content_type');
}
}
$post->postPlatforms()
->where('id', data_get($platformData, 'id'))
->update($updateData);
}
if (data_get($platformData, 'meta') !== null) {
$postPlatform = $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first();
if ($postPlatform) {
$updateData['meta'] = array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta'));
}
}
$post->postPlatforms()
->where('id', data_get($platformData, 'id'))
->update($updateData);
}
});
if ($status === PostStatus::Publishing->value) {
$post->update(['scheduled_at' => now()]);

View file

@ -180,7 +180,11 @@ private function authorizeModelOwnership(Model $model, Request $request): void
private function resolveModel(string $alias, string $id): Model
{
$modelClass = Relation::getMorphedModel($alias) ?? $alias;
$modelClass = Relation::getMorphedModel($alias);
if (! $modelClass) {
abort(404);
}
return $modelClass::findOrFail($id);
}

View file

@ -249,8 +249,12 @@ public function destroy(Request $request, Post $post): RedirectResponse
session()->flash('flash.banner', __('posts.flash.deleted'));
session()->flash('flash.bannerStyle', 'success');
$allowedRedirects = ['app.posts.index', 'app.calendar'];
if ($redirect = $request->input('redirect')) {
return redirect()->route($redirect);
if (in_array($redirect, $allowedRedirects)) {
return redirect()->route($redirect);
}
}
return back();

View file

@ -27,7 +27,14 @@ public function rules(): array
'platforms.*.content' => ['nullable', 'string', 'max:63206'],
'platforms.*.content_type' => ['required', 'string', Rule::in(array_column(ContentType::cases(), 'value'))],
'platforms.*.meta' => ['nullable', 'array'],
'scheduled_at' => ['nullable', 'date'],
'scheduled_at' => [
'nullable',
'date',
Rule::when(
in_array($this->input('status'), ['scheduled', 'publishing']),
['after:now']
),
],
'label_ids' => ['sometimes', 'array'],
'label_ids.*' => ['uuid', Rule::exists('workspace_labels', 'id')->where('workspace_id', $this->workspace->id)],
];

View file

@ -19,7 +19,7 @@ public function rules(): array
return [
'targets' => ['required', 'array', 'max:50'],
'targets.*.model' => ['required', 'string', Rule::in(['postPlatform', 'workspace', 'user'])],
'targets.*.model_id' => ['required', 'string'],
'targets.*.model_id' => ['required', 'uuid'],
];
}
}

View file

@ -38,7 +38,7 @@ public function rules(): array
return [
'content_range' => ['required', 'string', 'regex:'.self::CONTENT_RANGE_PATTERN],
'model' => ['required', 'string', Rule::in($this->allowedModels)],
'model_id' => ['required', 'string'],
'model_id' => ['required', 'uuid'],
'collection' => ['sometimes', 'string', 'max:255'],
'file_name' => [
'required',

View file

@ -38,7 +38,7 @@ public function rules(): array
],
'model_id' => [
'required',
'string',
'uuid',
],
'collection' => [
'sometimes',