diff --git a/app/Domains/StorageTest/Controllers/S3TestController.php b/app/Domains/StorageTest/Controllers/S3TestController.php new file mode 100644 index 0000000..95beb08 --- /dev/null +++ b/app/Domains/StorageTest/Controllers/S3TestController.php @@ -0,0 +1,39 @@ +json( + $this->s3TestService->storeTestFile( + $request->file('file'), + $request->validated('directory'), + (int) $request->validated('expires_in_minutes', 10), + ), + 201, + ); + } + + public function temporaryUrl(GenerateS3TemporaryUrlRequest $request): JsonResponse + { + return response()->json( + $this->s3TestService->generateTemporaryUrl( + $request->validated('path'), + (int) $request->validated('expires_in_minutes', 10), + ) + ); + } +} diff --git a/app/Domains/StorageTest/Requests/GenerateS3TemporaryUrlRequest.php b/app/Domains/StorageTest/Requests/GenerateS3TemporaryUrlRequest.php new file mode 100644 index 0000000..b906053 --- /dev/null +++ b/app/Domains/StorageTest/Requests/GenerateS3TemporaryUrlRequest.php @@ -0,0 +1,24 @@ +> + */ + public function rules(): array + { + return [ + 'path' => ['required', 'string', 'max:2048'], + 'expires_in_minutes' => ['nullable', 'integer', 'min:1', 'max:1440'], + ]; + } +} diff --git a/app/Domains/StorageTest/Requests/StoreS3TestFileRequest.php b/app/Domains/StorageTest/Requests/StoreS3TestFileRequest.php new file mode 100644 index 0000000..183ca36 --- /dev/null +++ b/app/Domains/StorageTest/Requests/StoreS3TestFileRequest.php @@ -0,0 +1,25 @@ +> + */ + public function rules(): array + { + return [ + 'file' => ['required', 'file', 'max:10240'], + 'directory' => ['nullable', 'string', 'max:255'], + 'expires_in_minutes' => ['nullable', 'integer', 'min:1', 'max:1440'], + ]; + } +} diff --git a/app/Domains/StorageTest/Services/S3TestService.php b/app/Domains/StorageTest/Services/S3TestService.php new file mode 100644 index 0000000..3638ced --- /dev/null +++ b/app/Domains/StorageTest/Services/S3TestService.php @@ -0,0 +1,79 @@ + + */ + public function storeTestFile( + UploadedFile $file, + ?string $directory = null, + int $expiresInMinutes = 10, + ): array { + $directory = $this->normalizeDirectory($directory); + $disk = Storage::disk('s3'); + $path = $disk->putFile($directory, $file); + + if (! is_string($path) || $path === '') { + Log::error('S3 upload returned an empty path.', [ + 'disk' => 's3', + 'directory' => $directory, + 'original_name' => $file->getClientOriginalName(), + 'mime_type' => $file->getClientMimeType(), + 'size' => $file->getSize(), + ]); + + throw new RuntimeException('No se pudo subir el archivo al disco s3.'); + } + + return [ + 'disk' => 's3', + 'directory' => $directory, + 'path' => $path, + 'filename' => basename($path), + 'original_name' => $file->getClientOriginalName(), + 'mime_type' => $file->getClientMimeType(), + 'extension' => $file->extension(), + 'size' => $file->getSize(), + 'temporary_url' => $disk->temporaryUrl($path, now()->addMinutes($expiresInMinutes)), + 'temporary_url_expires_at' => now()->addMinutes($expiresInMinutes)->toIso8601String(), + ]; + } + + /** + * @return array + */ + public function generateTemporaryUrl(string $path, int $expiresInMinutes = 10): array + { + return [ + 'disk' => 's3', + 'path' => $path, + 'temporary_url' => $this->temporaryUrlForPath($path, $expiresInMinutes), + 'temporary_url_expires_at' => now()->addMinutes($expiresInMinutes)->toIso8601String(), + ]; + } + + protected function temporaryUrlForPath(string $path, int $expiresInMinutes): string + { + return Storage::disk('s3')->temporaryUrl($path, now()->addMinutes($expiresInMinutes)); + } + + protected function normalizeDirectory(?string $directory): string + { + $directory = trim((string) $directory, '/'); + + if ($directory !== '') { + return $directory; + } + + return 'testing/attachments/'.now()->format('Y/m/d').'/'.Str::uuid(); + } +} diff --git a/app/Domains/StorageTest/routes/api.php b/app/Domains/StorageTest/routes/api.php new file mode 100644 index 0000000..ef5ff59 --- /dev/null +++ b/app/Domains/StorageTest/routes/api.php @@ -0,0 +1,9 @@ +group(function (): void { + Route::post('upload', [S3TestController::class, 'store']); + Route::get('temporary-url', [S3TestController::class, 'temporaryUrl']); +}); diff --git a/postman/attachable-s3-testing.postman_collection.json b/postman/attachable-s3-testing.postman_collection.json new file mode 100644 index 0000000..f7eca10 --- /dev/null +++ b/postman/attachable-s3-testing.postman_collection.json @@ -0,0 +1,95 @@ +{ + "info": { + "_postman_id": "8faefdb8-734a-4262-a3b6-4d49e56ea901", + "name": "Storage Test S3", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { + "key": "base_url", + "value": "http://127.0.0.1:8000" + }, + { + "key": "expires_in_minutes", + "value": "10" + }, + { + "key": "path", + "value": "" + } + ], + "item": [ + { + "name": "Upload Test File", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "type": "file", + "src": [] + }, + { + "key": "directory", + "value": "testing/manual", + "type": "text" + }, + { + "key": "expires_in_minutes", + "value": "{{expires_in_minutes}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/storage-test/s3/upload", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "storage-test", + "s3", + "upload" + ] + }, + "description": "Sube un archivo al disco s3 y devuelve el path junto con una temporary_url." + }, + "response": [] + }, + { + "name": "Generate Temporary URL", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/storage-test/s3/temporary-url?path={{path}}&expires_in_minutes={{expires_in_minutes}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "storage-test", + "s3", + "temporary-url" + ], + "query": [ + { + "key": "path", + "value": "{{path}}" + }, + { + "key": "expires_in_minutes", + "value": "{{expires_in_minutes}}" + } + ] + }, + "description": "Genera una URL temporal para un path ya existente en S3." + }, + "response": [] + } + ] +} diff --git a/routes/api.php b/routes/api.php index 38f7355..5433745 100644 --- a/routes/api.php +++ b/routes/api.php @@ -9,4 +9,5 @@ Route::get('/user', function (Request $request) { require __DIR__.'/../app/Domains/Catalog/routes/api.php'; require __DIR__.'/../app/Domains/Cart/routes/api.php'; +require __DIR__.'/../app/Domains/StorageTest/routes/api.php'; require __DIR__.'/../app/Domains/Tenant/routes/api.php';