# Supabase Storage 檔案儲存使用指南 ## 概述 KSTools License Manager 使用 Supabase Storage 來儲存和管理插件發布檔案。系統支援 ZIP、EXE、MSI 三種檔案格式,並自動建立 `plugin-releases` bucket 用於存放版本檔案。 ## 系統架構流程圖 ```mermaid graph TD A[開發者] --> B[準備發布檔案] B --> C{選擇檔案格式} C --> D[.zip 壓縮包] C --> E[.exe 可執行檔] C --> F[.msi 安裝程式] D --> G[上傳到 Supabase Storage] E --> G F --> G G --> H[獲取公開 URL] H --> I[更新 versions 表] I --> J[用戶可下載] subgraph "Supabase Storage" K[plugin-releases bucket] L[RLS 政策] M[公開 URL] end G --> K K --> L L --> M M --> H subgraph "前端系統" N[版本管理頁面] O[下載統計] P[檔案列表] end J --> N N --> O N --> P %% 精簡配色 - 只標記關鍵步驟 style A fill:#e3f2fd style G fill:#f3e5f5 style J fill:#d4edda ``` ## 發布流程圖 ```mermaid sequenceDiagram participant Dev as 開發者 participant UI as 前端介面 participant API as 後端 API participant Storage as Supabase Storage participant DB as Supabase DB participant User as 用戶 Dev->>+UI: 1. 選擇檔案上傳 (.zip/.exe/.msi) UI->>UI: 2. 檢測檔案格式 UI->>+API: 3. POST /api/admin/release (FormData) API->>API: 4. 驗證檔案格式和大小 API->>+Storage: 5. upload(file, filename) Storage-->>-API: 6. 返回上傳結果 API->>+Storage: 7. getPublicUrl(filename) Storage-->>-API: 8. 返回公開 URL API->>+DB: 9. 插入版本記錄 (含 title, file_type) DB-->>-API: 10. 確認記錄已建立 API-->>-UI: 11. 返回完整發布結果 UI-->>-Dev: 12. 發布成功通知 User->>+UI: 13. 查看版本列表 UI->>+DB: 14. 查詢版本記錄 DB-->>-UI: 15. 返回版本資料 (含檔案類型) UI-->>-User: 16. 顯示版本列表 (顯示檔案格式圖示) User->>+Storage: 17. 點擊下載 Storage-->>-User: 18. 直接下載檔案 %% 關鍵步驟高亮 rect rgb(255, 243, 205) note over UI,API: 檔案上傳驗證階段 end rect rgb(243, 229, 245) note over API,Storage: Storage 處理階段 end rect rgb(212, 237, 218) note over API,DB: 資料庫記錄階段 end ``` ## 檔案格式決策流程圖 ```mermaid flowchart TD Start([開始發布]) --> Choose{選擇檔案格式} Choose -->|開發版本| ZIP[ZIP 壓縮包] Choose -->|便攜版本| EXE[EXE 可執行檔] Choose -->|正式安裝| MSI[MSI 安裝程式] ZIP --> CheckZip{檔案完整性} EXE --> CheckExe{可執行性測試} MSI --> CheckMsi{安裝程式測試} CheckZip -->|通過| Upload1[上傳 .zip] CheckExe -->|通過| Upload2[上傳 .exe] CheckMsi -->|通過| Upload3[上傳 .msi] CheckZip -->|失敗| Error[修復並重新打包] CheckExe -->|失敗| Error CheckMsi -->|失敗| Error Upload1 --> Success[發布成功] Upload2 --> Success Upload3 --> Success Error --> Start %% 精簡配色 - 只標記關鍵步驟 style Start fill:#e3f2fd style Success fill:#d4edda style Error fill:#f8d7da ``` ## 檔案格式說明 ### 支援的檔案格式 | 格式 | 用途 | 檔案大小 | 建議使用場景 | |-----|------|---------|-------------| | `.zip` | 壓縮包 | 小 | 開發版本、源碼發布 | | `.exe` | 可執行檔 | 中 | 便攜版本、免安裝版 | | `.msi` | 安裝程式 | 大 | 正式發布、企業部署 | ### 檔案命名規範 ``` KSTools-v{版本號}.{格式} 範例: - KSTools-v1.2.0.zip - KSTools-v1.2.0.exe - KSTools-v1.2.0.msi ``` ## 設定步驟 ### 1. 執行 SQL Schema 在你的 Supabase 版本專案中執行 `supabase-version-schema.sql`,這會: - 建立 `plugin-releases` bucket - 建立支援多格式的 `versions` 表 - 設定適當的 RLS 政策 - 允許公開讀取檔案 - 允許認證用戶上傳檔案 ### 2. 驗證 Storage 設定 1. 登入 Supabase Dashboard 2. 進入你的版本專案 3. 點選 "Storage" 頁面 4. 確認看到 `plugin-releases` bucket ## 檔案上傳方式 ### 透過後端 API 上傳 系統採用統一的後端 API 上傳方式,確保安全性和一致性。 ```javascript // 前端版本發布表單提交 async function handleRelease(formData, file) { const releaseData = new FormData(); releaseData.append('version', formData.version); releaseData.append('title', formData.title); releaseData.append('changelog', formData.changelog); releaseData.append('file', file); const response = await fetch('/api/admin/release', { method: 'POST', headers: { 'Authorization': `Bearer ${session.access_token}` }, body: releaseData }); if (!response.ok) { throw new Error('發布失敗'); } return await response.json(); } ``` ### 檔案上傳流程 1. **前端驗證** - 檔案格式、大小檢查 2. **API 呼叫** - 傳送 FormData 到 `/api/admin/release` 3. **後端處理** - 檔案上傳到 Supabase Storage 4. **資料庫更新** - 儲存版本記錄 5. **返回結果** - 完整的發布資訊 ## 後端 API 實作詳情 ### FastAPI 端點:`/api/admin/release` ```python @router.post("/admin/release") async def release_version( version: str = Form(...), title: Optional[str] = Form(None), changelog: str = Form(...), min_revit_version: Optional[str] = Form(None), file: Optional[UploadFile] = File(None), current_user = Depends(verify_admin) ): """ 發布新版本 - 統一的檔案上傳端點 支援 .zip, .exe, .msi 三種格式 """ # 1. 驗證檔案格式 allowed_extensions = ['.zip', '.exe', '.msi'] file_extension = None for ext in allowed_extensions: if file.filename.lower().endswith(ext): file_extension = ext break if not file_extension: raise HTTPException( status_code=400, detail=f"不支援的檔案格式。支援格式:{', '.join(allowed_extensions)}" ) # 2. 上傳到 Supabase Storage file_content = await file.read() file_size = len(file_content) file_path = f"KSTools-v{version}{file_extension}" # 設定正確的 MIME 類型 content_types = { '.zip': 'application/zip', '.exe': 'application/octet-stream', '.msi': 'application/x-msi' } content_type = content_types.get(file_extension, 'application/octet-stream') final_download_url = await version_db.upload_file(file_path, file_content, content_type) # 3. 儲存版本記錄 version_data = { 'version': version, 'title': title, 'changelog': changelog, 'min_revit_version': min_revit_version, 'download_url': final_download_url, 'file_size': file_size, 'file_type': file_extension.lstrip('.') if file_extension else None, 'file_name': f"KSTools-v{version}{file_extension}" if file_extension else None, 'released_by': current_user.get('email', 'Unknown'), 'is_active': True } result = client.table('versions').insert(version_data).execute() return {"success": True, "version": result.data[0]} ``` ## 自動化流程 使用此 API 端點的優勢: - ✅ **統一入口** - 所有檔案上傳都通過同一個端點 - ✅ **自動驗證** - 檔案格式、大小自動檢查 - ✅ **安全控制** - 管理員權限驗證 - ✅ **完整記錄** - 自動建立資料庫記錄 - ✅ **錯誤處理** - 統一的錯誤回應 ## 資料庫記錄範例 系統會自動建立以下格式的版本記錄: ```sql -- 新增版本記錄(包含檔案格式和標題) INSERT INTO versions ( version, title, release_date, is_active, download_url, file_size, file_type, file_name, changelog, min_revit_version, released_by ) VALUES ( '1.2.0', '重大功能更新', -- 版本標題 NOW(), true, 'https://你的專案ID.supabase.co/storage/v1/object/public/plugin-releases/KSTools-v1.2.0.msi', 25600000, -- 檔案大小(位元組) 'msi', -- 檔案類型 'KSTools-v1.2.0.msi', -- 檔案名稱 '- 新功能說明\n- 錯誤修復', '2020', 'KyrosDev' ); ``` ## 前端顯示檔案類型 ```javascript // 在版本列表中顯示檔案格式圖示 function getFileTypeIcon(fileType) { const icons = { 'zip': '📦', 'exe': '⚙️', 'msi': '💾' }; return icons[fileType] || '📄'; } function renderVersionList(versions) { return versions.map(version => `
${version.version} ${getFileTypeIcon(version.file_type)} ${version.file_type.toUpperCase()} ${formatFileSize(version.file_size)} 下載 ${version.file_name}
`).join(''); } ``` ## 檔案管理操作流程圖 ```mermaid flowchart TD A[列出檔案] --> B{選擇操作} B --> C[下載] B --> D[刪除] B --> E[更新] B --> F[獲取資訊] A --> G[檢查 .zip] A --> H[檢查 .exe] A --> I[檢查 .msi] A --> J[storage.list API] C --> K[storage.download API] D --> L[storage.remove API] E --> M[storage.update API] F --> N[storage.getPublicUrl API] K --> O[檔案下載完成] L --> P[檔案刪除完成] M --> Q[檔案更新完成] N --> R[獲取URL完成] %% 精簡配色 - 只標記關鍵步驟 style A fill:#e3f2fd style D fill:#f8d7da style J fill:#f3e5f5 style K fill:#f3e5f5 style L fill:#f3e5f5 style M fill:#f3e5f5 style N fill:#f3e5f5 style O fill:#d4edda style P fill:#d4edda style Q fill:#d4edda style R fill:#d4edda ``` ## 檔案管理 ### 檢視所有檔案(按格式篩選) ```javascript // 列出所有檔案 const { data, error } = await supabaseVersion.storage .from('plugin-releases') .list(); // 按檔案格式篩選 const zipFiles = data.filter(file => file.name.endsWith('.zip')); const exeFiles = data.filter(file => file.name.endsWith('.exe')); const msiFiles = data.filter(file => file.name.endsWith('.msi')); ``` ### 批量操作不同格式檔案 ```javascript // 刪除特定版本的所有格式檔案 async function deleteVersionAllFormats(version) { const formats = ['zip', 'exe', 'msi']; const filesToDelete = formats.map(format => `KSTools-v${version}.${format}`); const { data, error } = await supabaseVersion.storage .from('plugin-releases') .remove(filesToDelete); return { success: !error, error }; } ``` ### 檢查檔案完整性 ```javascript // 驗證檔案是否完整上傳 async function verifyFileIntegrity(fileName, expectedSize) { const { data, error } = await supabaseVersion.storage .from('plugin-releases') .list('', { search: fileName }); if (error || !data.length) { return { valid: false, reason: '檔案不存在' }; } const file = data[0]; if (file.metadata?.size !== expectedSize) { return { valid: false, reason: '檔案大小不符' }; } return { valid: true }; } ``` ## 權限管理架構圖 ```mermaid graph TB subgraph "Supabase RLS 政策" A[Public Read Policy] B[Authenticated Upload Policy] C[Service Role Manage Policy] end subgraph "用戶角色" D[匿名用戶] E[登入用戶] F[Service Role] end subgraph "操作權限" G[只能下載] H[可以上傳+下載] I[完整管理權限] end subgraph "檔案格式支援" J[所有格式下載] K[所有格式上傳] L[所有格式管理] end D --> A --> G --> J E --> B --> H --> K F --> C --> I --> L %% 精簡配色 - 只標記關鍵權限 style C fill:#f3e5f5 style I fill:#f8d7da ``` ## 安全建議 ### 檔案上傳安全 1. **檔案格式驗證**:確保只允許 .zip、.exe、.msi 格式 2. **檔案大小限制**:設定合理的檔案大小上限 3. **病毒掃描**:建議對 .exe 和 .msi 檔案進行安全掃描 4. **數位簽章**:.exe 和 .msi 檔案應使用程式碼簽章 ### 權限控制 1. 不要在前端暴露 Service Role Key 2. 上傳功能建議透過後端 API 處理 3. 定期檢查並清理舊版本檔案 4. 對敏感操作實施雙重驗證 ## 疑難排解 ### 常見問題 **Q: 版本發布失敗** A: 檢查 API 權限、檔案格式是否正確,查看瀏覽器開發者工具錯誤訊息 **Q: 檔案上傳後找不到** A: 確認 Supabase Storage bucket `plugin-releases` 已正確建立並設定 RLS 政策 **Q: 檔案大小限制** A: 系統設定為 100MB 上限,Supabase 免費版單檔限制為 50MB,付費版可達 5GB **Q: 權限驗證失敗** A: 確認登入狀態和管理員權限,檢查 Bearer Token 是否正確傳送 ### 檢查清單 - [ ] SQL Schema 已執行(包含 file_type 欄位) - [ ] Storage bucket 已建立 - [ ] RLS 政策已設定 - [ ] 檔案命名符合規範 - [ ] 檔案格式驗證功能正常 - [ ] 版本記錄包含檔案類型資訊 - [ ] 下載連結可正常使用 - [ ] 前端顯示檔案格式圖示 ## 相關連結 - [Supabase Storage 官方文件](https://supabase.com/docs/guides/storage) - [Supabase Storage API 參考](https://supabase.com/docs/reference/javascript/storage-createbucket) - [檔案 MIME 類型參考](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types)