feat(core,ecommerce,ckeditor5,board,admin_basic): 공개 자산 직접 URL(CDN) 서빙 옵트인

StorageInterface::url 의 public 하드코딩을 완화하고 core.storage.filter_url 훅을
신설해, 완전 공개 자산(상품·카테고리·리뷰·에디터 이미지)의 직접 URL 서빙을 옵트인으로
열었다. 미설정 시 기존 스트리밍 100% 보존.

디스크 결정은 확장 개별 설정 > 코어 전역 > 미설정 순이며, 서빙·삭제·이동은 행에 기록된
disk 를 따라 전환 전후 자산이 혼재해도 각자 실제 저장 위치를 향한다. 고아 disk(공급
플러그인 비활성화)는 스트리밍으로 폴백해 500 이 되지 않는다.

공개 이슈 (@lyg-kaban 건의) 대응.
This commit is contained in:
HeuJung
2026-08-14 08:05:54 +09:00
parent 37fad55333
commit 057e002ad1
125 changed files with 5913 additions and 203 deletions
+14 -12
View File
@@ -3,7 +3,9 @@
namespace App\Extension\Storage;
use App\Contracts\Extension\StorageInterface;
use App\Extension\Storage\Concerns\ResolvesPublicUrl;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* 모듈 스토리지 드라이버
@@ -13,6 +15,8 @@ use Illuminate\Support\Facades\Storage;
*/
class ModuleStorageDriver implements StorageInterface
{
use ResolvesPublicUrl;
/**
* 모듈 식별자
*/
@@ -119,18 +123,16 @@ class ModuleStorageDriver implements StorageInterface
}
/**
* {@inheritDoc}
* `core.storage.filter_url` 훅 컨텍스트의 드라이버별 식별 정보를 반환합니다.
*
* @return array{scope: string, identifier: ?string} scope + 모듈 식별자
*/
public function url(string $category, string $path): ?string
protected function urlHookContext(): array
{
// public disk인 경우에만 직접 URL 반환
if ($this->disk !== 'public') {
return null;
}
$fullPath = $this->resolvePath($category, $path);
return Storage::disk($this->disk)->url($fullPath);
return [
'scope' => 'module',
'identifier' => $this->identifier,
];
}
/**
@@ -182,7 +184,7 @@ class ModuleStorageDriver implements StorageInterface
/**
* {@inheritDoc}
*/
public function response(string $category, string $path, string $filename, array $headers = []): ?\Symfony\Component\HttpFoundation\StreamedResponse
public function response(string $category, string $path, string $filename, array $headers = []): ?StreamedResponse
{
$fullPath = $this->resolvePath($category, $path);
@@ -207,7 +209,7 @@ class ModuleStorageDriver implements StorageInterface
/**
* {@inheritDoc}
*/
public function download(string $category, string $path, string $filename, array $headers = []): ?\Symfony\Component\HttpFoundation\StreamedResponse
public function download(string $category, string $path, string $filename, array $headers = []): ?StreamedResponse
{
$fullPath = $this->resolvePath($category, $path);