# API Spec

本文件定義 File Access Agent 對外 API 邊界，以及它依賴的 Member Center File Access 授權契約。

目前狀態：

- File Access Agent API：本文件定義為本專案目標契約。
- Member Center File Access API：依 `../member_center/docs/openapi.yaml` 與相關文件整理，作為本專案整合依據。Client Credentials 可核發的 scope 由 Member Center 的 OAuth application `usage` mapping 決定，不依賴呼叫端自行擴權。

## 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
- 若 token issuer 是公開 HTTPS 位址，但 JWKS 需走內網位址，可設定 `Auth:MetadataAddress` 覆寫 metadata discovery URL，`iss` 仍以 `Auth:Issuer` 驗證。

### 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` 或 `scp` 包含 `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 表（預設不啟用）
