# gateway.yaml 設定說明

Gateway 使用 [go-zero `conf`](https://go-zero.dev/docs/tutorials/go-zero/configuration/overview) 載入 YAML。結構定義在 `internal/config/config.go`，對應各模組的 `Conf` 結構。

## 用哪個檔案？

| 檔案 | 是否提交 git | 用途 |
|------|:------------:|------|
| **`gateway.yaml`** | ✅ | 預設：不需 Docker，僅 health |
| **`gateway.dev.example.yaml`** | ✅ | 本機完整功能範例（無真實帳密） |
| **`gateway.dev.yaml`** | ❌ **勿提交** | 你的本機設定（Port、DB、SMTP/SES/三竹帳密） |

```bash
# 第一次本機開發
cp etc/gateway.dev.example.yaml etc/gateway.dev.yaml
# 編輯 etc/gateway.dev.yaml（例如 Port、Mitake 帳密）— 此檔已在 .gitignore

make deps-up
make mongo-index
make run-dev          # 會自動 setup-dev（缺檔時從 example 複製）

# 僅 API（不需 gateway.dev.yaml）
make run
```

---

## go-zero 填寫規則（重要）

1. **欄位名稱必須與 Go struct 一致**（駝峰在 yaml 裡通常照抄，如 `MaxPoolSize`）。**不要**填 struct 裡沒有的欄位（例如以前的 `MaxStaleness`），否則可能觸發奇怪錯誤。
2. **沒標 `optional` 的頂層欄位**：在該區塊出現時需型別正確；我們已在 Mongo / Notification / Member 加上 `json:",optional"`，多數子欄位可省略。
3. **空字串 `""`**：可寫，表示無帳密 / 無 AuthSource。
4. **時間**：`30m`、`10s`（`time.Duration`）。
5. **字串陣列**（如 `Compressors`）必須是 YAML 列表，**不能**寫成單一字串：
   ```yaml
   Compressors:
     - zstd
     - snappy
   ```
6. **Mongo 埠號**：用整數 `Port: 27017`，不要 `Port: "27017"`；或直接在 `Host` 寫 `127.0.0.1:27017`。

---

## 頂層：HTTP 服務（`rest.RestConf`）

| 欄位 | 必填 | 說明 | 範例 |
|------|:----:|------|------|
| `Name` | ✓ | 服務名稱 | `gateway` |
| `Host` | ✓ | 監聽位址 | `0.0.0.0` |
| `Port` | ✓ | HTTP 埠 | `8888` |

其餘 `RestConf` 欄位（`Timeout`、`Log`、`Prometheus` 等）可省略，使用 go-zero 預設。

---

## `Mongo`（`internal/library/mongo.Conf`）

**`Host` 留空或整段註解** → 不連 Mongo，`Notifier` / `MemberVerification` 不會注入（仍可跑 health API）。

| 欄位 | 必填 | 說明 | 本機 dev 建議 |
|------|:----:|------|----------------|
| `Schema` | 選 | 連線 scheme，預設 `mongodb` | `mongodb` |
| `Host` | 啟用時 ✓ | 主機；可含埠 `127.0.0.1:27017` | `127.0.0.1` |
| `Port` | 選 | `Host` 無埠時附加，**整數** | `27017` |
| `Database` | 啟用時 ✓ | 資料庫名 | `gateway`（與 docker init 一致） |
| `User` | 選 | 帳號 | 本機留空 |
| `Password` | 選 | 密碼 | 本機留空 |
| `AuthSource` | 選 | 驗證用 DB；帳號在 `admin` 建則填 `admin` | 本機留空 |
| `ReplicaName` | 選 | replica set 名稱 | 本機單機**留空** |
| `TLS` | 選 | 是否 `tls=true` | `false` |
| `MaxPoolSize` | 選 | 連線池上限 | `30` |
| `MinPoolSize` | 選 | 連線池下限 | `10` |
| `MaxConnIdleTime` | 選 | 閒置連線逾時 | `30m` |
| `Compressors` | 選 | 壓縮演算法陣列 | 省略（程式預設 zstd、snappy） |
| `ConnectTimeoutMs` | 選 | 啟動 Ping 逾時（毫秒） | 省略（預設 10s） |

**會建立的 collections**（自動，無需手動建表）：`notifications`、`notification_dlq`。索引：`make mongo-index` 或 docker init。

**常見錯誤**

| 錯誤訊息 | 原因 |
|----------|------|
| `type mismatch for field "Mongo.Compressors"` | 曾用不存在的 `Port: "27017"` 或 `Compressors` 寫成單一字串 |
| `field "Mongo.xxx" is not set` | 舊版未加 `optional`；請更新程式或改用最精簡的 `gateway.dev.yaml` |
| `mongo: ping primary` 失敗 | 有填 `Mongo.Host` 但沒跑 `make deps-up` |

---

## `Redis`（go-zero `redis.RedisConf`）

**`Host` 留空**（`gateway.yaml` 預設）→ 不連 Redis。有 Mongo 時仍可用 memory 冪等／配額，但**無異步重試 worker**、**無 Member OTP**。

`gateway.yaml` 建議保留區塊並設空 Host：

```yaml
Redis:
  Host: ""
  Type: node
```

| 欄位 | 必填 | 說明 | 本機 dev 建議 |
|------|:----:|------|----------------|
| `Host` | 啟用時 ✓ | `host:port` | `localhost:6379` |
| `Type` | 啟用時 ✓ | 單機填 `node` | `node` |
| `Pass` | 選 | 密碼 | 省略 |
| `Tls` | 選 | TLS | 省略 |

---

## `Notification`（`internal/model/notification/config`）

僅在 **Mongo 已連線** 時由 `ServiceContext` 組裝 Notifier。子欄位皆可省略（有程式預設）。

### 根層

| 欄位 | 說明 | 預設 / 建議 |
|------|------|-------------|
| `DefaultLocale` | 模板語系 fallback | `zh-tw` |
| `RatePerTenant.Email` | 每租戶每日 Email 上限 | `100` |
| `RatePerTenant.SMS` | 每租戶每日 SMS 上限 | `50` |

### `Notification.Email`

| 欄位 | 說明 |
|------|------|
| `Provider` | 無真實 provider 時填 `mock` |
| `From` | 寄件者（Send 時帶入） |
| `APIKey` | 保留，目前未用 |

**SMTP**（`Enable: true` 時加入發信鏈，依 `Sort` failover）：

| 欄位 | 說明 |
|------|------|
| `Enable` / `Sort` | 是否啟用、優先順序（數字小先試） |
| `Host` / `Port` | SMTP 主機與埠（MailHog：`localhost:1025`） |
| `Username` / `Password` | 認證 |

**SES**（AWS）：

| 欄位 | 說明 |
|------|------|
| `Enable` / `Sort` | 同上 |
| `Region` / `AccessKey` / `SecretKey` / `SessionToken` | AWS 憑證 |

### `Notification.SMS`

| 欄位 | 說明 |
|------|------|
| `Provider` | 無真實 provider 時 `mock` |
| `Mitake.Enable` | 三竹簡訊 |
| `Mitake.User` / `Mitake.Password` | 三竹帳密 |
| `Mitake.Sort` | failover 順序 |

### `Notification.Async`

| 欄位 | 說明 |
|------|------|
| `QueueRedisKey` | Redis ZSET key（空則用內建 `notif:retry:zset`） |
| `Worker` | 背景重試 goroutine 數 |
| `MaxRetry` | 最大重試次數 |
| `BackoffSeconds` | 重試間隔（秒）陣列 |

需 **Mongo + Redis** 才會啟動 `RetryWorker`。

### `Notification.Push` / `Webhook`

保留欄位，目前未接線。

---

## `Member`（`internal/model/member/config`）

僅在 **Mongo + Redis + Notifier** 皆就緒時注入 `MemberVerification`。

### `Member.OTP`

| 欄位 | 說明 | 建議 |
|------|------|------|
| `Length` | OTP 位數 | `6` |
| `TTLSeconds` | challenge 存活秒數 | `300` |
| `MaxAttempts` | 驗證嘗試上限 | `5` |
| `ResendCooldownSeconds` | 重發冷卻 | `60` |
| `DailyVerifyLimit` | 每日驗證上限 | `10` |

---

## 功能與依賴對照

```mermaid
flowchart LR
  subgraph always [永遠可用]
    H[health API]
  end
  subgraph mongo [Mongo.Host 有值]
    N[Notifier Send/Get]
    A[Admin DLQ]
  end
  subgraph redis [Redis.Host 有值]
    Q[Enqueue + RetryWorker]
    O[Member OTP]
  end
  H --> always
  mongo --> N
  mongo --> A
  mongo --> redis
  redis --> Q
  redis --> O
```

| 你想用的功能 | Mongo | Redis |
|--------------|:-----:|:-----:|
| `/api/v1/health` | — | — |
| `Notifier.Send`（同步） | ✓ | 選（冪等／配額建議開） |
| `Notifier.Enqueue` + 重試 | ✓ | ✓ |
| Member 信箱／手機驗證 | ✓ | ✓ |

---

## 疑難排解

```bash
# 1. 只驗證 yaml 能否載入（不啟動服務）
go test ./internal/config/ -run TestLoadGatewayYAML -v

# 2. 看缺什麼欄位（啟動時）
go run gateway.go -f etc/gateway.yaml

# 3. dev 完整栈
make deps-up && make run-dev

# 4. 通知測試（單一入口，METHOD 指定測哪一種）
# 詳見 docs/notification-testing.md
make mongo-index
make notify-test METHOD=email-send TO=你的信箱@example.com
make notify-test METHOD=email-send TO=test@example.com MOCK=1
curl -s http://127.0.0.1:8888/api/v1/health
```

若你改壞了 yaml，可從 repo 內的 `gateway.yaml` / `gateway.dev.yaml` 重新複製，再依本文件逐段打開需要的區塊。

---

## 相關文件

- [deploy/README.md](../deploy/README.md) — Docker Compose
- [internal/model/notification/README.md](../internal/model/notification/README.md) — 通知模組
- [internal/library/mongo/README.md](../internal/library/mongo/README.md) — Mongo 存取層
