diff --git a/app/Http/Controllers/FileViewController.php b/app/Http/Controllers/FileViewController.php index 1e15781a..a605ab68 100644 --- a/app/Http/Controllers/FileViewController.php +++ b/app/Http/Controllers/FileViewController.php @@ -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'); + } } diff --git a/resources/views/bending/base/form.blade.php b/resources/views/bending/base/form.blade.php index 253f74d6..8e47860d 100644 --- a/resources/views/bending/base/form.blade.php +++ b/resources/views/bending/base/form.blade.php @@ -247,7 +247,7 @@ class="w-full border border-gray-300 rounded px-3 py-1.5 text-sm {{ $isView ? 'b

형상 이미지

@if(!empty($imageFile)) - 전개도 + 전개도 @else 이미지 없음 @endif diff --git a/resources/views/bending/products/form.blade.php b/resources/views/bending/products/form.blade.php index f5a53677..0747740e 100644 --- a/resources/views/bending/products/form.blade.php +++ b/resources/views/bending/products/form.blade.php @@ -436,7 +436,7 @@ class="w-full border border-gray-300 rounded px-3 py-1.5 text-sm {{ $isView ? 'b

결합형태 이미지

@if(!empty($imageFile)) - 결합형태 + 결합형태 @else 이미지 없음 @endif diff --git a/routes/web.php b/routes/web.php index 637cd830..051cb8bd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -498,6 +498,7 @@ // 파일 뷰어 (API R2 이미지 프록시) Route::get('/files/{id}/view', [\App\Http\Controllers\FileViewController::class, 'show'])->whereNumber('id')->name('files.view'); + Route::get('/files/{id}/proxy', [\App\Http\Controllers\FileViewController::class, 'proxy'])->whereNumber('id')->name('files.proxy'); // 절곡품 기초관리 Route::prefix('bending')->name('bending.')->group(function () {