From 573e2afbbf4b5e2c6419b60802e8784e774dcf02 Mon Sep 17 00:00:00 2001 From: sindoring Date: Sun, 22 Mar 2026 01:22:07 +0400 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?= =?UTF-8?q?=D0=B8=D1=85=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D1=81=20?= =?UTF-8?q?s3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Api/LauncherController.php | 26 ++++++++++++------- config/moonwell.php | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Api/LauncherController.php b/app/Http/Controllers/Api/LauncherController.php index 25608d5..bcc37dc 100644 --- a/app/Http/Controllers/Api/LauncherController.php +++ b/app/Http/Controllers/Api/LauncherController.php @@ -6,7 +6,7 @@ use App\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Storage; use OpenApi\Attributes as OA; -use Symfony\Component\HttpFoundation\StreamedResponse; + class LauncherController extends Controller { @@ -72,8 +72,8 @@ class LauncherController extends Controller #[OA\Get( path: '/api/launcher/download/{path}', - summary: 'Скачать файл клиента', - description: 'Стримит файл из S3 по относительному пути из манифеста. Путь может содержать вложенные директории (например `Data/ruRU/patch-ruRU-3.MPQ`).', + summary: 'Получить ссылку на скачивание файла', + description: 'Возвращает временную presigned-ссылку на файл в S3. Лаунчер скачивает файл напрямую из хранилища по этой ссылке. Путь может содержать вложенные директории (например `Data/ruRU/patch-ruRU-3.MPQ`).', tags: ['Launcher'], security: [['bearerAuth' => []]], parameters: [ @@ -89,10 +89,12 @@ class LauncherController extends Controller responses: [ new OA\Response( response: 200, - description: 'Бинарное содержимое файла', - content: new OA\MediaType( - mediaType: 'application/octet-stream', - schema: new OA\Schema(type: 'string', format: 'binary'), + description: 'Временная ссылка на скачивание', + content: new OA\JsonContent( + properties: [ + new OA\Property(property: 'url', type: 'string', format: 'uri', description: 'Presigned URL для скачивания'), + new OA\Property(property: 'expires_in', type: 'integer', description: 'Время жизни ссылки в секундах', example: 3600), + ], ), ), new OA\Response( @@ -115,7 +117,7 @@ class LauncherController extends Controller ), ], )] - public function download(string $path): StreamedResponse|JsonResponse + public function download(string $path): JsonResponse { $disk = Storage::disk($this->disk()); $fullPath = $this->basePath().'/'.$path; @@ -124,7 +126,13 @@ class LauncherController extends Controller return response()->json(['message' => 'Файл не найден.'], 404); } - return $disk->download($fullPath); + $ttlMinutes = config('moonwell.launcher.download_ttl', 60); + $url = $disk->temporaryUrl($fullPath, now()->addMinutes($ttlMinutes)); + + return response()->json([ + 'url' => $url, + 'expires_in' => $ttlMinutes * 60, + ]); } private function disk(): string diff --git a/config/moonwell.php b/config/moonwell.php index a5ef9fd..423b881 100644 --- a/config/moonwell.php +++ b/config/moonwell.php @@ -28,6 +28,7 @@ return [ 'disk' => env('MOONWELL_LAUNCHER_DISK', 's3'), 'base_path' => env('MOONWELL_LAUNCHER_BASE_PATH', 'World of Warcraft'), 'manifest_key' => env('MOONWELL_LAUNCHER_MANIFEST_KEY', 'manifest.json'), + 'download_ttl' => (int) env('MOONWELL_LAUNCHER_DOWNLOAD_TTL', 60), ], 'realm' => [