diff --git a/openspec/changes/cloud-ready-s3-storage/.openspec.yaml b/openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/.openspec.yaml similarity index 100% rename from openspec/changes/cloud-ready-s3-storage/.openspec.yaml rename to openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/.openspec.yaml diff --git a/openspec/changes/cloud-ready-s3-storage/design.md b/openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/design.md similarity index 100% rename from openspec/changes/cloud-ready-s3-storage/design.md rename to openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/design.md diff --git a/openspec/changes/cloud-ready-s3-storage/proposal.md b/openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/proposal.md similarity index 100% rename from openspec/changes/cloud-ready-s3-storage/proposal.md rename to openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/proposal.md diff --git a/openspec/changes/cloud-ready-s3-storage/specs/env-cloud-annotations/spec.md b/openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/specs/env-cloud-annotations/spec.md similarity index 100% rename from openspec/changes/cloud-ready-s3-storage/specs/env-cloud-annotations/spec.md rename to openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/specs/env-cloud-annotations/spec.md diff --git a/openspec/changes/cloud-ready-s3-storage/specs/file-storage-s3/spec.md b/openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/specs/file-storage-s3/spec.md similarity index 100% rename from openspec/changes/cloud-ready-s3-storage/specs/file-storage-s3/spec.md rename to openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/specs/file-storage-s3/spec.md diff --git a/openspec/changes/cloud-ready-s3-storage/tasks.md b/openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/tasks.md similarity index 100% rename from openspec/changes/cloud-ready-s3-storage/tasks.md rename to openspec/changes/archive/2026-08-02-cloud-ready-s3-storage/tasks.md diff --git a/openspec/specs/env-cloud-annotations/spec.md b/openspec/specs/env-cloud-annotations/spec.md index 489dee2..6cc29b5 100644 --- a/openspec/specs/env-cloud-annotations/spec.md +++ b/openspec/specs/env-cloud-annotations/spec.md @@ -1,9 +1,9 @@ # env-cloud-annotations -`.env.example` 補充雲端部署必要環境變數的說明與正確預設值。 +## Purpose +`.env.example` 補充雲端部署必要環境變數的說明與正確預設值,讓開發者與維運人員能一眼看出哪些變數在雲端環境需要調整,避免沿用本機開發預設值導致部署問題。 ## Requirements - ### Requirement: QUEUE_CONNECTION 預設值為 redis `.env.example` 的 `QUEUE_CONNECTION` SHALL 預設為 `redis`,與 VPS 實際運作設定一致,避免新環境按範本初始化後跑在效能較差的 database queue。 @@ -13,7 +13,7 @@ ### Requirement: .env.example 標示雲端必要變數 `.env.example` SHALL 以行內註解或正確預設值標示以下雲端部署時必須明確設定的變數: -- `FILESYSTEM_DISK`:雲端應設為 `s3`(預設 `local` 在容器重啟後遺失上傳檔案) +- `FILESYSTEM_DISK`:**預設維持 `local`**(本機開發環境無 S3 相容服務可用);雲端部署時 SHALL 手動設為 `s3` 並補齊 `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_BUCKET`/`AWS_ENDPOINT`/`AWS_URL`/`AWS_USE_PATH_STYLE_ENDPOINT` 等變數,此時上傳行為 SHALL 實際切換至 S3 相容物件儲存(不再只是文件註解提醒,`public` disk 驅動確實依此變數切換) - `LOG_CHANNEL`:預設改為 `stderr`(雲端 logging aggregator 標準;原 `stack/daily` 寫檔案不適合雲端) - `QUEUE_CONNECTION`:預設為 `redis` - `SESSION_DRIVER`:預設改為 `redis`(取代 `database`,擴容更高效) @@ -26,6 +26,11 @@ - **WHEN** 開發者以 `.env.example` 為基礎建立 `.env`,未手動修改 `LOG_CHANNEL` - **THEN** Laravel 日誌寫入 stderr,可透過 `docker compose logs app` 查看 +#### Scenario: 新環境照 .env.example 初始化檔案儲存 +- **WHEN** 開發者以 `.env.example` 為基礎建立 `.env`,未手動修改 `FILESYSTEM_DISK` +- **THEN** 上傳檔案寫入本機磁碟,不需任何 S3 credentials 即可完整跑起來 + #### Scenario: 操作者閱讀 .env.example 進行雲端部署 - **WHEN** 操作者參照 `.env.example` 設定雲端環境的 `.env` -- **THEN** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值 +- **THEN** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值,且 `FILESYSTEM_DISK` 的說明明確指出改為 `s3` 後需一併設定哪些 `AWS_*` 變數 + diff --git a/openspec/specs/file-storage-s3/spec.md b/openspec/specs/file-storage-s3/spec.md new file mode 100644 index 0000000..5d37c68 --- /dev/null +++ b/openspec/specs/file-storage-s3/spec.md @@ -0,0 +1,37 @@ +# file-storage-s3 Specification + +## Purpose + +使用者上傳檔案(課程圖片、教練證照、聊天室圖片)可透過 `FILESYSTEM_DISK` 環境變數切換存放於本機磁碟或 S3 相容物件儲存,並提供既有檔案的一次性遷移工具。 + +## Requirements +### Requirement: 上傳檔案可切換為 S3 相容物件儲存 +`public` disk SHALL 支援透過 `FILESYSTEM_DISK` 與 `AWS_*` 環境變數切換底層儲存為 S3 相容物件儲存(如 Cloudflare R2),且不需修改任何呼叫 `Storage::disk('public')` 的既有程式碼。 + +#### Scenario: FILESYSTEM_DISK 設為 s3 時上傳走物件儲存 +- **WHEN** `.env` 設定 `FILESYSTEM_DISK=s3` 並提供有效的 `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_BUCKET`/`AWS_ENDPOINT` +- **THEN** 課程圖片上傳(`CourseImageController`)、教練證照上傳、預約聊天室圖片上傳皆寫入該 S3 相容物件儲存,本機 `storage/app/public/` 不再新增檔案 + +#### Scenario: FILESYSTEM_DISK 設為 local 時維持本機磁碟(開發環境預設) +- **WHEN** `.env` 未設定或設定 `FILESYSTEM_DISK=local` +- **THEN** 上傳行為與現行本機磁碟儲存完全一致,不需任何 S3 credentials + +#### Scenario: 顯示已上傳檔案的網址依 disk 驅動自動切換 +- **WHEN** 呼叫 `CourseImage`/`ProviderCertification` model 的 `Storage::disk('public')->url(...)` +- **THEN** 回傳的網址依當前 `FILESYSTEM_DISK` 設定,分別指向本機 `/storage/...` 路徑或 S3 相容物件儲存的公開網址 + +### Requirement: 既有本機檔案可一次性遷移至 S3 相容物件儲存 +系統 SHALL 提供一個 artisan command,將 `storage/app/public/` 下既有檔案上傳至當前設定的 S3 相容物件儲存,且不刪除本機原始檔案。 + +#### Scenario: 執行遷移 command 上傳既有檔案 +- **WHEN** 操作者執行 `php artisan storage:migrate-to-s3` +- **THEN** `storage/app/public/` 下所有檔案(不含 `storage:link` 符號連結本身)被上傳至 S3 相容物件儲存的對應相對路徑,且本機檔案保持不變 + +#### Scenario: 遷移 command 支援 dry-run 預覽 +- **WHEN** 操作者執行 `php artisan storage:migrate-to-s3 --dry-run` +- **THEN** command 僅列出將被上傳的檔案清單與總數,不實際執行任何上傳 + +#### Scenario: 部分檔案上傳失敗不中斷整體流程 +- **WHEN** 遷移過程中某個檔案上傳失敗(如網路中斷) +- **THEN** command 記錄該檔案為失敗、繼續處理其餘檔案,結束後印出失敗清單並以非 0 exit code 結束 +