148 lines
5.9 KiB
PHP
148 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use OpenApi\Attributes as OA;
|
|
|
|
|
|
class LauncherController extends Controller
|
|
{
|
|
#[OA\Get(
|
|
path: '/api/launcher/manifest',
|
|
summary: 'Получить манифест файлов клиента',
|
|
description: 'Возвращает JSON-манифест со списком файлов клиента, их размерами и SHA-256 хешами. Лаунчер сверяет локальные файлы с манифестом и докачивает недостающие.',
|
|
tags: ['Launcher'],
|
|
security: [['bearerAuth' => []]],
|
|
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: 'Возвращает временную presigned-ссылку на файл в 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\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(
|
|
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): JsonResponse
|
|
{
|
|
$disk = Storage::disk($this->disk());
|
|
$fullPath = $this->basePath().'/'.$path;
|
|
|
|
if (! $disk->exists($fullPath)) {
|
|
return response()->json(['message' => 'Файл не найден.'], 404);
|
|
}
|
|
|
|
$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
|
|
{
|
|
return config('moonwell.launcher.disk', 's3');
|
|
}
|
|
|
|
private function basePath(): string
|
|
{
|
|
return config('moonwell.launcher.base_path', 'World of Warcraft');
|
|
}
|
|
}
|