|
|
||
|---|---|---|
| .. | ||
| cmd | ||
| crawler | ||
| etc | ||
| generate | ||
| internal | ||
| scripts | ||
| .gitignore | ||
| MIGRATION.md | ||
| Makefile | ||
| README.md | ||
| gateway.go | ||
| go.mod | ||
| go.sum | ||
README.md
apps/backend
Harbor Desk Phase C(go-zero)。
原則
| 做 | 不做 |
|---|---|
generate/api + make gen-api |
手寫 handler/routes |
generate/database/mongo + make migrate-up |
在 gateway 啟動時隱式建 index/seed |
monc + CacheRedis |
自己半套 mongo cache |
internal/logic + `module/*/domain |
repository |
指令
cd apps/backend
make build # gateway + worker
make start # build 後背景啟動 gateway + worker(PID/log 在 /tmp/haixun)
make stop
make restart
make test
make gen-api # 改 .api 後
make migrate-up # 改 generate/database 後
make init # 只建立可重複執行的 operational indexes
make seeder # 讀 etc/gateway.yaml 的 Seed.AdminPassword
make crawler-install # 安裝 dev_mode Chrome crawler 的 Playwright/Chromium
SCOUT_CRAWLER_TOKEN='...' make crawler-start
SCOUT_CRAWLER_TOKEN='...' make crawler-restart
make tidy
Scout Chrome Crawler
dev_mode=true 時,Scout 只走私有 Playwright crawler,沒有同步有效 Chrome session 就會要求使用者先同步。
- 在私有
etc/gateway.yaml填Scout.SessionSecret、Scout.CrawlerEndpoint、Scout.CrawlerToken。 - 執行
make crawler-install一次。 - 執行
SCOUT_CRAWLER_TOKEN='與 Scout.CrawlerToken 相同的值' make crawler-start。 - crawler 必須維持
127.0.0.1私網綁定,禁止公開反向代理。
# 需要 Mongo + Redis(go-zero monc / redis)
# 私有設定只放在 etc/gateway.yaml(此檔不會被 git 追蹤):
# cp etc/gateway.example.yaml etc/gateway.yaml
# 填入 Mongo/Redis/MinIO 密碼與兩組不同的 JWT secret。
# Mongo 若有帳密(本機 infra 常見),一定要帶進 URI:
MONGO_URL='mongodb://USER:PASS@127.0.0.1:27017/haixun?authSource=admin' make migrate-up
go run . -f etc/gateway.yaml
# Seed.AdminPassword 填好後執行 make seeder(uid=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 |
| 後端 | MinIO(dev)或 S3(prod),S3 API + path-style |
| env | HAIXUN_STORAGE_S3_* 或 MINIO_ROOT_USER / MINIO_ROOT_PASSWORD |
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 顯示語系文案;placeholder/hint/錯誤都用同一 key,勿另寫一句。
寄信(信箱驗證 / 重設密碼)
業務在 internal/logic/auth/*:產碼 → Notification.RenderVerify → SendEmail。
寄送實作在 internal/module/notification(SMTP,Mailgun 相容)。
模板與多語
- 引擎與 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)
PublicWebBase: "http://127.0.0.1:5173"
Brand:
Name: "Harbor Desk"
LogoURL: "" # 空 = PublicWebBase/brand-mark.jpg
Copyright: "© Harbor Desk"
本機:Mailpit(推薦)
# 已含在 old/infra docker-compose(mongo/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
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 投遞 |
前後端串不起來?檢查這幾項
- gateway 有在跑
ss -tlnp | grep 8888;沒有就go run . -f etc/gateway.yaml - Mongo URI 有帳密(
export MONGO_URI=...;沒帶會查不到 seed) - Redis 有密碼(
export REDIS_PASSWORD=...;沒帶 monc 回NOAUTH→ API 500) - 已跑 migrate(
make migrate-up,seedadmin@haixun.local) - FE 切到 live:設定 → 資料來源 → Live(預設是 mock,根本不會打後端)
- API base:
apps/web/.env建議VITE_API_BASE=(空),走 Vite proxy/api→:8888
遠端開*.30cm.net時不要寫死http://127.0.0.1:8888(那會打到使用者自己電腦) - 切 live 後重新登入 seed:
admin@haixun.local/admin123
| 資料 | 存哪 | 關機 |
|---|---|---|
| 會員 / settings | Mongo(monc) | 保留 |
| refresh token / 驗證碼 | Mongo(TTL index) | 保留(過期自動清) |
| monc 實體快取 | Redis(CacheRedis) | 可丟,會從 Mongo 回填 |
目錄
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 # 唯一設定
設定摘要
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/ FEpadStart(8,"0")) - 新註冊:
1000001,1000002, …