fix: [document-template] R2 file_id 기반 이미지 URL 수정
- 문서양식 섹션 이미지를 file_id 기반 R2 프록시 URL로 변경 - getSectionImageUrl, _previewImageUrl에 file_id 우선 처리 추가 - 서버사이드 Blade(print, show)도 file_id 기반 URL 생성 - DocumentTemplateSection 모델에 file_id fillable 추가 - 업로드 응답에 file_id 반환, 복제 시 file_id 복사
This commit is contained in:
@@ -459,6 +459,7 @@ public function duplicate(Request $request, int $id): JsonResponse
|
||||
'template_id' => $newTemplate->id,
|
||||
'title' => $section->title,
|
||||
'image_path' => $section->image_path,
|
||||
'file_id' => $section->file_id,
|
||||
'sort_order' => $section->sort_order,
|
||||
]);
|
||||
|
||||
@@ -587,11 +588,12 @@ public function uploadImage(Request $request): JsonResponse
|
||||
|
||||
if ($response->successful() && $response->json('success')) {
|
||||
$filePath = $response->json('data.file_path');
|
||||
$fileId = $response->json('data.id');
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'path' => $filePath,
|
||||
'url' => "{$apiBaseUrl}/storage/tenants/{$filePath}",
|
||||
'file_id' => $fileId,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -700,6 +702,7 @@ private function saveRelations(DocumentTemplate $template, array $data, bool $de
|
||||
$sectionData = [
|
||||
'title' => $section['title'] ?? '',
|
||||
'image_path' => $section['image_path'] ?? null,
|
||||
'file_id' => $section['file_id'] ?? null,
|
||||
'sort_order' => $sIndex,
|
||||
];
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ class DocumentTemplateSection extends Model
|
||||
'title',
|
||||
'description',
|
||||
'image_path',
|
||||
'file_id',
|
||||
'sort_order',
|
||||
];
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"resources/css/app.css": {
|
||||
"file": "assets/app-PYBJ3HkM.css",
|
||||
"file": "assets/app-B0m1D6Gu.css",
|
||||
"src": "resources/css/app.css",
|
||||
"isEntry": true,
|
||||
"name": "app",
|
||||
@@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"resources/js/app.js": {
|
||||
"file": "assets/app-BX8sFXki.js",
|
||||
"file": "assets/app-4nAhvfTZ.js",
|
||||
"name": "app",
|
||||
"src": "resources/js/app.js",
|
||||
"isEntry": true
|
||||
|
||||
@@ -684,7 +684,7 @@ class="w-20 px-2 py-1 border border-gray-300 rounded text-xs">
|
||||
|
||||
// ===== 섹션 관리 =====
|
||||
function addSection() {
|
||||
const section = { id: generateId(), title: '', image_path: null, items: [] };
|
||||
const section = { id: generateId(), title: '', image_path: null, file_id: null, items: [] };
|
||||
templateState.sections.push(section);
|
||||
renderSections();
|
||||
}
|
||||
@@ -974,10 +974,10 @@ class="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm font-medium">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
${section.image_path ? `
|
||||
${(section.file_id || section.image_path) ? `
|
||||
<div class="px-4 py-2 bg-blue-50 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<img src="${getSectionImageUrl(section.image_path)}" alt="섹션 이미지" class="h-16 rounded border border-gray-200">
|
||||
<img src="${getSectionImageUrl(section)}" alt="섹션 이미지" class="h-16 rounded border border-gray-200">
|
||||
<span class="text-xs text-gray-500">이미지 첨부됨</span>
|
||||
</div>
|
||||
<button onclick="removeSectionImage('${section.id}')" class="text-red-500 hover:text-red-700 text-xs">
|
||||
@@ -1340,7 +1340,7 @@ function saveTemplate() {
|
||||
};
|
||||
|
||||
// 디버그: 섹션별 image_path 확인
|
||||
console.log('[saveTemplate] sections image_path:', data.sections.map(s => ({ id: s.id, title: s.title, image_path: s.image_path })));
|
||||
console.log('[saveTemplate] sections:', data.sections.map(s => ({ id: s.id, title: s.title, image_path: s.image_path, file_id: s.file_id })));
|
||||
|
||||
const url = templateState.id
|
||||
? `/api/admin/document-templates/${templateState.id}`
|
||||
@@ -1439,6 +1439,7 @@ function uploadSectionImage(sectionId, input) {
|
||||
const section = templateState.sections.find(s => s.id ==sectionId);
|
||||
if (section) {
|
||||
section.image_path = result.path;
|
||||
section.file_id = result.file_id;
|
||||
renderSections();
|
||||
showToast('이미지가 업로드되었습니다.', 'success');
|
||||
}
|
||||
@@ -1460,6 +1461,7 @@ function removeSectionImage(sectionId) {
|
||||
console.log('[removeSectionImage] found section:', section ? section.id : 'NOT FOUND', 'image_path:', section?.image_path);
|
||||
if (section) {
|
||||
section.image_path = null;
|
||||
section.file_id = null;
|
||||
try {
|
||||
renderSections();
|
||||
showToast('이미지가 삭제되었습니다. 저장 버튼을 눌러 반영하세요.', 'info');
|
||||
@@ -2170,15 +2172,16 @@ class="w-full px-2 py-1 border border-gray-200 rounded text-xs">`;
|
||||
}
|
||||
|
||||
// ===== 유틸리티 =====
|
||||
function getSectionImageUrl(imagePath) {
|
||||
function getSectionImageUrl(section) {
|
||||
// file_id 기반 (R2 프록시)
|
||||
if (section.file_id) {
|
||||
return '{{ rtrim(config("app.api_url", "http://api.sam.kr"), "/") }}/files/' + section.file_id + '/view';
|
||||
}
|
||||
// fallback: image_path 직접 사용 (레거시)
|
||||
const imagePath = section.image_path;
|
||||
if (!imagePath) return '';
|
||||
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) return imagePath;
|
||||
// 새 API 업로드 형식 (tenant path: 1/temp/2026/02/xxx.jpg)
|
||||
if (/^\d+\//.test(imagePath)) {
|
||||
return 'http://api.sam.kr/storage/tenants/' + imagePath;
|
||||
}
|
||||
// 레거시 형식 (document-templates/xxx.jpg) - MNG 로컬 스토리지
|
||||
return '/storage/' + imagePath;
|
||||
return '{{ rtrim(config("app.api_url", "http://api.sam.kr"), "/") }}/storage/tenants/' + imagePath;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
|
||||
@@ -35,16 +35,15 @@ function closePreviewModal() {
|
||||
if (e.key === 'Escape') closePreviewModal();
|
||||
});
|
||||
|
||||
// 이미지 URL 헬퍼 (API tenant storage vs MNG local storage)
|
||||
function _previewImageUrl(imagePath) {
|
||||
// 이미지 URL 헬퍼 (file_id 기반 R2 프록시)
|
||||
function _previewImageUrl(section) {
|
||||
if (section.file_id) {
|
||||
return '{{ rtrim(config("app.api_url", "http://api.sam.kr"), "/") }}/files/' + section.file_id + '/view';
|
||||
}
|
||||
const imagePath = section.image_path;
|
||||
if (!imagePath) return '';
|
||||
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) return imagePath;
|
||||
// 새 API 업로드 형식 (tenant path: 1/temp/2026/02/xxx.jpg)
|
||||
if (/^\d+\//.test(imagePath)) {
|
||||
return 'http://api.sam.kr/storage/tenants/' + imagePath;
|
||||
}
|
||||
// 레거시 형식 (document-templates/xxx.jpg)
|
||||
return '/storage/' + imagePath;
|
||||
return '{{ rtrim(config("app.api_url", "http://api.sam.kr"), "/") }}/storage/tenants/' + imagePath;
|
||||
}
|
||||
|
||||
// HTML 이스케이프
|
||||
@@ -636,8 +635,8 @@ function _getCheckLabels(col) {
|
||||
const imageSectionsHtml = imageSections.map(s => `
|
||||
<div class="mb-3">
|
||||
<p class="text-sm font-bold mb-1">■ ${_previewEsc(s.title)}</p>
|
||||
${s.image_path
|
||||
? `<img src="${_previewImageUrl(s.image_path)}" alt="${_previewEsc(s.title)}" class="max-h-48 mx-auto border rounded">`
|
||||
${(s.file_id || s.image_path)
|
||||
? `<img src="${_previewImageUrl(s)}" alt="${_previewEsc(s.title)}" class="max-h-48 mx-auto border rounded">`
|
||||
: `<div class="border border-dashed border-gray-300 rounded p-6 text-center text-gray-400 text-xs">이미지 미등록 (편집에서 업로드)</div>`
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -66,10 +66,10 @@ class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg transiti
|
||||
@endif
|
||||
|
||||
{{-- 검사 기준 이미지 --}}
|
||||
@if($section->image_path)
|
||||
@if($section->file_id || $section->image_path)
|
||||
@php
|
||||
$sectionImgUrl = preg_match('/^\d+\//', $section->image_path)
|
||||
? rtrim(config('app.api_url', 'http://api.sam.kr'), '/') . '/storage/tenants/' . $section->image_path
|
||||
$sectionImgUrl = $section->file_id
|
||||
? rtrim(config('app.api_url', 'http://api.sam.kr'), '/') . '/files/' . $section->file_id . '/view'
|
||||
: asset('storage/' . $section->image_path);
|
||||
@endphp
|
||||
<div class="mb-4">
|
||||
|
||||
@@ -425,12 +425,12 @@ class="px-2 py-2 text-center text-xs font-medium text-gray-600 border border-gra
|
||||
@endphp
|
||||
{{-- 검사기준서 섹션 (이미지만 렌더링) --}}
|
||||
@foreach(($document->template->sections ?? collect()) as $section)
|
||||
@if($section->title !== '중간검사 DATA' && $section->image_path)
|
||||
@if($section->title !== '중간검사 DATA' && ($section->file_id || $section->image_path))
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 mb-4">{{ $section->title }}</h2>
|
||||
@php
|
||||
$sectionImgUrl = preg_match('/^\d+\//', $section->image_path)
|
||||
? rtrim(config('app.api_url', 'http://api.sam.kr'), '/') . '/storage/tenants/' . $section->image_path
|
||||
$sectionImgUrl = $section->file_id
|
||||
? rtrim(config('app.api_url', 'http://api.sam.kr'), '/') . '/files/' . $section->file_id . '/view'
|
||||
: asset('storage/' . $section->image_path);
|
||||
@endphp
|
||||
<img src="{{ $sectionImgUrl }}" alt="{{ $section->title }}" class="max-w-full h-auto mb-4 rounded border">
|
||||
@@ -446,10 +446,10 @@ class="px-2 py-2 text-center text-xs font-medium text-gray-600 border border-gra
|
||||
@foreach($document->template->sections as $section)
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 mb-4">{{ $section->title }}</h2>
|
||||
@if($section->image_path)
|
||||
@if($section->file_id || $section->image_path)
|
||||
@php
|
||||
$sectionImgUrl = preg_match('/^\d+\//', $section->image_path)
|
||||
? rtrim(config('app.api_url', 'http://api.sam.kr'), '/') . '/storage/tenants/' . $section->image_path
|
||||
$sectionImgUrl = $section->file_id
|
||||
? rtrim(config('app.api_url', 'http://api.sam.kr'), '/') . '/files/' . $section->file_id . '/view'
|
||||
: asset('storage/' . $section->image_path);
|
||||
@endphp
|
||||
<img src="{{ $sectionImgUrl }}" alt="{{ $section->title }}" class="max-w-full h-auto mb-4 rounded border">
|
||||
|
||||
Reference in New Issue
Block a user