365 lines
8.1 KiB
Markdown
365 lines
8.1 KiB
Markdown
# API Spec
|
||
|
||
本文件定義 File Access Agent 對外 API 邊界,以及它依賴的 Member Center File Access 授權契約。
|
||
|
||
目前狀態:
|
||
|
||
- File Access Agent API:本文件定義為本專案目標契約。
|
||
- Member Center File Access API:依 `../member_center/docs/openapi.yaml` 與相關文件整理,作為本專案整合依據。
|
||
|
||
## 0. 部署與資料假設
|
||
|
||
- Single-tenant instance:一個 File Access Agent instance 只服務一個 tenant。
|
||
- 服務啟動時需設定固定 `INSTANCE_TENANT_ID`;所有請求中的 `tenant_id`(token 或 validation 結果)都必須與此值一致。
|
||
- 預設無 DB,服務維持 stateless。
|
||
- Metadata 讀取以 bucket provider 為主,不以本地 DB 作為權威來源。
|
||
- 若未來出現審計、replay 防護或特殊索引需求,可再加可選持久化層。
|
||
|
||
## 1. 角色分工
|
||
|
||
### File Access Agent
|
||
|
||
負責:
|
||
|
||
- 驗證 upload 用的 Member Center JWT access token
|
||
- 接收檔案上傳、下載、metadata 查詢、刪除
|
||
- 對 delegated download token 向 Member Center 做線上驗證
|
||
- 控制實際 bucket / file space 存取
|
||
|
||
不負責:
|
||
|
||
- OAuth client 管理
|
||
- access token 簽發
|
||
- delegated download token 簽發
|
||
- 商業規則判斷與版本管理
|
||
- 結構化查詢或 metadata 主資料治理
|
||
|
||
### Member Center
|
||
|
||
負責:
|
||
|
||
- 簽發 upload 用的 S2S JWT access token
|
||
- 簽發 delegated short-lived opaque download token
|
||
- 提供 delegated token validation endpoint
|
||
|
||
## 2. 授權模型
|
||
|
||
### 2.1 Upload
|
||
|
||
- token 來源:Member Center `client_credentials`
|
||
- audience:`file_access_api`
|
||
- 必要 scope:`files:upload.write`
|
||
- 必要 claim:`tenant_id`
|
||
- 驗證方式:File Access Agent 以 Member Center JWKS 驗簽 JWT
|
||
|
||
### 2.2 Download
|
||
|
||
- token 來源:業務服務以 `files:download.delegate` 呼叫 Member Center 申請
|
||
- token 類型:delegated short-lived opaque token
|
||
- token 需綁定:
|
||
- `tenant_id`
|
||
- `user_id`
|
||
- `file_id` 或 `object_key`
|
||
- `method=GET`
|
||
- 短效 `exp`
|
||
- 驗證方式:File Access Agent 呼叫 Member Center validation endpoint 線上驗證
|
||
|
||
### 2.3 Metadata
|
||
|
||
建議沿用 upload 類型的 S2S JWT:
|
||
|
||
- audience:`file_access_api`
|
||
- 必要 scope:`files:metadata.read`
|
||
- 必要 claim:`tenant_id`
|
||
|
||
### 2.4 Delete
|
||
|
||
建議沿用 upload 類型的 S2S JWT:
|
||
|
||
- audience:`file_access_api`
|
||
- 必要 scope:`files:delete`
|
||
- 必要 claim:`tenant_id`
|
||
|
||
## 3. File Access Agent API
|
||
|
||
Base path 暫定:`/`
|
||
|
||
### 3.1 Upload File
|
||
|
||
- Method: `PUT`
|
||
- Path: `/files/{objectKey}`
|
||
- Auth: `Bearer` JWT
|
||
- Required scope: `files:upload.write`
|
||
|
||
用途:
|
||
|
||
- 由業務服務上傳檔案到 File Access Agent
|
||
|
||
Path parameters:
|
||
|
||
- `objectKey`:URL-encoded object key
|
||
|
||
Headers:
|
||
|
||
- `Authorization: Bearer <access_token>`
|
||
- `Content-Type: <mime-type>`
|
||
|
||
Optional headers:
|
||
|
||
- `X-File-Id: <string>`
|
||
|
||
Request body:
|
||
|
||
- binary stream
|
||
|
||
Validation:
|
||
|
||
- JWT signature / `iss` / `aud=file_access_api` / `exp`
|
||
- token `tenant_id`
|
||
- `scope=files:upload.write`
|
||
- request `objectKey` 是否符合允許的 tenant 邊界策略
|
||
|
||
Object key 命名建議(介接規範):
|
||
|
||
- Agent 不做複雜命名治理,`objectKey` 由介接服務自行決定。
|
||
- 建議 key 結構:`{domain}/{entity}/{yyyy}/{MM}/{dd}/{id-or-uuid}/{sanitized-filename}`
|
||
- 建議至少包含一個高唯一性段(例如 `uuid` 或資料庫主鍵),避免只用原始檔名。
|
||
- 建議避免使用空白、反斜線與 `..`;上傳前先做檔名 sanitize(保留副檔名)。
|
||
- 建議全部使用小寫與固定分隔字元(`/`、`-`),便於跨服務對齊。
|
||
|
||
避免重複檔名與版本策略(責任邊界):
|
||
|
||
- 若上傳相同 `objectKey`,Agent 視為覆蓋(overwrite),不自動做版本保留。
|
||
- Agent 不負責「同名檔案改名」或「自動遞增版本號」。
|
||
- 若要避免覆蓋,介接服務應在上傳前自行產生新 key(例如 `timestamp + uuid`)。
|
||
- 若要做版本管理,請由介接服務把每個版本寫成不同 `objectKey`(例如 `.../v0001/...`、`.../v0002/...`)。
|
||
- 若要保留「原始檔名查詢」能力,請在介接服務自己的資料表維護 `display_name -> objectKey` 對照。
|
||
|
||
Responses:
|
||
|
||
- `201 Created`
|
||
- `400 Bad Request`
|
||
- `401 Unauthorized`
|
||
- `403 Forbidden`
|
||
- `409 Conflict`
|
||
- `413 Payload Too Large`
|
||
|
||
Success body:
|
||
|
||
```json
|
||
{
|
||
"tenant_id": "uuid",
|
||
"file_id": "optional-string",
|
||
"object_key": "reports/2026/04/file.pdf",
|
||
"content_type": "application/pdf",
|
||
"size": 12345,
|
||
"etag": "optional-string",
|
||
"last_modified_at": "2026-04-24T03:10:00Z"
|
||
}
|
||
```
|
||
|
||
### 3.2 Download File
|
||
|
||
- Method: `GET`
|
||
- Path: `/files/{objectKey}`
|
||
- Auth: delegated download token
|
||
|
||
用途:
|
||
|
||
- 由 client 使用 delegated token 下載檔案
|
||
|
||
Path parameters:
|
||
|
||
- `objectKey`:URL-encoded object key
|
||
|
||
Headers:
|
||
|
||
- `Authorization: Bearer <delegated_download_token>`
|
||
|
||
Validation:
|
||
|
||
- 將 token 與實際 `tenant_id + file_id/object_key + method=GET` 邊界送到 Member Center validation endpoint
|
||
- validation 成功後才允許下載
|
||
|
||
Responses:
|
||
|
||
- `200 OK` with file stream
|
||
- `401 Unauthorized`
|
||
- `403 Forbidden`
|
||
- `404 Not Found`
|
||
|
||
### 3.3 Get File Metadata
|
||
|
||
- Method: `GET`
|
||
- Path: `/files/metadata/{objectKey}`
|
||
- Auth: `Bearer` JWT
|
||
- Required scope: `files:metadata.read`
|
||
|
||
用途:
|
||
|
||
- 由業務服務讀取檔案 metadata,不回傳檔案內容
|
||
|
||
Responses:
|
||
|
||
- `200 OK`
|
||
- `401 Unauthorized`
|
||
- `403 Forbidden`
|
||
- `404 Not Found`
|
||
|
||
Success body:
|
||
|
||
```json
|
||
{
|
||
"tenant_id": "uuid",
|
||
"file_id": "optional-string",
|
||
"object_key": "reports/2026/04/file.pdf",
|
||
"content_type": "application/pdf",
|
||
"size": 12345,
|
||
"etag": "optional-string",
|
||
"last_modified_at": "2026-04-24T03:10:00Z"
|
||
}
|
||
```
|
||
|
||
### 3.4 Head File
|
||
|
||
- Method: `HEAD`
|
||
- Path: `/files/{objectKey}`
|
||
- Auth: `Bearer` JWT
|
||
- Required scope: `files:metadata.read`
|
||
|
||
用途:
|
||
|
||
- 提供較輕量的 existence / metadata header 檢查
|
||
|
||
Responses:
|
||
|
||
- `200 OK`
|
||
- `401 Unauthorized`
|
||
- `403 Forbidden`
|
||
- `404 Not Found`
|
||
|
||
### 3.5 Delete File
|
||
|
||
- Method: `DELETE`
|
||
- Path: `/files/{objectKey}`
|
||
- Auth: `Bearer` JWT
|
||
- Required scope: `files:delete`
|
||
|
||
用途:
|
||
|
||
- 由業務服務刪除檔案
|
||
|
||
Responses:
|
||
|
||
- `204 No Content`
|
||
- `401 Unauthorized`
|
||
- `403 Forbidden`
|
||
- `404 Not Found`
|
||
|
||
### 3.6 Health
|
||
|
||
- Method: `GET`
|
||
- Path: `/health`
|
||
- Auth: none
|
||
|
||
用途:
|
||
|
||
- liveness / readiness 的最小檢查
|
||
|
||
Responses:
|
||
|
||
- `200 OK`
|
||
|
||
Success body:
|
||
|
||
```json
|
||
{
|
||
"status": "ok"
|
||
}
|
||
```
|
||
|
||
## 4. 與 Member Center 的整合 API
|
||
|
||
以下端點不屬於本專案提供,但 File Access Agent 需要依賴。
|
||
|
||
### 4.1 Issue Delegated Download Token
|
||
|
||
- Provider: Member Center
|
||
- Method: `POST`
|
||
- Path: `/file-access/download-tokens`
|
||
- Required scope: `files:download.delegate`
|
||
|
||
Request body:
|
||
|
||
```json
|
||
{
|
||
"tenant_id": "uuid",
|
||
"user_id": "uuid",
|
||
"file_id": "optional-string",
|
||
"object_key": "reports/2026/04/file.pdf",
|
||
"method": "GET",
|
||
"expires_in_seconds": 300
|
||
}
|
||
```
|
||
|
||
Response body:
|
||
|
||
- 由 Member Center 定義;目前至少會回傳 delegated download token 與到期資訊
|
||
|
||
### 4.2 Validate Delegated Download Token
|
||
|
||
- Provider: Member Center
|
||
- Method: `POST`
|
||
- Path: `/file-access/download-tokens/validate`
|
||
- Required scope: `files:download.read`
|
||
|
||
Request body:
|
||
|
||
```json
|
||
{
|
||
"token": "opaque-token",
|
||
"tenant_id": "uuid",
|
||
"file_id": "optional-string",
|
||
"object_key": "reports/2026/04/file.pdf",
|
||
"method": "GET"
|
||
}
|
||
```
|
||
|
||
Response body:
|
||
|
||
- 由 Member Center 定義;目前至少需包含 validation success / active 狀態與對應邊界資訊
|
||
|
||
## 5. 共通錯誤格式
|
||
|
||
建議本專案統一採用:
|
||
|
||
```json
|
||
{
|
||
"error": "string_code",
|
||
"message": "human readable message",
|
||
"request_id": "uuid"
|
||
}
|
||
```
|
||
|
||
建議錯誤碼:
|
||
|
||
- `invalid_token`
|
||
- `insufficient_scope`
|
||
- `tenant_mismatch`
|
||
- `object_key_mismatch`
|
||
- `method_mismatch`
|
||
- `file_not_found`
|
||
- `payload_too_large`
|
||
- `unsupported_media_type`
|
||
- `storage_unavailable`
|
||
|
||
## 6. 待補細節
|
||
|
||
- `tenant_id` 從 request path、header 或 object key prefix 如何對齊
|
||
- `file_id` 是否作為正式主 key,或僅保留 `object_key`
|
||
- `HEAD /files/{objectKey}` 是否保留,或改以 `GET /files/metadata/{objectKey}`
|
||
- upload 是否需要支援 overwrite 策略 header
|
||
- 下載是否要支援 `Range` request
|
||
- metadata / delete 是否允許 delegated token,或只接受 S2S JWT
|
||
- 可選持久化是否需要最小 audit / jti 表(預設不啟用)
|