Files
alterminal/.zcode/plans/plan-sess_de9dbda7-7f39-4521-9978-101f4bf6beb3.md

40 lines
3.0 KiB
Markdown

# 實作 `create-account` CLI 子指令
## 背景
alterminal(Go + chi + GORM + PostgreSQL 的 OIDC 服務)目前 `User` 模型(user.go)與 argon2id 密碼雜湊已完成,但沒有任何建立帳號的入口。要在二進位檔加入 CLI 子指令:`./alterminal create-account ...`,不帶參數時行為不變(啟動 HTTP 伺服器)。
## 指令介面
```
alterminal create-account -username alice -email alice@example.com [-name "Alice"] [-email-verified] [-password secret]
```
- `-username`(必填):登入帳號,限制 `^[A-Za-z0-9._-]+$`、長度 ≤ 64
- `-email`(必填):以 `net/mail.ParseAddress` 驗證格式,長度 ≤ 255
- `-name`(選填):顯示名稱,長度 ≤ 255
- `-email-verified`(選填,預設 false):設定 OIDC `email_verified` claim
- `-password`(選填):密碼,最小長度 8(OWASP 建議)。**省略時以互動式無回顯提示輸入兩次**(用 `golang.org/x/term.ReadPassword`),兩次不一致則報錯;非終端機環境(管線)未提供旗標時直接報錯提示改用 `-password`
## 檔案變更
1. **新增依賴**:`go get golang.org/x/term`(與既有 x/crypto 同屬 golang.org/x)
2. **main.go**:在 `main()` 開頭加入子指令分派——`len(os.Args) > 1` 時交給 `runCommand(os.Args[1:])`,否則照常啟動伺服器;伺服器部分程式碼不動
3. **新增 `createaccount.go`**(沿用根目錄、`package main` 的現有風格):
- `runCommand`:分派子指令;僅有 `create-account`,未知子指令印用法後離開
- `runCreateAccount(args)`:
1. `flag.NewFlagSet("create-account", flag.ExitOnError)` 解析旗標,欄位 `strings.TrimSpace`
2. `validateAccountInput` 驗證 username/email/name(可單元測試的純函式)
3. `resolvePassword`:旗標優先,否則互動輸入兩次
4. 重用 `openDB()`(含 AutoMigrate,確保資料表存在)
5. 建立 `User` 並呼叫現有的 `SetPassword`(argon2id)
6. 重複檢查:先以查詢提供友善錯誤(帳號已存在 / Email 已存在),`db.Create` 再以 `gorm.ErrDuplicatedRows` 兜底(並發保護)
7. 成功輸出 `帳號建立成功:id=1 username=alice email=alice@example.com`(不印密碼);失敗經 `log.Fatal("create-account: ", err)` 離開(與現有 main 錯誤風格一致)
- 使用者面向訊息採繁體中文(與 README、程式註解一致)
4. **新增 `createaccount_test.go`**:仿照 `user_test.go` 的 table-driven 純邏輯測試——`validateAccountInput` 各種非法輸入、密碼長度檢查(不含需要 DB 或 TTY 的部分,專案目前無 DB 測試基礎設施)
5. **README.md**:在「快速開始」加入「建立使用者帳號」小節(指令、旗標、互動輸入說明);Roadmap 的「使用者系統」僅完成一環,維持未勾選
## 驗證
- `go build ./...`、`go vet ./...`、`go test ./...`
- 煙霧測試:若本機 PostgreSQL 有啟動,執行 `go run . create-account -username smoke -email smoke@example.com -password testpass1`,確認成功輸出、重複執行收到「已存在」錯誤、`CheckPassword` 可驗證;無 DB 時以單元測試與建置結果為準