diff --git a/app/Http/Controllers/Api/UploadController.php b/app/Http/Controllers/Api/UploadController.php index f5b63daf..e6104975 100644 --- a/app/Http/Controllers/Api/UploadController.php +++ b/app/Http/Controllers/Api/UploadController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Api\StoreUploadRequest; +use App\Http\Resources\Api\MediaUploadResource; use App\Models\Media; use App\Models\Workspace; use Illuminate\Http\JsonResponse; @@ -15,20 +16,25 @@ class UploadController extends Controller { + private const CACHE_TTL_BUFFER_SECONDS = 60; + public function store(StoreUploadRequest $request, string $token): JsonResponse { $expiresAt = (int) $request->query('expires'); - $ttl = max(60, $expiresAt - now()->timestamp + 60); + $ttl = max( + self::CACHE_TTL_BUFFER_SECONDS, + $expiresAt - now()->timestamp + self::CACHE_TTL_BUFFER_SECONDS, + ); if (! Cache::add("mcp_upload:{$token}", true, $ttl)) { - abort(Response::HTTP_CONFLICT, 'Upload token already used.'); + abort(Response::HTTP_CONFLICT); } if (Media::where('upload_token', $token)->exists()) { - abort(Response::HTTP_CONFLICT, 'Upload token already consumed.'); + abort(Response::HTTP_CONFLICT); } - $workspace = Workspace::findOrFail((string) $request->query('ws')); + $workspace = Workspace::findOrFail((string) $request->query('workspace_id')); $media = DB::transaction(function () use ($workspace, $request, $token): Media { $media = $workspace->addMedia($request->file('media'), 'assets'); @@ -38,12 +44,8 @@ public function store(StoreUploadRequest $request, string $token): JsonResponse return $media; }); - return response()->json([ - 'upload_token' => $token, - 'media_id' => $media->id, - 'type' => $media->type, - 'mime_type' => $media->mime_type, - 'original_filename' => $media->original_filename, - ], Response::HTTP_CREATED); + return MediaUploadResource::make($media) + ->response() + ->setStatusCode(Response::HTTP_CREATED); } } diff --git a/app/Http/Resources/Api/MediaUploadResource.php b/app/Http/Resources/Api/MediaUploadResource.php new file mode 100644 index 00000000..a3898d96 --- /dev/null +++ b/app/Http/Resources/Api/MediaUploadResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'upload_token' => $this->upload_token, + 'media_id' => $this->id, + 'type' => $this->type, + 'mime_type' => $this->mime_type, + 'original_filename' => $this->original_filename, + ]; + } +} diff --git a/app/Mcp/Tools/Post/RequestMediaUploadTool.php b/app/Mcp/Tools/Post/RequestMediaUploadTool.php index 354e9100..fb17667b 100644 --- a/app/Mcp/Tools/Post/RequestMediaUploadTool.php +++ b/app/Mcp/Tools/Post/RequestMediaUploadTool.php @@ -31,7 +31,7 @@ public function handle(Request $request): Response|ResponseFactory $uploadUrl = URL::temporarySignedRoute( 'api.uploads.store', $expiresAt, - ['token' => $token, 'ws' => $workspaceId], + ['token' => $token, 'workspace_id' => $workspaceId], ); return Response::structured([ diff --git a/tests/Feature/Api/UploadControllerTest.php b/tests/Feature/Api/UploadControllerTest.php index bee8a9ed..2d79584a 100644 --- a/tests/Feature/Api/UploadControllerTest.php +++ b/tests/Feature/Api/UploadControllerTest.php @@ -26,7 +26,7 @@ function signedUploadUrl(Workspace $ws, string $token, ?int $expiresInMinutes = return URL::temporarySignedRoute( 'api.uploads.store', now()->addMinutes($expiresInMinutes), - ['token' => $token, 'ws' => $ws->id], + ['token' => $token, 'workspace_id' => $ws->id], ); } @@ -56,7 +56,7 @@ function signedUploadUrl(Workspace $ws, string $token, ?int $expiresInMinutes = $token = (string) Str::uuid(); $file = UploadedFile::fake()->image('shot.png', 50, 50); - $response = $this->postJson(route('api.uploads.store', ['token' => $token, 'ws' => $this->workspace->id]), [ + $response = $this->postJson(route('api.uploads.store', ['token' => $token, 'workspace_id' => $this->workspace->id]), [ 'media' => $file, ]); @@ -70,7 +70,7 @@ function signedUploadUrl(Workspace $ws, string $token, ?int $expiresInMinutes = $file = UploadedFile::fake()->image('shot.png', 50, 50); $url = signedUploadUrl($this->workspace, $token); - $tampered = str_replace("ws={$this->workspace->id}", "ws={$other->id}", $url); + $tampered = str_replace("workspace_id={$this->workspace->id}", "workspace_id={$other->id}", $url); $response = $this->postJson($tampered, ['media' => $file]); @@ -84,7 +84,7 @@ function signedUploadUrl(Workspace $ws, string $token, ?int $expiresInMinutes = $url = URL::temporarySignedRoute( 'api.uploads.store', now()->subMinute(), - ['token' => $token, 'ws' => $this->workspace->id], + ['token' => $token, 'workspace_id' => $this->workspace->id], ); $response = $this->postJson($url, ['media' => $file]);