# 管理後台與教材輸出 API 規格

## 設計原則

- 管理 API 只給登入後台使用。
- 公開站優先讀取發布後的靜態 `data/materials.json`。
- 若之後要做更細的公開 API，可以在不破壞前台的前提下追加。

## 共通規則

- Base Path：`/api/admin`
- 回應格式：`application/json`
- 時間格式：ISO 8601
- 驗證失敗：`422`
- 未登入：`401`
- 權限不足：`403`

## Auth

### `POST /api/admin/auth/login`

用途：管理者登入

Request:

```json
{
  "email": "admin@example.com",
  "password": "plain-password"
}
```

Response:

```json
{
  "user": {
    "id": 1,
    "name": "Site Admin",
    "email": "admin@example.com",
    "role": "super_admin"
  }
}
```

### `POST /api/admin/auth/logout`

用途：登出並使 Session 失效

### `GET /api/admin/me`

用途：取得目前登入者資訊

## Themes

### `GET /api/admin/themes`

用途：列出主題與發布狀態

### `POST /api/admin/themes`

用途：建立主題

Request:

```json
{
  "slug": "theme-habit",
  "title": "習慣養成",
  "subtitle": "把每天回來說成自然日文",
  "description": "適合暖身的一組主題"
}
```

### `PATCH /api/admin/themes/{id}`

用途：更新主題資料

### `POST /api/admin/themes/{id}/assignments`

用途：設定此主題要出現哪些教材

Request:

```json
{
  "memoryIds": ["mem-01", "mem-02", "mem-03", "mem-05"],
  "repeatIds": ["rep-01", "rep-04", "rep-05"],
  "recallIds": ["rec-03", "rec-04", "rec-05"]
}
```

## Content Items

### `GET /api/admin/content-items`

Query:

- `type=memory|repeat|recall|vocab|grammar|reading`
- `status=draft|in_review|scheduled|published|archived`
- `theme_id=123`
- `keyword=...`

### `POST /api/admin/content-items`

用途：新增教材

Request:

```json
{
  "publicId": "rep-10",
  "itemType": "repeat",
  "label": "複誦句",
  "jpText": "今週は読む時間を先に決めておくことにした。",
  "readingText": "こんしゅう は よむ じかん を さき に きめておく こと に した。",
  "zhText": "我決定這週先把閱讀時間定下來。",
  "practiceText": "先慢唸，再換成自己的版本。",
  "tipText": "適合同時練習 〜ておく 與 〜ことにした。",
  "ttsText": "こんしゅうは よむじかんを さきに きめておく ことにした",
  "audioAssetId": 25,
  "tags": ["文法", "安排"],
  "jlptBand": "N3"
}
```

### `PATCH /api/admin/content-items/{id}`

用途：更新教材欄位

### `POST /api/admin/content-items/{id}/status`

用途：切換狀態

Request:

```json
{
  "status": "in_review"
}
```

## Assets

### `POST /api/admin/assets`

用途：上傳音檔、圖片、PDF

Content-Type：`multipart/form-data`

Fields:

- `file`
- `kind=audio|image|pdf`
- `title`
- `altText`

Response:

```json
{
  "id": 25,
  "kind": "audio",
  "originalName": "lesson-01.mp3",
  "mimeType": "audio/mpeg",
  "sizeBytes": 1822400,
  "publicUrl": "/uploads/audio/2026/04/uuid-lesson-01.mp3",
  "durationSeconds": 18
}
```

### `GET /api/admin/assets`

用途：搜尋可綁定的素材

## Schedule

### `PUT /api/admin/theme-schedule/{date}`

用途：指定某天的主題

Request:

```json
{
  "themeId": 2
}
```

## Publish

### `POST /api/admin/publish/materials`

用途：把資料庫中的已發布內容編譯成前台要讀的 `data/materials.json`

Request:

```json
{
  "note": "新增時間安排主題與三組新複誦句"
}
```

Response:

```json
{
  "snapshotId": 8,
  "version": 8,
  "outputPath": "/public_html/jp/data/materials.json",
  "checksum": "sha256:..."
}
```

### `GET /api/admin/publish/history`

用途：列出發布歷史

### `POST /api/admin/publish/{snapshotId}/rollback`

用途：回滾到指定版本

## 公開輸出

### `GET /data/materials.json`

用途：給現在的靜態前台直接讀取

輸出結構必須維持目前前台相容：

```json
{
  "site": {},
  "themes": [],
  "memoryCards": [],
  "repeatCards": [],
  "recallCards": [],
  "vocabLibrary": [],
  "grammarLibrary": [],
  "readingLibrary": []
}
```

### `GET /data/materials.manifest.json`

用途：可選。提供發布版本、時間與 checksum，方便除錯與前台顯示版本。

## API 驗證重點

- `publicId` 必須唯一，且維持前台慣用格式，例如 `mem-01`。
- `itemType` 與欄位組合必須一致。
- `audioAssetId` 若存在，必須是 `kind=audio`。
- 發布前要檢查所有主題引用的內容是否存在。
- 發布前要檢查輸出的 JSON 是否能成功 parse。
