[]]], responses: [ new OA\Response( response: 200, description: 'Манифест файлов', content: new OA\JsonContent( properties: [ new OA\Property( property: 'files', type: 'array', items: new OA\Items( properties: [ new OA\Property(property: 'path', type: 'string', example: 'Data/common.MPQ'), new OA\Property(property: 'size', type: 'integer', format: 'int64', example: 2881154862), new OA\Property(property: 'sha256', type: 'string', format: 'hex', example: 'd850ffa5efd6a1ba845899a7d9f9ad27f6eea7ac606e95033506c72c86ee0236'), ], ), ), ], ), ), new OA\Response( response: 401, description: 'Не авторизован', content: new OA\JsonContent( properties: [ new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.'), ], ), ), new OA\Response( response: 404, description: 'Манифест не найден в хранилище', content: new OA\JsonContent( properties: [ new OA\Property(property: 'message', type: 'string', example: 'Манифест не найден.'), ], ), ), ], )] public function manifest(): JsonResponse { $disk = Storage::disk($this->disk()); $key = config('moonwell.launcher.manifest_key'); if (! $disk->exists($key)) { return response()->json(['message' => 'Манифест не найден.'], 404); } $manifest = json_decode($disk->get($key), true); return response()->json($manifest); } #[OA\Get( path: '/api/launcher/download/{path}', summary: 'Скачать файл клиента', description: 'Стримит файл из S3 по относительному пути из манифеста. Путь может содержать вложенные директории (например `Data/ruRU/patch-ruRU-3.MPQ`).', tags: ['Launcher'], security: [['bearerAuth' => []]], parameters: [ new OA\Parameter( name: 'path', in: 'path', required: true, description: 'Относительный путь к файлу из манифеста', schema: new OA\Schema(type: 'string'), example: 'Data/common.MPQ', ), ], responses: [ new OA\Response( response: 200, description: 'Бинарное содержимое файла', content: new OA\MediaType( mediaType: 'application/octet-stream', schema: new OA\Schema(type: 'string', format: 'binary'), ), ), new OA\Response( response: 401, description: 'Не авторизован', content: new OA\JsonContent( properties: [ new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.'), ], ), ), new OA\Response( response: 404, description: 'Файл не найден', content: new OA\JsonContent( properties: [ new OA\Property(property: 'message', type: 'string', example: 'Файл не найден.'), ], ), ), ], )] public function download(string $path): StreamedResponse|JsonResponse { $disk = Storage::disk($this->disk()); $fullPath = $this->basePath().'/'.$path; if (! $disk->exists($fullPath)) { return response()->json(['message' => 'Файл не найден.'], 404); } return $disk->download($fullPath); } private function disk(): string { return config('moonwell.launcher.disk', 's3'); } private function basePath(): string { return config('moonwell.launcher.base_path', 'World of Warcraft'); } }