chore(openspec): 新增 cloud-ready-s3-storage change 規劃
延續 P1 雲原生改造方向,規劃將上傳檔案(課程圖片、教練證照、聊天室圖片) 改存 S3 相容物件儲存,實現 container 無狀態化。核心設計:public disk driver 直接吃 FILESYSTEM_DISK env 切換,既有 6 個呼叫點零改動,本機開發 環境維持 local 預設不受影響。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LpcY9b7y9x4fusHBTXciQ
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-08-02
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
現況:
|
||||||
|
- 上傳功能有 6 個呼叫點,全部寫死 `Storage::disk('public')`(`CourseImageController`、`CompressesImages` trait、`CourseImage`/`ProviderCertification` model 的 `Storage::disk('public')->url(...)`、`BookingMessageController`)
|
||||||
|
- `config/filesystems.php` 的 `default` 設定(`FILESYSTEM_DISK` env)**沒有任何程式碼讀取**,因為所有呼叫點都直接指名 `'public'`,不吃 default disk
|
||||||
|
- `public` disk 目前寫死 `driver => local`,`root => storage_path('app/public')`
|
||||||
|
- `s3` disk 設定區塊已存在(Laravel 骨架預設),但未安裝 `league/flysystem-aws-s3-v3`,且應用程式從未使用這個 disk key
|
||||||
|
- 本機開發環境(docker-compose)沒有任何 S3 相容服務(無 MinIO container)
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- 上傳檔案(課程圖片、教練證照、聊天室圖片)改存 S3 相容物件儲存,實現 container 無狀態化,容器可自由重建/複製/水平擴容而不遺失使用者資料
|
||||||
|
- 零改動 6 個既有呼叫點程式碼
|
||||||
|
- 本機開發環境不受影響,不需要開發者額外申請 S3 帳號才能跑起來
|
||||||
|
- 既有已上傳檔案完整遷移,切換後所有舊圖片網址仍可正常顯示
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- 不處理 P0 另一項待辦「移除 bind mount / code 烘進 image」
|
||||||
|
- 不引入 MinIO 或任何本機 S3 模擬服務——開發環境維持 local disk
|
||||||
|
- 不重構 6 個呼叫點的程式碼結構(即使 `Storage::disk('public')` 這種寫死字串不是最佳實踐,此次不動,避免範圍擴大)
|
||||||
|
- 不處理 `storage/logs/` 或其他非使用者上傳內容的磁碟佔用(那是 log rotation 的範疇,非本次目標)
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
### D1:讓 `public` disk 的 `driver` 本身吃 env,而非新增第二顆 disk
|
||||||
|
|
||||||
|
`config/filesystems.php` 的 `public` disk 改成:
|
||||||
|
|
||||||
|
```php
|
||||||
|
'public' => [
|
||||||
|
'driver' => env('FILESYSTEM_DISK', 'local'),
|
||||||
|
'root' => storage_path('app/public'),
|
||||||
|
'url' => env('AWS_URL') ?: env('APP_URL').'/storage',
|
||||||
|
'visibility' => 'public',
|
||||||
|
'throw' => false,
|
||||||
|
// S3 專用欄位,driver=local 時被忽略,不影響本機開發
|
||||||
|
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||||
|
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||||
|
'region' => env('AWS_DEFAULT_REGION'),
|
||||||
|
'bucket' => env('AWS_BUCKET'),
|
||||||
|
'endpoint' => env('AWS_ENDPOINT'),
|
||||||
|
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
**為什麼不是新增一顆 `s3` disk 再改 6 個呼叫點指過去**:呼叫點越少改動風險越低;`FILESYSTEM_DISK` 這個 env 名稱原本就是為了「決定用什麼儲存」而存在(`env-cloud-annotations` spec 也是這樣描述的語意),只是它原本控制的是沒人用的 `default` key。把它直接接到 `public` disk 的 `driver`,是重新賦予這個既有 env 變數「做它原本應該做的事」,不是新造一個變數。
|
||||||
|
|
||||||
|
**取捨**:這是刻意偏離 Laravel 慣例(`FILESYSTEM_DISK` 官方語意是選預設 disk,不是切換單一 disk 的 driver)。因為程式碼現狀已經把 `public` 寫死,順著現狀改動 blast radius 最小;若未來重構掉 6 個寫死呼叫點,屆時可以再拆回標準的雙 disk 寫法。
|
||||||
|
|
||||||
|
### D2:本機開發維持 `local`,`.env.example` 預設不動
|
||||||
|
|
||||||
|
`.env.example` 的 `FILESYSTEM_DISK=local` 保留不變(只更新註解文字),開發者 clone 專案照舊直接動。VPS 的 `.env`(不進版控)手動改成 `FILESYSTEM_DISK=s3` + 補齊 `AWS_*` credentials,跟 `cloud-ready-p1` 階段 Session/Log 的做法一致(機敏值不走 CI 自動化,一次性手動 SSH 改)。
|
||||||
|
|
||||||
|
### D3:S3 相容服務選 Cloudflare R2
|
||||||
|
|
||||||
|
理由:免費額度(10GB 儲存 + 無出口流量費)對目前用量綽綽有餘;API 相容 S3,`league/flysystem-aws-s3-v3` 原生支援(透過 `AWS_ENDPOINT` + `AWS_USE_PATH_STYLE_ENDPOINT=true` 指向 R2 endpoint)。非鎖定 R2——設計上任何 S3 相容服務(AWS S3、MinIO 等)都能用同一套 env 變數切換,不影響 code。
|
||||||
|
|
||||||
|
**取捨**:R2 bucket 預設不開放公開讀取,需要在 R2 後台額外設定 public access 或綁自訂網域,否則圖片網址會回 403。這是外部平台設定,不是 code 範疇,但屬於本次上線前必須確認的步驟。
|
||||||
|
|
||||||
|
### D4:既有檔案遷移用一次性 artisan command,不自動刪除本機檔案
|
||||||
|
|
||||||
|
新增 `app/Console/Commands/MigrateStorageToS3.php`:
|
||||||
|
- 掃描 `storage/app/public/` 底下所有檔案(排除 `storage:link` 產生的符號連結本身)
|
||||||
|
- 逐一用 `Storage::disk('s3')->put()` 上傳到 S3/R2,保留原本相對路徑(DB 裡 `image_path` 欄位值不用改,因為路徑結構不變)
|
||||||
|
- 上傳後用 `Storage::disk('s3')->exists()` 驗證,失敗的路徑列表印出來,不中斷整個流程(方便重跑補上傳失敗的部分)
|
||||||
|
- 支援 `--dry-run` 只列出會上傳的檔案數量與清單,不實際執行
|
||||||
|
- **不刪除本機檔案**——遷移完成、人工確認 S3 上圖片都能正常顯示後,本機檔案清理是後續手動動作,不在這個 command 的職責內(避免遷移腳本本身變成一個刪檔風險點)
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- **[Risk] R2 bucket 未開放公開讀取,切換後圖片全部 403/404**
|
||||||
|
→ **Mitigation**:上線前先用 `curl` 直接打 R2 網址驗證至少一張測試圖可公開存取,再切 `FILESYSTEM_DISK=s3`
|
||||||
|
|
||||||
|
- **[Risk] 遷移腳本執行中網路中斷,部分檔案未上傳成功,但沒人發現**
|
||||||
|
→ **Mitigation**:command 明確印出失敗清單並回傳非 0 exit code;上線前先跑 `--dry-run` 確認檔案總數,遷移後跑一次全量 `exists()` 檢查
|
||||||
|
|
||||||
|
- **[Risk] `env('FILESYSTEM_DISK')` 語意被重新定義,未來新開發者看 Laravel 官方文件會誤解**
|
||||||
|
→ **Mitigation**:在 `config/filesystems.php` 加註解說明此專案的特殊用法(此為文件層級緩解,非 code 邏輯風險)
|
||||||
|
|
||||||
|
- **[Risk] Composer 新依賴需要 rebuild image,VPS 有短暫停機窗口**
|
||||||
|
→ **Mitigation**:沿用 `cloud-ready-p1` 已驗證過的 `docker compose up -d --build` 流程,排低流量時段
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
**程式碼(PR 合併前):**
|
||||||
|
1. `composer require league/flysystem-aws-s3-v3`
|
||||||
|
2. `config/filesystems.php`:`public` disk 改為 D1 的設定
|
||||||
|
3. `.env.example`:補齊 `AWS_ENDPOINT`/`AWS_URL`/`AWS_USE_PATH_STYLE_ENDPOINT` 說明
|
||||||
|
4. 新增 `MigrateStorageToS3` artisan command + 測試
|
||||||
|
|
||||||
|
**VPS 維護窗口(PR merge + CI/CD 完成後):**
|
||||||
|
```bash
|
||||||
|
# 1. R2 後台先建好 bucket,設定 public access,取得 endpoint/bucket 資訊
|
||||||
|
# 2. SSH 進 VPS,更新 .env(不進版控)
|
||||||
|
# AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION=auto
|
||||||
|
# AWS_BUCKET / AWS_ENDPOINT / AWS_URL / AWS_USE_PATH_STYLE_ENDPOINT=true
|
||||||
|
# FILESYSTEM_DISK 暫時保留 local(先不切)
|
||||||
|
|
||||||
|
# 3. Rebuild(帶入新 composer 依賴)
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# 4. 用 s3 disk 跑遷移(此時 FILESYSTEM_DISK 仍是 local,遷移用另一個 artisan 參數指定目標 disk)
|
||||||
|
docker compose exec app php artisan storage:migrate-to-s3 --dry-run
|
||||||
|
docker compose exec app php artisan storage:migrate-to-s3
|
||||||
|
|
||||||
|
# 5. 驗證幾張圖片的 R2 直連網址可正常開啟後,才切換
|
||||||
|
sed -i 's/FILESYSTEM_DISK=local/FILESYSTEM_DISK=s3/' .env
|
||||||
|
docker compose exec app php artisan config:clear
|
||||||
|
|
||||||
|
# 6. 實際跑一次上傳/刪除/顯示(三個功能各測一次)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- R2 帳號是否已申請?需要 Hank 先建好 bucket 才能進入 VPS 維護窗口步驟。
|
||||||
|
- 是否綁自訂網域到 R2(例如 `cdn.hank-space.com`)取代 R2 預設網域?影響 `AWS_URL` 設定值,建議上線前確認。
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
作為實踐雲原生架構的一環,延續 P1 階段(Session/Log/Scheduler 雲端化)的方向,將使用者上傳檔案(課程圖片、教練證照、預約聊天室圖片)改為 S3 相容物件儲存,是 container 無狀態化的標準作法——容器可以隨時被重建、複製、水平擴容,而不必依賴本機磁碟保留使用者資料。`env-cloud-annotations` spec 已標注 `FILESYSTEM_DISK` 雲端應設為 `s3`,但目前只是註解提醒,尚未實際遷移——`.env.example` 仍預設 `local`,`public` disk 驅動也還是本機磁碟。這是 cloud-ready 路線圖 P0 剩餘兩項之一(另一項為移除 bind mount,不在本次範圍)。
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- **`composer.json`**:新增 `league/flysystem-aws-s3-v3`(Laravel 官方 S3 驅動套件,目前未安裝)
|
||||||
|
- **`config/filesystems.php`**:`public` disk 的 `driver` 從 `local` 改為 `s3` 相容設定(**disk key 名稱維持 `public` 不變**,因為 6 個既有呼叫點都寫死 `Storage::disk('public')`,此法零程式碼改動)
|
||||||
|
- **`.env.example`**:`FILESYSTEM_DISK` **預設維持 `local`**(本機開發環境沒有 S3 相容服務可用,維持 dev 友善),補上 `AWS_ENDPOINT`、`AWS_URL`、`AWS_USE_PATH_STYLE_ENDPOINT` 等 R2 相容欄位與「雲端環境請設為 s3」的說明註解
|
||||||
|
- **新增一次性遷移 artisan command**:把 `storage/app/public/` 現有檔案(課程圖片、教練證照、聊天室圖片)上傳到 S3/R2,避免切換後舊圖 404
|
||||||
|
- **VPS `.env`**:手動新增 AWS/R2 credentials(機敏值,不進版控)
|
||||||
|
- **驗證**:上傳、刪除、顯示三個流程在課程圖片、教練證照、預約聊天室圖片三個既有功能上皆需重新驗證
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
- `file-storage-s3`:使用者上傳檔案(課程圖片、教練證照、聊天室圖片)存放於 S3 相容物件儲存,而非 container 本地磁碟;container 重建/水平擴容時檔案不遺失
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
- `env-cloud-annotations`:`FILESYSTEM_DISK` 需求從「標注建議值」升級為「實際預設值」,比照 P1 階段 `SESSION_DRIVER`/`LOG_CHANNEL` 已完成的模式
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- **`config/filesystems.php`**:`public` disk 驅動設定
|
||||||
|
- **`composer.json` / `composer.lock`**:新增 S3 驅動依賴
|
||||||
|
- **`.env.example`**:新增/調整 AWS_* 相關變數與說明
|
||||||
|
- **新增檔案**:一次性遷移 command(`app/Console/Commands/`)
|
||||||
|
- **VPS `.env`**:需手動補齊 credentials(不進版控)
|
||||||
|
- **外部依賴(新增)**:需要一組 S3 相容物件儲存帳號(建議 Cloudflare R2,免費額度足夠現行用量)
|
||||||
|
- **不影響**:`CourseImageController`、`CompressesImages` trait、`CourseImage`/`ProviderCertification` model、`BookingMessageController` 這 6 個呼叫點程式碼本身(disk 名稱不變);資料庫 schema;前端
|
||||||
|
- **部署影響**:需 rebuild app image(新 composer 依賴);遷移舊檔案時有短暫視窗新舊圖網址不一致,建議排低流量時段
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: .env.example 標示雲端必要變數
|
||||||
|
`.env.example` SHALL 以行內註解或正確預設值標示以下雲端部署時必須明確設定的變數:
|
||||||
|
- `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`,擴容更高效)
|
||||||
|
|
||||||
|
#### Scenario: 新環境照 .env.example 初始化 session driver
|
||||||
|
- **WHEN** 開發者以 `.env.example` 為基礎建立 `.env`,未手動修改 `SESSION_DRIVER`
|
||||||
|
- **THEN** Laravel session driver 使用 Redis
|
||||||
|
|
||||||
|
#### Scenario: 新環境照 .env.example 初始化 log channel
|
||||||
|
- **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** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值,且 `FILESYSTEM_DISK` 的說明明確指出改為 `s3` 後需一併設定哪些 `AWS_*` 變數
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
## ADDED 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 結束
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
## 1. 依賴與設定 [後端]
|
||||||
|
|
||||||
|
- [ ] 1.1 [後端] `composer require league/flysystem-aws-s3-v3`
|
||||||
|
- [ ] 1.2 [後端] `config/filesystems.php`:`public` disk 改為 D1 設計(`driver` 吃 `env('FILESYSTEM_DISK', 'local')`,補上 `key`/`secret`/`region`/`bucket`/`endpoint`/`use_path_style_endpoint`),並加註解說明此專案對 `FILESYSTEM_DISK` 的特殊用法
|
||||||
|
- [ ] 1.3 [後端] `.env.example`:`FILESYSTEM_DISK` 保留 `local` 預設,更新行內註解;補齊 `AWS_ENDPOINT`、`AWS_URL`、`AWS_USE_PATH_STYLE_ENDPOINT` 三個變數與說明
|
||||||
|
|
||||||
|
## 2. 遷移 Command [後端]
|
||||||
|
|
||||||
|
- [ ] 2.1 [後端] 新增 `app/Console/Commands/MigrateStorageToS3.php`(`storage:migrate-to-s3` 指令),掃描 `storage/app/public/` 並上傳至 `Storage::disk('s3')`,保留相對路徑,排除 `storage:link` 符號連結
|
||||||
|
- [ ] 2.2 [後端] 實作 `--dry-run` 參數:只列出檔案清單與總數,不執行上傳
|
||||||
|
- [ ] 2.3 [後端] 上傳後用 `Storage::disk('s3')->exists()` 驗證,失敗檔案收集成清單並於結尾印出,command 以非 0 exit code 結束(若有失敗)
|
||||||
|
- [ ] 2.4 [後端] 確認 command 不刪除任何本機檔案(無論成功或失敗)
|
||||||
|
|
||||||
|
## 3. 測試 [後端]
|
||||||
|
|
||||||
|
- [ ] 3.1 [後端] `MigrateStorageToS3` command 測試:正常上傳、`--dry-run` 不執行上傳、部分失敗時正確回報且不中斷
|
||||||
|
- [ ] 3.2 [後端] `config/filesystems.php` 改動後,既有 `CourseImageTest`/`ProviderCertification` 相關測試在 `FILESYSTEM_DISK=local`(預設)下全數通過,確認零回歸
|
||||||
|
- [ ] 3.3 [後端] 新增測試驗證 `FILESYSTEM_DISK=local` 與未設定時行為一致(呼叫 `Storage::disk('public')` 不需任何 AWS credentials 即可運作)
|
||||||
|
|
||||||
|
## 4. R2 環境準備 [整合測試]
|
||||||
|
|
||||||
|
- [ ] 4.1 [整合測試] 於 Cloudflare R2 建立 bucket,設定 public access(或綁自訂網域)
|
||||||
|
- [ ] 4.2 [整合測試] 取得 `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_BUCKET`/`AWS_ENDPOINT` 等 R2 credentials
|
||||||
|
- [ ] 4.3 [整合測試] 用 `curl` 直接打 R2 網址驗證至少一個手動上傳的測試檔案可公開存取(403 排查必須在此步驟完成,不帶進生產切換)
|
||||||
|
|
||||||
|
## 5. VPS 部署與遷移 [整合測試]
|
||||||
|
|
||||||
|
- [ ] 5.1 [整合測試] SSH 進 VPS,`.env` 補齊 R2 credentials,`FILESYSTEM_DISK` 暫時保留 `local`
|
||||||
|
- [ ] 5.2 [整合測試] `docker compose up -d --build`(帶入新 composer 依賴)
|
||||||
|
- [ ] 5.3 [整合測試] 執行 `docker compose exec app php artisan storage:migrate-to-s3 --dry-run`,確認檔案數量與清單合理
|
||||||
|
- [ ] 5.4 [整合測試] 執行 `docker compose exec app php artisan storage:migrate-to-s3`,記錄失敗清單(若有)並重跑補上傳
|
||||||
|
- [ ] 5.5 [整合測試] 抽查數個已遷移檔案的 R2 直連網址,確認可正常開啟
|
||||||
|
|
||||||
|
## 6. 切換與驗證 [整合測試]
|
||||||
|
|
||||||
|
- [ ] 6.1 [整合測試] VPS `.env` 將 `FILESYSTEM_DISK` 改為 `s3`,`docker compose exec app php artisan config:clear`
|
||||||
|
- [ ] 6.2 [整合測試] 手動驗證課程圖片上傳/刪除/顯示三個流程
|
||||||
|
- [ ] 6.3 [整合測試] 手動驗證教練證照上傳/顯示流程
|
||||||
|
- [ ] 6.4 [整合測試] 手動驗證預約聊天室圖片上傳/顯示流程
|
||||||
|
- [ ] 6.5 [整合測試] 確認 `storage/app/public/` 本機檔案仍完整保留(作為遷移後的備份,暫不刪除)
|
||||||
|
|
||||||
|
## 7. OpenSpec 同步
|
||||||
|
|
||||||
|
- [ ] 7.1 更新 `openspec/specs/env-cloud-annotations/spec.md`(套用本次 delta)
|
||||||
|
- [ ] 7.2 新增 `openspec/specs/file-storage-s3/spec.md`
|
||||||
|
- [ ] 7.3 完成後歸檔本 change(`openspec archive cloud-ready-s3-storage`)
|
||||||
Reference in New Issue
Block a user