thread-master/apps/backend/README.md

194 lines
6.5 KiB
Markdown
Raw Normal View History

2026-07-10 12:54:45 +00:00
# apps/backend
Harbor Desk Phase Cgo-zero
## 原則
| 做 | 不做 |
|----|------|
| `generate/api` + `make gen-api` | 手寫 handlerroutes |
| `generate/database/mongo` + `make migrate-up` | `cmd/init`、`cmd/seeder` 程式遷移 |
| `monc` + `CacheRedis` | 自己半套 mongo cache |
| `internal/logic` + `module/*/domain|repository|usecase` | 業務塞進 handler |
## 指令
```bash
cd apps/backend
make build # gateway + worker
make test
make gen-api # 改 .api 後
make migrate-up # 改 generate/database 後
make tidy
```
```bash
# 需要 Mongo + Redisgo-zero monc / redis
# Mongo 若有帳密(本機 infra 常見),一定要帶進 URI
export MONGO_URI='mongodb://USER:PASS@127.0.0.1:27017/?authSource=admin'
export MONGO_URL="$MONGO_URI" # migrate 用
make migrate-up
go run . -f etc/gateway.yaml
# admin@haixun.local / admin123seeduid=1000000 → 顯示 01000000
```
### 日誌與密碼(安全)
| 禁止出現在 log | 說明 |
|----------------|------|
| `password` / `new_password` / `current_password` / `temporary_password` | 明碼 |
| `password_hash` | bcrypt 也不可打 log |
| access/refresh token、api_key | 憑證 |
實作:
- 啟動時 `securelog.SilenceMongo()` → 關閉 go-zero **mon** 文件 dump否則 ReplaceOne 會印出整份 member
- `RestConf.Verbose = false` → 不印 request body
- 自寫 log 請用 `securelog.RedactAny(v)` / 勿 `logx.Infof("%+v", req)`
HTTP access log 只記 method/path/status/duration不含 body。
### 物件儲存(頭像)— **僅 MinIO/S3**
不落本機磁碟(無 localstore / `/files`)。未設定 Endpoint 時 upload 直接失敗。
| 項目 | 說明 |
|------|------|
| API | `POST /api/v1/media/upload`(需 JWT`{ content: dataURL, kind: "avatar" }` |
| 回傳 | `{ url, key }` — Mongo member **只存 url** |
| 後端 | MinIOdev或 S3prodS3 API + path-style |
| env | `HAIXUN_STORAGE_S3_*``MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` |
```yaml
ObjectStorage:
Endpoint: "http://127.0.0.1:9000" # 必填
Bucket: "haixun-assets"
AccessKey: "haixun"
SecretKey: "..."
UsePathStyle: true
PublicBaseURL: "" # 或 CDN / 反向代理公開前綴
```
FE選頭像 → upload → URL → `PATCH profile`(禁止 data URL
### 密碼政策(前後端一致)
| 規則 | 說明 |
|------|------|
| 長度 | **≥ 12** 字元 |
| 組成 | **大寫 + 小寫 + 數字 + 符號** 皆須至少一個 |
| 後端 | `ValidatePasswordPolicy` → 不符回 **400003** + `domain.PasswordPolicyEN` |
| 前端 | `passwordPolicy.ts` + 語言包 `password.policy.hint` / `api.err.400003` |
**同一句說明:**
- EN: `Password must be at least 12 characters with upper, lower, digit, and symbol`
- ZH: `密碼須至少 12 碼,且含大寫、小寫、數字與符號`
前端依 code 400003 顯示語系文案placeholderhint錯誤都用同一 key勿另寫一句。
### 寄信(信箱驗證 / 重設密碼)
業務在 `internal/logic/auth/*`:產碼 → `Notification.RenderVerify``SendEmail`
寄送實作在 `internal/module/notification`SMTPMailgun 相容)。
#### 模板與多語
- 引擎與 stand-alone 相同:**hermes**`github.com/matcornic/hermes/v2`
- 語系:`zh-TW`(預設)/`en`;來源為會員 `preferred_language`,忘記密碼則用請求 `language`FE 當前 locale
- Logo`Brand.LogoURL` 或預設 `{PublicWebBase}/brand-mark.jpg``apps/web/public/brand-mark.jpg`
```yaml
PublicWebBase: "http://127.0.0.1:5173"
Brand:
Name: "Harbor Desk"
LogoURL: "" # 空 = PublicWebBase/brand-mark.jpg
Copyright: "© Harbor Desk"
```
#### 本機Mailpit推薦
```bash
# 已含在 old/infra docker-composemongo/redis/mailpit
cd old/infra && docker compose --env-file ../deploy/.env.dev up -d mailpit
# UI 看信
open http://127.0.0.1:8025
```
| 項目 | 值 |
|------|-----|
| SMTP | `127.0.0.1:1025`(無密碼也可;可開 AUTH accept-any 模擬 Mailgun |
| Web UI | http://127.0.0.1:8025 |
| gateway 預設 | `Mail.SMTP.Host=127.0.0.1` `Port=1025` |
呼叫 `POST /api/v1/auth/email/send-code``password/request-reset` 後,到 Mailpit UI 看 HTML 信。
#### 正式Mailgun SMTP
```yaml
Mail:
Sender: "Harbor Desk <noreply@your-domain.com>"
SMTP:
Host: "smtp.mailgun.org"
Port: 587
User: "postmaster@YOUR_DOMAIN"
Password: "<Mailgun SMTP password>"
DevExposeCode: false
```
同一套 `Mail.SMTP` 設定;本機指 Mailpit、上線指 Mailgun**code 不用改**。
env 覆寫:`MAIL_SMTP_*` 或 `HAIXUN_SMTP_*`(與 `old/deploy/.env.dev` 相容)。
| SMTP Host | 行為 |
|-----------|------|
| **空** | log-only不真寄 |
| **Mailpit / Mailgun** | 真走 SMTP 投遞 |
### 前後端串不起來?檢查這幾項
1. **gateway 有在跑** `ss -tlnp | grep 8888`;沒有就 `go run . -f etc/gateway.yaml`
2. **Mongo URI 有帳密**`export MONGO_URI=...`;沒帶會查不到 seed
3. **Redis 有密碼**`export REDIS_PASSWORD=...`;沒帶 monc 回 `NOAUTH` → API 500
4. **已跑 migrate**`make migrate-up`seed `admin@haixun.local`
5. **FE 切到 live**:設定 → 資料來源 → Live預設是 **mock**,根本不會打後端)
6. **API base**`apps/web/.env` 建議 `VITE_API_BASE=`(空),走 Vite proxy `/api``:8888`
遠端開 `*.30cm.net` 時**不要**寫死 `http://127.0.0.1:8888`(那會打到使用者自己電腦)
7. 切 live 後**重新登入** seed`admin@haixun.local` / `admin123`
| 資料 | 存哪 | 關機 |
|------|------|------|
| 會員 / settings | **Mongo**monc | 保留 |
| refresh token / 驗證碼 | **Mongo**TTL index | 保留(過期自動清) |
| monc 實體快取 | RedisCacheRedis | 可丟,會從 Mongo 回填 |
## 目錄
```text
generate/api/ # goctl 契約
generate/database/mongo # golang-migrate JSON
generate/goctl/ # handler 模板102000
cmd/worker/ # 獨立 worker
internal/handler|logic # goctl 產 / 薄邏輯
internal/module/ # domain · repository · usecase
internal/lib/mongo # monc URI 小工具
etc/gateway.yaml # 唯一設定
```
## 設定摘要
```yaml
CacheRedis: # monc Redis 快取
- Host: 127.0.0.1:6379
Type: node
Mongo:
URI: "mongodb://127.0.0.1:27017" # 有 auth 用 MONGO_URI 覆寫
Database: "haixun"
```
### 會員 uid
-**1_000_000**seed admin = `1000000`
- 顯示補成 **8 位**`01000000``%08d` / FE `padStart(8,"0")`
- 新註冊:`1000001`, `1000002`, …