fix: [bending] Canvas 편집기용 이미지 프록시 라우트 추가 (R2 CORS 우회)

This commit is contained in:
김보곤
2026-03-21 10:35:12 +09:00
parent f03ce495f1
commit cc58a0f37a
4 changed files with 57 additions and 2 deletions

View File

@@ -54,4 +54,58 @@ public function show(int $id)
return redirect($url);
}
/**
* 이미지 프록시 (Canvas 편집기용)
*
* R2에서 이미지를 서버 사이드로 다운로드하여 같은 도메인에서 스트리밍.
* crossOrigin 요청 없이 Canvas에서 taint 없이 사용 가능.
*/
public function proxy(int $id)
{
$cacheKey = "file_presigned_url:{$id}";
$url = Cache::remember($cacheKey, now()->addMinutes(5), function () use ($id) {
$baseUrl = config('services.api.base_url', 'https://api.sam.kr');
$internalUrl = config('services.api.internal_url');
$apiKey = config('services.api.key');
$token = session('api_access_token', '');
$headers = [
'X-API-KEY' => $apiKey,
'X-TENANT-ID' => session('selected_tenant_id', 1),
];
if ($internalUrl) {
$headers['Host'] = parse_url($baseUrl, PHP_URL_HOST) ?: 'api.sam.kr';
$baseUrl = $internalUrl;
}
$response = Http::baseUrl($baseUrl)
->withoutVerifying()
->withHeaders($headers)
->withToken($token)
->timeout(10)
->get("/api/v1/files/{$id}/presigned-url");
return $response->successful() ? $response->json('data.url') : null;
});
if (! $url) {
Cache::forget($cacheKey);
abort(404);
}
$imageResponse = Http::withoutVerifying()->timeout(15)->get($url);
if (! $imageResponse->successful()) {
abort(404);
}
$contentType = $imageResponse->header('Content-Type') ?: 'image/png';
return response($imageResponse->body(), 200)
->header('Content-Type', $contentType)
->header('Cache-Control', 'public, max-age=300');
}
}