https://github.com/gnuboard/g7/issues/125 — 상품 상세설명을 HTML 로 저장할 때 HTMLPurifier 가 모듈 vendor 폴더 안에 정의 캐시를 만들려다 실패해 저장이 매번 500 으로 끝나던 문제를 고친다. vendor 를 읽기 전용으로 두는 표준 배포에서 그 쓰기는 예외가 아니라 PHP 경고로 나오고 Laravel 이 이를 ErrorException 으로 승격시킨다. 캐시는 설정 해시당 1회만 기록되므로 캐시가 영영 생기지 않아 재시도해도 같은 결과였다. 캐시 경로를 storage 아래로 옮기고, 그 경로마저 확보하지 못하면 캐시만 끄고 정화는 그대로 수행한다 — 캐시는 성능 장치이고 정화는 보안 장치라, 전자의 실패가 후자를 건너뛰게 만들면 안 된다. 저장은 성공하므로 운영자에게 도달하는 흔적이 로그 하나뿐이라 error 수준으로 남긴다 (출하 기본 로그 수준이 error 라 warning 은 기본 설치 상태에서 파일에 남지 않는다). 그 과정에서 갈라져 있던 두 축을 코어 한 곳으로 모은다. - 쓰기 디렉토리 확보: 억제 생성·chmod·setgid·소유권 상속·쓰기 판정 절차가 정적 게시와 정의 캐시 두 곳에 서로 다른 하드닝으로 복제돼 있었다(억제 mkdir·setgid·clearstatcache 가 사본마다 한쪽씩 빠져 있었다). FilePermissionHelper 의 ensureWritableDirectory 와 hardenDirectory 로 통합하고, 실패 사유는 out 파라미터로 올려 정책(조용한 성능 저하 대 시끄러운 실패)은 호출부가 정하게 둔다. - 확장 저장 경로: storage_path('app/modules/…') 손조립이 30곳에 흩어져 있어 테스트 격리 분기를 넣으려면 사본마다 복제해야 했고, 한 곳만 빠뜨려도 그 확장의 테스트가 운영 설정 파일을 덮어쓴다. 디스크 root 를 단일 출처로 읽는 ExtensionStoragePath 로 전환하고 테스트 분기는 config/filesystems.php 한 줄에서 끝낸다. 함께 고친 것 - 테스트가 운영 라우트 캐시로 부팅해 확장 allowlist 가 라우트 축에서 통째로 무력화되던 문제. 삭제가 아니라 경로를 돌린다 — 라우트 캐시는 확장 작업 전까지 재생성되지 않아, 삭제하면 운영 사이트가 그때까지 라우트 파일 스캔 경로로 떨어진다. - PHPUnit 프로세스가 확장 vendor 의 제3자 composer 패키지를 오토로드하지 않아 그 패키지를 쓰는 코드 경로가 통째로 테스트 불가였던 문제. 확장 자신의 오토로더를 그대로 쓰면 활성 디렉토리가 _bundled 를 이기고 base path 유추까지 깨지므로, 생성된 맵에서 제3자 항목만 골라 별도 로더로 등록한다. - 게시 폴더가 setgid 를 갖지 않아, 명령줄과 웹이 번갈아 만든 하위 폴더를 다른 쪽이 쓰지 못하던 문제. - 관리자 템플릿이 HTML 정화 라이브러리를 직접 지정하지 않아 전이 의존으로 딸려온 구버전이 쓰이던 문제. 동반 산출물 - 규정 표(·AGENTS.md) 6행 + storage-driver/service-repository/testing-guide 문서 - audit 룰 2종 + coverage 6항목. 저장소가 이미 전량 전환돼 전수 실행이 공허 통과하므로 판정식은 픽스처 36건이 잠근다 - INSTALL.md 에 설치 후 파일 권한 절 추가 (vendor 쓰기 권한 불요를 명시)
155 lines
5.9 KiB
PHP
155 lines
5.9 KiB
PHP
<?php
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| 확장 저장 루트 (modules / plugins 디스크)
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| 테스트에서는 운영 데이터와 격리된 경로를 쓴다. 격리가 없으면 테스트가 실제
|
|
| `storage/app/modules/{id}/settings/*.json` 을 덮어써 운영 설정이 사라진다.
|
|
|
|
|
| 이 값이 확장 저장 위치의 단일 출처다. 각 확장이 `app()->runningUnitTests()` 로
|
|
| 같은 분기를 자기 안에 복사해 두면 한 곳만 빠뜨려도 그 확장의 테스트가 조용히
|
|
| 운영 파일을 건드린다 — 분기는 여기 한 곳에만 둔다.
|
|
|
|
|
*/
|
|
$extensionStorageRoot = env('APP_ENV') === 'testing' ? 'framework/testing' : 'app';
|
|
|
|
return [
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Default Filesystem Disk
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Here you may specify the default filesystem disk that should be used
|
|
| by the framework. The "local" disk, as well as a variety of cloud
|
|
| based disks are available to your application for file storage.
|
|
|
|
|
*/
|
|
|
|
'default' => env('FILESYSTEM_DISK', 'local'),
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Filesystem Disks
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Below you may configure as many filesystem disks as necessary, and you
|
|
| may even configure multiple disks for the same driver. Examples for
|
|
| most supported storage drivers are configured here for reference.
|
|
|
|
|
| Supported drivers: "local", "ftp", "sftp", "s3"
|
|
|
|
|
*/
|
|
|
|
'disks' => [
|
|
|
|
// serve => false: Laravel 이 자동 생성하는 GET/PUT /storage/{path} 라우트를 노출하지
|
|
// 않는다 (공개#52). 이 디스크들을 HTTP 로 직접 서빙하는 정상 흐름이 G7 에 없어
|
|
// (업로드는 전부 /api/.../attachments + StorageInterface, 확장 에셋은 public/build),
|
|
// 인증·권한 검사 없는 임의 경로 파일 쓰기(PUT) 입구를 닫는다. 디스크 자체는 그대로 동작.
|
|
'local' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path('app/private'),
|
|
'serve' => false,
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
'modules' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path($extensionStorageRoot.'/modules'),
|
|
'serve' => false,
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
'plugins' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path($extensionStorageRoot.'/plugins'),
|
|
'serve' => false,
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
'attachments' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path('app/attachments'),
|
|
'serve' => false,
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
'settings' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path('app/settings'),
|
|
'serve' => false,
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
// 확장(모듈/플러그인) 프론트엔드 IIFE/CSS 번들 병합 결과 캐시.
|
|
// ExtensionBundleService 가 version-in-path 파일명으로 저장/서빙한다.
|
|
//
|
|
// `permissions` 선언이 없으면 Flysystem 이 디스크 인스턴스화 시점에 root 를
|
|
// `0700` 으로 만든다. 그러면 **먼저 touch 한 프로세스가 독점**한다 — CLI 가 먼저면
|
|
// 이후 php-fpm 의 쓰기가 `UnableToWriteFile` 로 죽고, `throw => true` 라 그 예외가
|
|
// 공개 번들 엔드포인트의 500 으로 그대로 나간다. 디스크 생성 시점이 유일한 예방
|
|
// 지점이므로 여기서 그룹 쓰기를 열어 둔다 (실패 시 fail-soft 는 서비스가 담당).
|
|
'ext-bundles' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path('app/ext-bundles'),
|
|
'serve' => false,
|
|
'throw' => true,
|
|
'report' => false,
|
|
'permissions' => [
|
|
'file' => ['public' => 0664, 'private' => 0664],
|
|
'dir' => ['public' => 0775, 'private' => 0775],
|
|
],
|
|
],
|
|
|
|
'public' => [
|
|
'driver' => 'local',
|
|
'root' => storage_path('app/public'),
|
|
'url' => env('APP_URL').'/storage',
|
|
'visibility' => 'public',
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
's3' => [
|
|
'driver' => 's3',
|
|
'key' => env('AWS_ACCESS_KEY_ID'),
|
|
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
|
'region' => env('AWS_DEFAULT_REGION'),
|
|
'bucket' => env('AWS_BUCKET'),
|
|
// 빈 값(`AWS_URL=`/`AWS_ENDPOINT=`)은 미설정으로 정규화 — env() 는 키가
|
|
// 존재하면 빈 문자열을 돌려주므로, '' 가 endpoint 로 주입되면 S3 클라이언트
|
|
// 구성이 깨진다 (cp .env.example 설치 절차 방어).
|
|
'url' => env('AWS_URL') ?: null,
|
|
'endpoint' => env('AWS_ENDPOINT') ?: null,
|
|
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
|
'throw' => true,
|
|
'report' => false,
|
|
],
|
|
|
|
],
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Symbolic Links
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Here you may configure the symbolic links that will be created when the
|
|
| `storage:link` Artisan command is executed. The array keys should be
|
|
| the locations of the links and the values should be their targets.
|
|
|
|
|
*/
|
|
|
|
'links' => [
|
|
public_path('storage') => storage_path('app/public'),
|
|
],
|
|
|
|
];
|