- Backend (FastAPI): - Attachment and AttachmentVersion models with migration - FileStorageService with SHA-256 checksum validation - File type validation (whitelist/blacklist) - Full CRUD API with version control support - Audit trail integration for upload/download/delete - Configurable upload directory and file size limit - Frontend (React + Vite): - AttachmentUpload component with drag & drop - AttachmentList component with download/delete - TaskAttachments combined component - Attachments service for API calls - Testing: - 12 tests for storage service and API endpoints - OpenSpec: - add-document-management change archived 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
4.4 KiB
Markdown
160 lines
4.4 KiB
Markdown
## Context
|
||
|
||
文件管理是專案系統的核心功能,需要考慮:
|
||
- 檔案存儲策略(本地 vs NAS)
|
||
- 安全需求(加密、浮水印)
|
||
- 版本控制邏輯
|
||
- 大檔案處理
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- 提供任務層級的檔案附件功能
|
||
- 支援基本 CRUD 操作
|
||
- 整合現有 Audit Trail
|
||
- 為未來 NAS 整合預留擴展性
|
||
|
||
**Non-Goals:**
|
||
- 即時協作編輯(不在此範圍)
|
||
- 全文搜尋(未來功能)
|
||
- 檔案預覽(未來功能)
|
||
|
||
## Decisions
|
||
|
||
### 1. 檔案存儲策略
|
||
|
||
**Decision:** 使用本地檔案系統 + 環境變數配置路徑
|
||
|
||
**Rationale:**
|
||
- 開發階段使用本地存儲簡化設置
|
||
- 生產環境透過環境變數指向 NAS 掛載點
|
||
- 路徑結構:`{UPLOAD_DIR}/{project_id}/{task_id}/{attachment_id}/{version}/`
|
||
|
||
**Alternatives considered:**
|
||
- 直接 NAS 整合 - 開發環境設置複雜
|
||
- S3 相容存儲 - 增加外部依賴
|
||
|
||
### 2. 版本控制模型
|
||
|
||
**Decision:** 主表 + 版本歷史表分離
|
||
|
||
```
|
||
pjctrl_attachments (主表,存儲最新版本資訊)
|
||
├── id, task_id, filename, current_version, ...
|
||
|
||
pjctrl_attachment_versions (歷史表)
|
||
├── id, attachment_id, version, file_path, ...
|
||
```
|
||
|
||
**Rationale:**
|
||
- 主表快速查詢當前附件
|
||
- 歷史表保留所有版本
|
||
- 上傳同名檔案 → 建立新版本 → 更新主表 current_version
|
||
|
||
### 3. 加密策略
|
||
|
||
**Decision:** 使用 Fernet (基於 AES-128-CBC) 對稱加密
|
||
|
||
**Rationale:**
|
||
- Python cryptography 庫內建支援
|
||
- 自動處理 IV、padding、HMAC 驗證
|
||
- 比原生 AES-256 更安全(防止實作錯誤)
|
||
- 效能足夠(非大規模加密場景)
|
||
|
||
**實作方式:**
|
||
- 加密金鑰存儲於環境變數 `ENCRYPTION_KEY`
|
||
- 僅對機密專案的附件加密
|
||
- 加密狀態存於 `is_encrypted` 欄位
|
||
|
||
### 4. 浮水印策略
|
||
|
||
**Decision:** 下載時動態生成浮水印
|
||
|
||
**Rationale:**
|
||
- 不修改原始檔案
|
||
- 每次下載包含當下使用者資訊
|
||
- 使用 Pillow (圖片) 和 PyMuPDF (PDF) 處理
|
||
|
||
**浮水印內容:**
|
||
- 使用者姓名 + 工號
|
||
- 下載時間
|
||
- 機密等級(如適用)
|
||
|
||
### 5. 檔案大小限制
|
||
|
||
**Decision:** 預設 50MB,可透過環境變數調整
|
||
|
||
```python
|
||
MAX_FILE_SIZE = int(os.getenv("MAX_FILE_SIZE_MB", 50)) * 1024 * 1024
|
||
```
|
||
|
||
**Rationale:**
|
||
- 避免記憶體溢出
|
||
- 大檔案使用串流處理
|
||
- 生產環境可依需求調整
|
||
|
||
## Data Model
|
||
|
||
```sql
|
||
-- 附件主表
|
||
pjctrl_attachments
|
||
├── id: UUID (PK)
|
||
├── task_id: UUID (FK -> tasks)
|
||
├── filename: VARCHAR(255) -- 顯示名稱
|
||
├── original_filename: VARCHAR(255) -- 原始上傳名稱
|
||
├── mime_type: VARCHAR(100)
|
||
├── file_size: BIGINT
|
||
├── current_version: INT DEFAULT 1
|
||
├── is_encrypted: BOOLEAN DEFAULT false
|
||
├── uploaded_by: UUID (FK -> users)
|
||
├── is_deleted: BOOLEAN DEFAULT false
|
||
├── created_at: TIMESTAMP
|
||
└── updated_at: TIMESTAMP
|
||
|
||
-- 版本歷史表
|
||
pjctrl_attachment_versions
|
||
├── id: UUID (PK)
|
||
├── attachment_id: UUID (FK -> attachments)
|
||
├── version: INT
|
||
├── file_path: VARCHAR(1000) -- 實際存儲路徑
|
||
├── file_size: BIGINT
|
||
├── checksum: VARCHAR(64) -- SHA-256
|
||
├── uploaded_by: UUID (FK -> users)
|
||
├── created_at: TIMESTAMP
|
||
└── INDEX (attachment_id, version)
|
||
```
|
||
|
||
## API Design
|
||
|
||
```
|
||
POST /api/tasks/{task_id}/attachments # 上傳附件
|
||
GET /api/tasks/{task_id}/attachments # 列出附件
|
||
GET /api/attachments/{id} # 取得附件資訊
|
||
GET /api/attachments/{id}/download # 下載附件
|
||
GET /api/attachments/{id}/download?version=2 # 下載特定版本
|
||
DELETE /api/attachments/{id} # 刪除附件(軟刪除)
|
||
GET /api/attachments/{id}/versions # 版本歷史
|
||
POST /api/attachments/{id}/restore/{version} # 回復特定版本
|
||
```
|
||
|
||
## Risks / Trade-offs
|
||
|
||
| Risk | Mitigation |
|
||
|------|------------|
|
||
| 大檔案記憶體溢出 | 使用串流上傳/下載 |
|
||
| 加密金鑰洩漏 | 僅存於環境變數,定期輪換 |
|
||
| 浮水印處理耗時 | 限制支援的檔案類型,非同步處理大檔案 |
|
||
| NAS 不可用 | 本地存儲 fallback,監控告警 |
|
||
|
||
## Migration Plan
|
||
|
||
1. Phase 1: 建立模型、基本 CRUD、本地存儲
|
||
2. Phase 2: 版本控制
|
||
3. Phase 3: 加密與浮水印(可選)
|
||
|
||
## Open Questions
|
||
|
||
- [ ] NAS 掛載點路徑確認
|
||
- [ ] 生產環境加密金鑰管理方式(KMS? Vault?)
|
||
- [ ] 是否需要支援拖放上傳多檔案?
|