convertor/api-server/docs/API_SPEC.md
Your Name e083e5d11d feat: Add Rust API Server with REST and GraphQL support
- Implement JWT authentication layer
- Add Engine Registry with 20+ conversion engines
- Implement Conversion Service with background job processing
- REST API endpoints for file conversion operations
- GraphQL API with queries and mutations
- Comprehensive error handling with conversion suggestions
- Integration tests for both REST and GraphQL APIs
- Complete API documentation

Features:
- REST API: /api/v1/* endpoints
- GraphQL API: /graphql endpoint with playground
- JWT Bearer token authentication
- Engine validation with alternative suggestions
- File download via API (not exposing file paths)
- Support for FFmpeg, ImageMagick, LibreOffice, Pandoc, and more
2026-01-21 11:40:03 +08:00

614 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ConvertX API 規格文件
## 概述
ConvertX API Server 是一個獨立的檔案轉換 API 服務,同時提供 REST API 和 GraphQL API。
兩種 API 是完全獨立的服務,使用者可以選擇只使用其中一種。
## 認證
### JWT Bearer Token
所有 API 請求(除了健康檢查)都需要在 HTTP Header 中提供有效的 JWT Token
```http
Authorization: Bearer <jwt-token>
```
### Token 結構
```json
{
"sub": "user-unique-id",
"exp": 1735689600,
"iat": 1735603200,
"email": "user@example.com",
"roles": ["user", "admin"]
}
```
| 欄位 | 必填 | 說明 |
|------|------|------|
| `sub` | ✓ | 使用者唯一識別碼 |
| `exp` | ✓ | Token 過期時間Unix timestamp |
| `iat` | ✓ | Token 簽發時間Unix timestamp |
| `email` | - | 使用者 Email |
| `roles` | - | 使用者角色列表 |
### 認證錯誤回應
| 狀況 | 錯誤碼 | HTTP 狀態 |
|------|--------|-----------|
| 缺少 Authorization Header | `MISSING_AUTH_HEADER` | 401 |
| Token 格式錯誤 | `INVALID_TOKEN` | 401 |
| Token 簽名無效 | `INVALID_TOKEN` | 401 |
| Token 已過期 | `TOKEN_EXPIRED` | 401 |
---
## REST API 規格
### 基礎資訊
- **Base URL**: `http://localhost:3001/api/v1`
- **Content-Type**: `application/json`(一般請求)或 `multipart/form-data`(檔案上傳)
---
### 健康檢查
#### `GET /health` 或 `GET /api/v1/health`
檢查 API Server 運作狀態。不需要認證。
**回應**
```json
{
"status": "healthy",
"version": "0.1.0",
"timestamp": "2024-01-15T10:30:00Z"
}
```
---
### 引擎管理
#### `GET /api/v1/engines`
列出所有可用的轉換引擎。
**Headers**
```
Authorization: Bearer <token>
```
**回應**
```json
{
"engines": [
{
"id": "ffmpeg",
"name": "FFmpeg",
"description": "Audio and video conversion using FFmpeg",
"supported_input_formats": ["mp4", "webm", "avi", "mkv", "mov", "mp3", "wav", "flac", "ogg", "m4a", "gif"],
"supported_output_formats": ["webm", "avi", "mkv", "mov", "mp3", "wav", "flac", "ogg", "gif", "m4a", "aac", "mp4"]
},
{
"id": "imagemagick",
"name": "ImageMagick",
"description": "Image format conversion using ImageMagick",
"supported_input_formats": ["png", "jpg", "jpeg", "gif", "bmp", "webp", "tiff", "svg"],
"supported_output_formats": ["jpg", "jpeg", "gif", "bmp", "webp", "tiff", "ico", "pdf", "png"]
}
]
}
```
---
#### `GET /api/v1/engines/:engine_id`
取得特定引擎的詳細資訊。
**參數**
- `engine_id`: 引擎識別碼(如 `ffmpeg`, `imagemagick`
**回應 (200)**
```json
{
"id": "ffmpeg",
"name": "FFmpeg",
"description": "Audio and video conversion using FFmpeg",
"supported_input_formats": ["mp4", "webm", "avi"],
"supported_output_formats": ["mp4", "webm", "mp3"]
}
```
**回應 (404)**
```json
{
"error": {
"code": "ENGINE_NOT_FOUND",
"message": "Engine not found: nonexistent"
}
}
```
---
#### `GET /api/v1/engines/:engine_id/conversions`
取得特定引擎支援的轉換對應表。
**回應**
```json
{
"engine_id": "ffmpeg",
"conversions": {
"mp4": ["webm", "avi", "mkv", "mov", "mp3", "wav", "flac", "ogg", "gif"],
"webm": ["mp4", "avi", "mkv", "mov", "mp3", "wav", "flac", "ogg", "gif"],
"mp3": ["wav", "flac", "ogg", "m4a", "aac"]
}
}
```
---
### 轉檔任務
#### `POST /api/v1/convert`
建立新的轉檔任務。
**Headers**
```
Authorization: Bearer <token>
Content-Type: multipart/form-data
```
**表單欄位**
| 欄位 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `file` | File | ✓ | 要轉換的檔案 |
| `engine` | String | ✓ | 轉換引擎 ID |
| `target_format` | String | ✓ | 目標格式(不含點) |
| `options` | String | - | JSON 格式的轉換選項 |
**回應 (201)**
```json
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Conversion job created successfully"
}
```
**回應 (422) - 不支援的轉換**
```json
{
"error": {
"code": "UNSUPPORTED_CONVERSION",
"message": "Unsupported conversion from pdf to mp4 using engine ffmpeg",
"suggestions": [
{
"engine": "libreoffice",
"from": "pdf",
"to": "docx"
},
{
"engine": "libreoffice",
"from": "pdf",
"to": "html"
},
{
"engine": "calibre",
"from": "pdf",
"to": "epub"
}
]
}
}
```
---
#### `GET /api/v1/jobs`
列出當前使用者的所有任務。
**回應**
```json
{
"jobs": [
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"original_filename": "video.mp4",
"source_format": "mp4",
"target_format": "webm",
"engine": "ffmpeg",
"download_url": "/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000/download",
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:01:30Z"
}
],
"total": 1
}
```
---
#### `GET /api/v1/jobs/:job_id`
取得特定任務的狀態。
**任務狀態**
| 狀態 | 說明 |
|------|------|
| `pending` | 等待處理 |
| `processing` | 處理中 |
| `completed` | 完成 |
| `failed` | 失敗 |
**回應 (200) - 完成**
```json
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"original_filename": "video.mp4",
"source_format": "mp4",
"target_format": "webm",
"engine": "ffmpeg",
"download_url": "/api/v1/jobs/550e8400-e29b-41d4-a716-446655440000/download",
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:01:30Z"
}
```
**回應 (200) - 失敗**
```json
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"original_filename": "corrupted.mp4",
"source_format": "mp4",
"target_format": "webm",
"engine": "ffmpeg",
"error_message": "Conversion failed: Invalid data found when processing input",
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:00:05Z"
}
```
---
#### `GET /api/v1/jobs/:job_id/download`
下載轉換後的檔案。
**前提條件**
- 任務狀態必須是 `completed`
- 只有任務建立者可以下載
**回應 Headers**
```
Content-Type: <mime-type>
Content-Disposition: attachment; filename="output.webm"
```
**錯誤回應**
| 狀況 | 錯誤碼 | HTTP 狀態 |
|------|--------|-----------|
| 任務未完成 | `BAD_REQUEST` | 400 |
| 無權限 | `UNAUTHORIZED` | 401 |
| 任務不存在 | `JOB_NOT_FOUND` | 404 |
| 檔案遺失 | `FILE_NOT_FOUND` | 404 |
---
#### `DELETE /api/v1/jobs/:job_id`
刪除任務及相關檔案。
**回應 (200)**
```json
{
"message": "Job deleted successfully",
"job_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
---
## GraphQL API 規格
### Endpoint
- **URL**: `http://localhost:3001/graphql`
- **Method**: POST
- **Content-Type**: `application/json`
GraphQL Playground 可透過 GET 請求訪問同一 URL。
### Schema
```graphql
# ========== Queries ==========
type Query {
"""健康檢查不需認證"""
health: Health!
"""列出所有可用引擎"""
engines: [Engine!]!
"""取得特定引擎"""
engine(id: ID!): Engine
"""列出當前使用者的所有任務"""
jobs: [Job!]!
"""取得特定任務"""
job(id: ID!): Job
"""驗證轉換是否支援"""
validateConversion(
engine: String!
from: String!
to: String!
): CreateJobResult!
"""取得轉換建議"""
suggestions(from: String!, to: String!): [Suggestion!]!
}
# ========== Mutations ==========
type Mutation {
"""建立轉檔任務"""
createJob(
filename: String!
fileBase64: String!
input: CreateJobInput!
): CreateJobResult!
"""刪除任務"""
deleteJob(id: ID!): Boolean!
}
# ========== Input Types ==========
input CreateJobInput {
"""轉換引擎 ID"""
engine: String!
"""目標格式"""
targetFormat: String!
"""轉換選項JSON 字串"""
options: String
}
# ========== Object Types ==========
type Health {
status: String!
version: String!
timestamp: DateTime!
}
type Engine {
id: ID!
name: String!
description: String!
supportedInputFormats: [String!]!
supportedOutputFormats: [String!]!
}
type Job {
id: ID!
originalFilename: String!
sourceFormat: String!
targetFormat: String!
engine: String!
status: JobStatus!
outputFilename: String
errorMessage: String
downloadUrl: String
createdAt: DateTime!
completedAt: DateTime
}
type Suggestion {
engine: String!
from: String!
to: String!
}
type CreateJobResult {
success: Boolean!
job: Job
error: ConversionError
}
type ConversionError {
code: String!
message: String!
suggestions: [Suggestion!]!
}
# ========== Enums ==========
enum JobStatus {
PENDING
PROCESSING
COMPLETED
FAILED
}
# ========== Scalars ==========
scalar DateTime
```
### 使用範例
#### 列出所有引擎
```graphql
query {
engines {
id
name
description
supportedInputFormats
supportedOutputFormats
}
}
```
#### 驗證轉換是否支援
```graphql
query {
validateConversion(engine: "ffmpeg", from: "mp4", to: "webm") {
success
error {
code
message
suggestions {
engine
from
to
}
}
}
}
```
#### 取得轉換建議
```graphql
query {
suggestions(from: "pdf", to: "docx") {
engine
from
to
}
}
```
#### 建立轉檔任務
```graphql
mutation CreateConversionJob($filename: String!, $content: String!) {
createJob(
filename: $filename
fileBase64: $content
input: {
engine: "imagemagick"
targetFormat: "jpg"
}
) {
success
job {
id
status
originalFilename
}
error {
code
message
suggestions {
engine
from
to
}
}
}
}
```
Variables:
```json
{
"filename": "image.png",
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
}
```
#### 查詢任務狀態
```graphql
query {
job(id: "550e8400-e29b-41d4-a716-446655440000") {
id
status
originalFilename
sourceFormat
targetFormat
engine
downloadUrl
errorMessage
createdAt
completedAt
}
}
```
#### 列出所有任務
```graphql
query {
jobs {
id
status
originalFilename
targetFormat
downloadUrl
}
}
```
#### 刪除任務
```graphql
mutation {
deleteJob(id: "550e8400-e29b-41d4-a716-446655440000")
}
```
---
## 錯誤碼對照表
| 錯誤碼 | HTTP 狀態 | 說明 | 包含建議 |
|--------|-----------|------|----------|
| `UNAUTHORIZED` | 401 | 未授權存取 | ✗ |
| `INVALID_TOKEN` | 401 | Token 無效 | ✗ |
| `TOKEN_EXPIRED` | 401 | Token 已過期 | ✗ |
| `MISSING_AUTH_HEADER` | 401 | 缺少認證標頭 | ✗ |
| `BAD_REQUEST` | 400 | 請求格式錯誤 | ✗ |
| `INVALID_FILE` | 400 | 無法辨識檔案格式 | ✗ |
| `FILE_TOO_LARGE` | 400 | 檔案超過大小限制 | ✗ |
| `ENGINE_NOT_FOUND` | 404 | 引擎不存在 | ✗ |
| `JOB_NOT_FOUND` | 404 | 任務不存在 | ✗ |
| `FILE_NOT_FOUND` | 404 | 檔案不存在 | ✗ |
| `UNSUPPORTED_CONVERSION` | 422 | 不支援的轉換 | ✓ |
| `CONVERSION_FAILED` | 500 | 轉換失敗 | ✗ |
| `INTERNAL_ERROR` | 500 | 內部錯誤 | ✗ |