File size: 13,931 Bytes
8843db1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# 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 => `
        <div class="version-item">
            <span class="version-number">${version.version}</span>
            <span class="file-type">
                ${getFileTypeIcon(version.file_type)}
                ${version.file_type.toUpperCase()}
            </span>
            <span class="file-size">${formatFileSize(version.file_size)}</span>
            <a href="${version.download_url}" class="download-btn">
                下載 ${version.file_name}
            </a>
        </div>
    `).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)