From cd339ca17aa56e4cdd7e4ac07c14b04745127ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 24 Feb 2026 17:57:14 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[esign]=20=EB=B2=95=EC=9D=B8=EB=8F=84?= =?UTF-8?q?=EC=9E=A5=20GCS=20=EB=AF=B8=EC=84=A4=EC=A0=95=20=EC=8B=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=BB=AC=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EC=A7=80=20?= =?UTF-8?q?=ED=8F=B4=EB=B0=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ESign/EsignApiController.php | 134 ++++++++++++------ routes/web.php | 1 + 2 files changed, 95 insertions(+), 40 deletions(-) diff --git a/app/Http/Controllers/ESign/EsignApiController.php b/app/Http/Controllers/ESign/EsignApiController.php index 0c84814e..c7e16168 100644 --- a/app/Http/Controllers/ESign/EsignApiController.php +++ b/app/Http/Controllers/ESign/EsignApiController.php @@ -145,23 +145,63 @@ public function getStamp(): JsonResponse ->where('setting_key', 'company_stamp') ->first(); - if (! $setting || empty($setting->setting_value['gcs_object'])) { + if (! $setting) { return response()->json(['success' => true, 'data' => null]); } - $gcs = app(GoogleCloudStorageService::class); - $signedUrl = $gcs->getSignedUrl($setting->setting_value['gcs_object'], 60); + $value = $setting->setting_value; + + // 로컬 스토리지 + if (! empty($value['local_path'])) { + if (Storage::disk('local')->exists($value['local_path'])) { + $imageUrl = route('esign.contracts.stamp.image', ['tenant' => $tenantId]); + + return response()->json([ + 'success' => true, + 'data' => ['image_url' => $imageUrl], + ]); + } - if (! $signedUrl) { return response()->json(['success' => true, 'data' => null]); } - return response()->json([ - 'success' => true, - 'data' => [ - 'image_url' => $signedUrl, - ], - ]); + // GCS 스토리지 (레거시) + if (! empty($value['gcs_object'])) { + $gcs = app(GoogleCloudStorageService::class); + $signedUrl = $gcs->getSignedUrl($value['gcs_object'], 60); + if ($signedUrl) { + return response()->json([ + 'success' => true, + 'data' => ['image_url' => $signedUrl], + ]); + } + } + + return response()->json(['success' => true, 'data' => null]); + } + + /** + * 법인도장 이미지 서빙 (로컬 스토리지용) + */ + public function serveStampImage(int $tenant) + { + $setting = TenantSetting::where('tenant_id', $tenant) + ->where('setting_group', 'esign') + ->where('setting_key', 'company_stamp') + ->first(); + + if (! $setting || empty($setting->setting_value['local_path'])) { + abort(404); + } + + $path = $setting->setting_value['local_path']; + if (! Storage::disk('local')->exists($path)) { + abort(404); + } + + return response(Storage::disk('local')->get($path)) + ->header('Content-Type', 'image/png') + ->header('Cache-Control', 'private, max-age=3600'); } /** @@ -181,51 +221,60 @@ public function uploadStamp(Request $request): JsonResponse } $gcs = app(GoogleCloudStorageService::class); - if (! $gcs->isAvailable()) { - return response()->json(['success' => false, 'message' => 'GCS 설정이 되어 있지 않습니다.'], 500); - } - // 기존 GCS 파일 삭제 + // 기존 파일 삭제 $existing = TenantSetting::where('tenant_id', $tenantId) ->where('setting_group', 'esign') ->where('setting_key', 'company_stamp') ->first(); - if ($existing && ! empty($existing->setting_value['gcs_object'])) { - $gcs->delete($existing->setting_value['gcs_object']); + if ($existing) { + $val = $existing->setting_value; + if (! empty($val['gcs_object']) && $gcs->isAvailable()) { + $gcs->delete($val['gcs_object']); + } + if (! empty($val['local_path'])) { + Storage::disk('local')->delete($val['local_path']); + } } - // 임시 파일에 저장 후 GCS 업로드 - $tmpFile = tempnam(sys_get_temp_dir(), 'stamp_'); - file_put_contents($tmpFile, $imageData); + // GCS 사용 가능하면 GCS에 업로드 + if ($gcs->isAvailable()) { + $tmpFile = tempnam(sys_get_temp_dir(), 'stamp_'); + file_put_contents($tmpFile, $imageData); - $gcsObject = "esign/{$tenantId}/stamps/company_stamp.png"; - $gcsUri = $gcs->upload($tmpFile, $gcsObject); - unlink($tmpFile); + $gcsObject = "esign/{$tenantId}/stamps/company_stamp.png"; + $gcsUri = $gcs->upload($tmpFile, $gcsObject); + unlink($tmpFile); - if (! $gcsUri) { - return response()->json(['success' => false, 'message' => 'GCS 업로드에 실패했습니다.'], 500); + if ($gcsUri) { + TenantSetting::updateOrCreate( + ['tenant_id' => $tenantId, 'setting_group' => 'esign', 'setting_key' => 'company_stamp'], + ['setting_value' => ['gcs_object' => $gcsObject], 'updated_by' => auth()->id()] + ); + + return response()->json([ + 'success' => true, + 'message' => '법인도장이 등록되었습니다.', + 'data' => ['image_url' => $gcs->getSignedUrl($gcsObject, 60)], + ]); + } } + // GCS 미사용 → 로컬 스토리지 + $localPath = "esign/stamps/{$tenantId}/company_stamp.png"; + Storage::disk('local')->put($localPath, $imageData); + TenantSetting::updateOrCreate( - [ - 'tenant_id' => $tenantId, - 'setting_group' => 'esign', - 'setting_key' => 'company_stamp', - ], - [ - 'setting_value' => ['gcs_object' => $gcsObject], - 'updated_by' => auth()->id(), - ] + ['tenant_id' => $tenantId, 'setting_group' => 'esign', 'setting_key' => 'company_stamp'], + ['setting_value' => ['local_path' => $localPath], 'updated_by' => auth()->id()] ); - $signedUrl = $gcs->getSignedUrl($gcsObject, 60); + $imageUrl = route('esign.contracts.stamp.image', ['tenant' => $tenantId]); return response()->json([ 'success' => true, 'message' => '법인도장이 등록되었습니다.', - 'data' => [ - 'image_url' => $signedUrl, - ], + 'data' => ['image_url' => $imageUrl], ]); } @@ -241,10 +290,15 @@ public function deleteStamp(): JsonResponse ->first(); if ($setting) { - $gcsObject = $setting->setting_value['gcs_object'] ?? null; - if ($gcsObject) { + $val = $setting->setting_value; + if (! empty($val['gcs_object'])) { $gcs = app(GoogleCloudStorageService::class); - $gcs->delete($gcsObject); + if ($gcs->isAvailable()) { + $gcs->delete($val['gcs_object']); + } + } + if (! empty($val['local_path'])) { + Storage::disk('local')->delete($val['local_path']); } $setting->delete(); } diff --git a/routes/web.php b/routes/web.php index 014e3607..00d05035 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1502,6 +1502,7 @@ Route::get('/stamp', [EsignApiController::class, 'getStamp'])->name('stamp.get'); Route::post('/stamp', [EsignApiController::class, 'uploadStamp'])->name('stamp.upload'); Route::delete('/stamp', [EsignApiController::class, 'deleteStamp'])->name('stamp.delete'); + Route::get('/stamp/image/{tenant}', [EsignApiController::class, 'serveStampImage'])->name('stamp.image'); Route::get('/search-partners', [EsignApiController::class, 'searchPartners'])->name('search-partners'); Route::get('/search-tenants', [EsignApiController::class, 'searchTenants'])->name('search-tenants'); Route::get('/generate-contract-number', [EsignApiController::class, 'generateContractNumber'])->name('generate-contract-number');