공개 제보 — 파일 스토리지에서 S3 를 선택해 저장해도 실제 파일 저장이 동작하지 않던 결함의 전면 수정. - S3 어댑터(league/flysystem-aws-s3-v3)·predis 를 코어 기본 의존성으로 포함 — 어댑터 부재 즉사, phpredis 확장 없는 서버의 redis 선택 전면 다운 차단 (부트 시 확장 부재 감지 → predis 자동 폴백) - storage_driver=s3 저장 시 코어 첨부 업로드 디스크를 s3 로 전환 (ATTACHMENT_DISK env 명시가 항상 우선, 기존 행은 저장 당시 disk 로 서빙) - 첨부·템플릿 레이아웃 첨부 서빙을 행 disk 를 따르는 스토리지 스트림으로 교체 — 로컬 절대 경로 전제 fileResponse 는 S3 행에서 filemtime stat 500 (streamedFileResponse: 행 메타 기반 ETag/304/Cache-Control) - S3 호환 스토리지(R2/MinIO/NCP) 연결 지원: 엔드포인트 URL·path-style 설정 신설, 리전 목록 선택 → 자유 입력 전환, 연결 테스트를 실제 저장 경로와 동일 설정(endpoint/path-style)으로 정렬 - 사용 불능 드라이버(어댑터·PHP 확장 부재)의 저장/테스트 요청을 사유와 함께 422 로 차단하는 서버 게이트 신설 (DriverRegistryService 능력 판정) - 웹소켓 연결 테스트에 서버(백엔드 발송용) endpoint 검사 추가 — 클라이언트만 검사해 테스트 성공 + 실제 발송 실패가 가능하던 비대칭 해소 - env 빈 값(`KEY=`) 함정 정규화: AWS_URL/AWS_ENDPOINT/ATTACHMENT_DISK 빈 문자열을 미설정으로 취급 (config 정규화 + 예시 파일 주석 처리) - 플러그인 드라이버 폴백의 log 카테고리 죽은 키(logging.default) 정정 및 websocket 유령 설정 키 제거 - 실 AWS S3 종단 검증 완료 (설정 저장 → 업로드 S3 실저장 → 서빙 200/304)
75 lines
5.2 KiB
YAML
75 lines
5.2 KiB
YAML
feature: S3 스토리지 드라이버 실동작 (공개 #99 / 내부 #563)
|
||
|
||
description: |
|
||
관리자 > 환경설정 > 드라이버의 S3 선택이 실제 파일 저장으로 이어지게 하는 결함 수정.
|
||
|
||
핵심 동작:
|
||
- league/flysystem-aws-s3-v3 / predis 코어 의존성 포함 (해석 시점 즉사 소멸)
|
||
- s3_endpoint / s3_use_path_style 신설 → filesystems.disks.s3.{endpoint,use_path_style_endpoint} 주입
|
||
(S3 호환 스토리지 R2/MinIO/NCP 연결 경로)
|
||
- s3_region 3중 하드코딩 제거 → 형식 검증(regex ^[a-z0-9-]+$, max 64) 완화
|
||
- 드라이버 가용성 서버 게이트: 어댑터 클래스/PHP 확장 부재 드라이버 저장·테스트 422
|
||
- 코어 첨부 업로드 디스크: ATTACHMENT_DISK env > storage_driver=s3 → s3 > attachments
|
||
(기존 행은 행 disk 서빙 — 혼재 안전)
|
||
- 연결 테스트: 어댑터 선검사 + endpoint/path-style 반영, websocket 은 client/server
|
||
양측 endpoint probe (백엔드 발송은 server endpoint 사용)
|
||
|
||
axes:
|
||
region_shape: [aws_region, auto, custom_minio, invalid_format]
|
||
endpoint: [absent, custom_url, path_style]
|
||
driver_usability: [adapter_present, adapter_absent]
|
||
attachment_disk: [env_override, follow_s3, local_default]
|
||
|
||
exclusions:
|
||
- { region_shape: invalid_format, endpoint: custom_url, reason: "리전 검증이 선행 422 — endpoint 축 도달 전 차단" }
|
||
- { region_shape: invalid_format, endpoint: path_style, reason: "동일" }
|
||
- { region_shape: invalid_format, attachment_disk: env_override, reason: "저장 실패 시 디스크 전환 미발생" }
|
||
- { region_shape: invalid_format, attachment_disk: follow_s3, reason: "동일" }
|
||
- { driver_usability: adapter_absent, endpoint: custom_url, reason: "가용성 게이트가 선행 422 — 어댑터는 코어 필수 의존성이라 부재 상태는 memcached 확장 축으로 대변" }
|
||
- { driver_usability: adapter_absent, endpoint: path_style, reason: "동일" }
|
||
- { driver_usability: adapter_absent, attachment_disk: follow_s3, reason: "저장 차단 시 디스크 전환 미발생" }
|
||
- { driver_usability: adapter_absent, attachment_disk: env_override, reason: "동일" }
|
||
- { driver_usability: adapter_absent, region_shape: invalid_format, reason: "리전 검증이 선행 422 — 가용성 축 도달 전 차단" }
|
||
- { driver_usability: adapter_absent, region_shape: auto, reason: "가용성 게이트는 드라이버 ID 만 판정 — 리전 값과 직교, aws_region 대표 조합으로 검증" }
|
||
- { driver_usability: adapter_absent, region_shape: custom_minio, reason: "동일" }
|
||
- { attachment_disk: env_override, region_shape: auto, reason: "디스크 전환 판정은 storage_driver 값만 본다 — 리전·endpoint 와 직교, aws_region×absent 대표 조합으로 검증" }
|
||
- { attachment_disk: env_override, region_shape: custom_minio, reason: "동일" }
|
||
- { attachment_disk: env_override, endpoint: custom_url, reason: "동일" }
|
||
- { attachment_disk: env_override, endpoint: path_style, reason: "동일" }
|
||
- { attachment_disk: follow_s3, region_shape: auto, reason: "디스크 전환 판정은 storage_driver 값만 본다 — aws_region×absent(단위)·custom_minio×path_style(Feature 왕복) 두 대표 조합으로 검증" }
|
||
- { attachment_disk: follow_s3, endpoint: custom_url, reason: "동일" }
|
||
- { attachment_disk: follow_s3, region_shape: aws_region, endpoint: path_style, reason: "동일" }
|
||
- { attachment_disk: follow_s3, region_shape: custom_minio, endpoint: absent, reason: "동일" }
|
||
- { region_shape: auto, endpoint: absent, reason: "R2 의 auto 리전은 커스텀 endpoint 전제 — endpoint 없는 auto 는 실사용 조합이 아님" }
|
||
- { region_shape: auto, endpoint: path_style, reason: "R2 는 virtual-host 스타일 — path_style 조합은 custom_minio 축이 대표" }
|
||
- { region_shape: custom_minio, endpoint: absent, reason: "MinIO 리전 값은 커스텀 endpoint 전제 — endpoint 없는 조합은 실사용이 아님" }
|
||
- { region_shape: custom_minio, endpoint: custom_url, reason: "MinIO 는 path-style 대표 조합으로 검증 — virtual-host 모드도 endpoint 주입 경로는 동일" }
|
||
|
||
effects:
|
||
- s3_disk_resolves
|
||
- endpoint_injected
|
||
- attachment_upload_follows_driver
|
||
- unusable_driver_rejected
|
||
- invalid_region_rejected
|
||
- test_covers_server_websocket
|
||
- unauthenticated_returns_401
|
||
# E2E/렌더 표면 효과 (Playwright·Vitest 가 마킹)
|
||
- region_free_input_mounted
|
||
- endpoint_field_mounted
|
||
- path_style_toggle_mounted
|
||
- local_roundtrip_preserves_new_keys
|
||
|
||
# E2E 표면 명세 (문서화) — 커버리지 집계는 아래 test_files 의 @scenario/@effects 마킹이 담당
|
||
e2e:
|
||
surfaces: [region_free_input, endpoint_field, path_style_toggle, save_gating]
|
||
|
||
test_files:
|
||
- tests/Playwright/specs/admin/settings-drivers-s3-endpoint.spec.ts
|
||
- tests/Unit/Providers/SettingsServiceProviderS3ConfigTest.php
|
||
- tests/Unit/Services/DriverConnectionTesterTest.php
|
||
- tests/Unit/Services/DriverRegistryServiceUsabilityTest.php
|
||
- tests/Feature/Api/Admin/SettingsControllerS3ValidationTest.php
|
||
- tests/Feature/Api/Public/AttachmentS3DiskServingTest.php
|
||
- tests/Unit/Services/TemplateLayoutAttachmentServingTest.php
|
||
- templates/_bundled/sirsoft-admin_basic/__tests__/layouts/admin-settings-drivers-s3-fields.test.tsx
|