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

11 KiB
Raw Blame History

ConvertX API 規格文件

概述

ConvertX API Server 是一個獨立的檔案轉換 API 服務,同時提供 REST API 和 GraphQL API。 兩種 API 是完全獨立的服務,使用者可以選擇只使用其中一種。

認證

JWT Bearer Token

所有 API 請求(除了健康檢查)都需要在 HTTP Header 中提供有效的 JWT Token

Authorization: Bearer <jwt-token>

Token 結構

{
  "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 /healthGET /api/v1/health

檢查 API Server 運作狀態。不需要認證。

回應

{
  "status": "healthy",
  "version": "0.1.0",
  "timestamp": "2024-01-15T10:30:00Z"
}

引擎管理

GET /api/v1/engines

列出所有可用的轉換引擎。

Headers

Authorization: Bearer <token>

回應

{
  "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)

{
  "id": "ffmpeg",
  "name": "FFmpeg",
  "description": "Audio and video conversion using FFmpeg",
  "supported_input_formats": ["mp4", "webm", "avi"],
  "supported_output_formats": ["mp4", "webm", "mp3"]
}

回應 (404)

{
  "error": {
    "code": "ENGINE_NOT_FOUND",
    "message": "Engine not found: nonexistent"
  }
}

GET /api/v1/engines/:engine_id/conversions

取得特定引擎支援的轉換對應表。

回應

{
  "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)

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "message": "Conversion job created successfully"
}

回應 (422) - 不支援的轉換

{
  "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

列出當前使用者的所有任務。

回應

{
  "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) - 完成

{
  "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) - 失敗

{
  "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)

{
  "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

# ========== 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

使用範例

列出所有引擎

query {
  engines {
    id
    name
    description
    supportedInputFormats
    supportedOutputFormats
  }
}

驗證轉換是否支援

query {
  validateConversion(engine: "ffmpeg", from: "mp4", to: "webm") {
    success
    error {
      code
      message
      suggestions {
        engine
        from
        to
      }
    }
  }
}

取得轉換建議

query {
  suggestions(from: "pdf", to: "docx") {
    engine
    from
    to
  }
}

建立轉檔任務

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:

{
  "filename": "image.png",
  "content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
}

查詢任務狀態

query {
  job(id: "550e8400-e29b-41d4-a716-446655440000") {
    id
    status
    originalFilename
    sourceFormat
    targetFormat
    engine
    downloadUrl
    errorMessage
    createdAt
    completedAt
  }
}

列出所有任務

query {
  jobs {
    id
    status
    originalFilename
    targetFormat
    downloadUrl
  }
}

刪除任務

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 內部錯誤