11 KiB
11 KiB
ConvertX API 架構說明
系統架構圖
┌─────────────────────────────────────────────────────────────────┐
│ API Server │
│ │
│ ┌──────────────┐ │
│ │ Auth Layer │◄──── JWT Token 驗證 │
│ │ (JWT) │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Request Router │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────────┐ │ │
│ │ │ REST API │ │ GraphQL API │ │ │
│ │ │ /api/v1/* │ │ /graphql │ │ │
│ │ └──────┬──────┘ └────────┬─────────┘ │ │
│ │ │ │ │ │
│ └──────────┼─────────────────────────┼──────────────────┘ │
│ │ │ │
│ └────────────┬────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Conversion Service │ │
│ │ (Domain Logic) │ │
│ │ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Engine Registry │ │ │
│ │ └─────────────────┘ │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ External Converters │ │
│ │ (FFmpeg, etc.) │ │
│ └───────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────┐
│ File Storage │
│ ┌─────────────────┐ │
│ │ data/uploads/ │ │
│ │ data/output/ │ │
│ └─────────────────┘ │
└───────────────────────┘
模組說明
1. Auth Layer (src/auth.rs)
負責所有請求的 JWT 驗證。
職責:
- 解析 Authorization Header
- 驗證 JWT Token 簽名
- 檢查 Token 是否過期
- 提取使用者資訊
不負責:
- 產生 JWT Token(由外部認證服務處理)
- 使用者管理
2. REST API (src/rest.rs)
提供 RESTful HTTP 端點。
端點:
GET /health- 健康檢查GET /api/v1/engines- 列出引擎POST /api/v1/convert- 建立轉檔任務GET /api/v1/jobs- 列出任務GET /api/v1/jobs/:id- 取得任務狀態GET /api/v1/jobs/:id/download- 下載結果DELETE /api/v1/jobs/:id- 刪除任務
3. GraphQL API (src/graphql.rs)
提供 GraphQL 查詢與變更。
Queries:
health- 健康檢查engines- 列出引擎engine(id)- 取得特定引擎jobs- 列出任務job(id)- 取得特定任務validateConversion- 驗證轉換suggestions- 取得建議
Mutations:
createJob- 建立任務deleteJob- 刪除任務
4. Engine Registry (src/engine.rs)
管理所有可用的轉換引擎及其能力。
職責:
- 註冊引擎及其支援的格式
- 驗證轉換是否可行
- 提供替代方案建議
設計原則:
- 引擎必須明確指定,不自動選擇
- 不支援的轉換必須回傳建議
5. Conversion Service (src/conversion.rs)
處理轉檔任務的核心邏輯。
職責:
- 建立和管理轉檔任務
- 儲存上傳的檔案
- 呼叫外部轉換程式
- 管理輸出檔案
6. Error Handling (src/error.rs)
統一的錯誤處理機制。
特色:
- 結構化的錯誤回應
- 錯誤碼分類
- 轉換建議整合
資料流程
轉檔請求流程
Client Request
│
▼
┌─────────────────┐
│ JWT Validation │ ──────► 401 Unauthorized
└────────┬────────┘
│ Valid
▼
┌─────────────────┐
│ Parse Request │ ──────► 400 Bad Request
│ (file, engine, │
│ target_format) │
└────────┬────────┘
│ Valid
▼
┌─────────────────┐
│ Validate │ ──────► 422 Unsupported
│ Conversion │ (with suggestions)
│ (Engine Registry)│
└────────┬────────┘
│ Supported
▼
┌─────────────────┐
│ Create Job │
│ Save File │
│ Start Conversion│
└────────┬────────┘
│
▼
┌─────────────────┐
│ 201 Created │
│ Return Job ID │
└─────────────────┘
轉換執行流程
Background Task
│
▼
┌─────────────────┐
│ Update Status │
│ → Processing │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Execute │
│ Converter │
│ Command │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│Success│ │Failed │
└───┬───┘ └───┬───┘
│ │
▼ ▼
┌───────┐ ┌───────┐
│Update │ │Update │
│Status │ │Status │
│=Done │ │=Failed│
└───────┘ └───────┘
設計決策
1. 為什麼使用 Rust?
- 效能:處理大量並發請求時表現優異
- 記憶體安全:避免常見的記憶體問題
- 非同步支援:Tokio 生態系統成熟
- 類型安全:編譯時檢查減少運行時錯誤
2. 為什麼同時支援 REST 和 GraphQL?
- REST:簡單直觀,適合檔案上傳/下載
- GraphQL:靈活查詢,減少 over-fetching
- 獨立使用:使用者可選擇適合的 API
3. 為什麼採用「結果僅透過 API 下載」策略?
- 安全性:不暴露檔案系統路徑
- 權限控制:下載時驗證身份
- 彈性部署:易於整合 CDN 或雲端儲存
- 可擴展性:未來可改用分散式儲存
4. 為什麼引擎必須明確指定?
- 可預測性:使用者明確知道使用什麼工具
- 一致性:避免因自動選擇導致的結果差異
- 透明度:錯誤時能明確指出是哪個引擎的問題
擴展指南
新增轉換引擎
- 在
src/engine.rs的register_default_engines()中新增:
let new_engine = Engine::new(
"engine_id",
"Engine Name",
"Engine description"
)
.add_conversion("input_format", vec!["output1", "output2"]);
self.register(new_engine);
- 在
src/conversion.rs的run_converter_command()中新增命令:
"engine_id" => (
"command",
vec!["arg1".to_string(), input, output],
),
新增 REST 端點
在 src/rest.rs 中:
pub fn routes() -> Router<AppState> {
Router::new()
.route("/api/v1/new-endpoint", get(new_handler))
// ...
}
async fn new_handler(
State(state): State<AppState>,
RequireAuth(user): RequireAuth,
) -> ApiResult<impl IntoResponse> {
// ...
}
新增 GraphQL 查詢/變更
在 src/graphql.rs 中:
#[Object]
impl QueryRoot {
async fn new_query(&self, ctx: &Context<'_>) -> GqlResult<SomeType> {
let _user = validate_auth(ctx)?;
// ...
}
}
#[Object]
impl MutationRoot {
async fn new_mutation(&self, ctx: &Context<'_>, input: Input) -> GqlResult<Output> {
let user = validate_auth(ctx)?;
// ...
}
}
效能考量
並發處理
- 使用 Tokio 非同步運行時
- 轉換任務在背景執行
- 任務狀態儲存在記憶體(生產環境應使用資料庫)
檔案處理
- 大檔案直接寫入磁碟,不佔用過多記憶體
- 輸出檔案串流下載
未來優化方向
- 使用 Redis 進行任務佇列
- 使用 PostgreSQL 進行任務持久化
- 實作任務優先級
- 新增任務取消功能
- 實作 Webhook 回呼