Files
Gnuboard7/tests/scenarios/s3-storage-driver.yaml
T
HeuJung ea58c606a2 fix(core,admin_basic): S3 스토리지 드라이버 실동작 결함 수정 및 S3 호환 스토리지 연결 지원
공개 제보 — 파일 스토리지에서 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)
2026-08-13 15:15:19 +09:00

75 lines
5.2 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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