docs(api): group OpenAPI by tags + add Chinese field descriptions and enums
Make the generated docs/openapi/gateway.yaml usable by adding three things go-doc parses out of the .api source: - @server tags + summary on every block → Swagger UI groups endpoints (Auth / Member / Permission / Normal) instead of dumping everything under "default". - backtick end-of-line // 中文 on every Request field → property descriptions in the schema. go-doc only reads the trailing comment, not the line above, so all comments are placed on the same line as the tag. - options=A|B|C in json/form tags wherever validate:"oneof=..." exists → enum dropdowns. The validate tag is kept for runtime validation; go-zero also enforces options= at bind time. Codify the rules in generate/api/README.md (tags / 行末註解 / options=) and add AGENTS.md at repo root so any AI agent (Claude / Cursor / Codex) picks them up automatically when working on the project. types.go regenerated via make gen-api to keep json tags in sync. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
fa50c64ee4
commit
4590f1c951
|
|
@ -0,0 +1,74 @@
|
|||
# AGENTS.md
|
||||
|
||||
給 AI coding agent(Claude / Cursor / Codex / 其他)的專案工作準則。請在開始任務前讀過一遍,並在需要時翻閱對應子文件。
|
||||
|
||||
## 專案簡介
|
||||
|
||||
`template-monorepo` 是基於 [go-zero](https://github.com/zeromicro/go-zero) 的 API Gateway,採用「**模組化 Clean Architecture**」:每個業務模組(auth / member / notification / permission ...)放在 `internal/model/<module>/`,內部分 `domain`(介面 + enum + errors) / `repository`(Mongo / Redis 實作) / `usecase`(原子業務邏輯) / `config`。
|
||||
|
||||
跨模組編排(例如「發 OTP → 寄信 → 驗碼 → 更新 profile」)一律放在 `internal/logic/<module>/`,**usecase 不可呼叫其他 usecase**。
|
||||
|
||||
## 必讀文件
|
||||
|
||||
| 文件 | 何時讀 |
|
||||
|---|---|
|
||||
| [`generate/api/README.md`](generate/api/README.md) | 新增 / 修改 API 端點、type、文件分組、欄位描述、enum 列舉前 |
|
||||
| [`generate/doc-generate/README.md`](generate/doc-generate/README.md) | 需要查 go-doc 支援的 tag / `@respdoc` 寫法時 |
|
||||
| `internal/model/<module>/README.md` | 動到該模組的領域邏輯時 |
|
||||
| `docs/model.md`(若存在) | 全局架構規範 |
|
||||
|
||||
## 標準工作流程
|
||||
|
||||
### 1. 修改 API(`.api` → handler / types / docs)
|
||||
|
||||
1. 編輯 `generate/api/*.api`(遵守 `generate/api/README.md` 的三條規則:tags 分組 / backtick 行末 `//` 中文 description / `options=A|B|C` enum)
|
||||
2. `make gen-api` — 重新產生 `internal/handler/`、`internal/logic/`(已存在則不覆蓋)、`internal/types/types.go`
|
||||
3. `make gen-doc` — 重新產生 `docs/openapi/gateway.yaml`(gitignore,本地驗證用)
|
||||
4. 實作 / 修改 `internal/logic/<module>/<handler>_logic.go` 的業務邏輯
|
||||
5. `go build ./...` 確保編譯通過
|
||||
6. `make lint` / `make test` 視改動範圍跑
|
||||
|
||||
### 2. 新增 / 修改業務模組
|
||||
|
||||
- 領域介面與型別放 `internal/model/<module>/domain/`
|
||||
- Mongo / Redis 實作放 `internal/model/<module>/repository/`
|
||||
- 原子 usecase 放 `internal/model/<module>/usecase/`(**不可**互相呼叫)
|
||||
- 多步驟流程編排放 `internal/logic/<module>/`
|
||||
- 模組的對外裝配入口統一在 `internal/model/<module>/usecase/module.go`,並從 `internal/svc/service_context.go` 注入
|
||||
|
||||
### 3. 錯誤碼
|
||||
|
||||
- 業務碼格式 `SSCCCDDD`(scope * 1_000_000 + category * 1_000 + detail)
|
||||
- Scope 註冊在 `internal/library/errors/code/types.go`(Facade=10, Auth=28, Member=29, Notification=30, Permission=31)
|
||||
- 新增 scope 時:同步更新 `gateway.api` 的 `bizCodeEnumDescription`
|
||||
|
||||
### 4. Redis / Mongo / 設定
|
||||
|
||||
- 每個模組的設定型別放 `internal/model/<module>/config/config.go`,再合入 `internal/config/config.go` 的 `Config` struct
|
||||
- Redis client 共用 `internal/library/redis/`,需 Pub/Sub 用 `client.PubSubClient()`
|
||||
- Mongo index 註冊到 `cmd/mongo-index/main.go`(在 `run()` 裡呼叫 `<module>repo.EnsureMongoIndexes`)
|
||||
|
||||
## 通用準則
|
||||
|
||||
- **回應 traditional Chinese**(繁體中文)
|
||||
- 程式碼註解只寫「為什麼」、邊界條件、trade-off,**不寫**「import the module / increment counter」這類顯而易見的描述
|
||||
- 不主動建立 `*.md` 文件,除非使用者明確要求
|
||||
- 改 git config / 強制 push / `rm -rf` 等破壞性操作 **必須**先取得使用者同意
|
||||
- 不要在沒被要求時直接 commit;commit 前先 `git status` / `git diff` 確認
|
||||
- commit message 用繁中描述「為什麼」改,不是「改了什麼」
|
||||
|
||||
## 指令速查
|
||||
|
||||
| 指令 | 用途 |
|
||||
|---|---|
|
||||
| `make gen-api` | `.api` → handler / logic(skip exists)/ types |
|
||||
| `make gen-doc` | `.api` → `docs/openapi/gateway.yaml` |
|
||||
| `make gen-mock` | 模組 mock(gomock) |
|
||||
| `make tools` | 安裝 goctl / goimports / golangci-lint |
|
||||
| `make fix` | gofmt + goimports + lint --fix + lint |
|
||||
| `make check` | fix + test(提交前) |
|
||||
| `make run-dev` | 本機啟動(需 `make deps-up`) |
|
||||
| `make deps-up` | docker compose Mongo + Redis |
|
||||
| `make mongo-index` | 建立 / 更新 Mongo 索引 |
|
||||
|
||||
完整列表跑 `make help`。
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
| `common.api` | 共用文件型別(`APIErrorStatus`、`ErrorDetail`) |
|
||||
| `auth.api` | Auth 路由(scope 28) |
|
||||
| `member.api` | Member 路由(scope 29) |
|
||||
| `permission.api` | Permission / RBAC 路由(scope 31) |
|
||||
| `normal.api` | 路由與業務 `data` 型別 |
|
||||
|
||||
## 指令
|
||||
|
|
@ -25,6 +26,74 @@ make gen-doc # 生成 docs/openapi/gateway.yaml(OpenAPI 3.0)
|
|||
- 多狀態碼用 `/* @respdoc-200 ... */` 區塊,放在 `@handler` 前
|
||||
- **Request 驗證**:欄位可加 `validate:"required,email"` 等 tag;`make gen-api` 後 handler 會自動 `ValidateAll`(見 `generate/goctl/api/handler.tpl`)
|
||||
|
||||
## 文件分組與欄位說明(go-doc 規則)
|
||||
|
||||
OpenAPI 由 `generate/doc-generate`(go-doc)從 `.api` 直接讀取,下列三種寫法**必須**遵守,AI agent 在新增 / 修改 API 時請一併照做:
|
||||
|
||||
### 1. Swagger UI 分組(tags)
|
||||
|
||||
每個 `.api` 檔的 `@server (...)` 區塊**必須**帶 `tags:` 與 `summary:`,否則 endpoint 會散落在 "default" 群組。
|
||||
|
||||
```go
|
||||
@server (
|
||||
group: permission // goctl 用:決定 handler 子目錄
|
||||
prefix: /api/v1/permissions
|
||||
tags: "Permission - 權限" // ← go-doc 用:Swagger UI 分組標題
|
||||
summary: "Catalog / 角色 / 使用者角色 / 外部映射 / Policy"
|
||||
)
|
||||
```
|
||||
|
||||
> 一個 `.api` 內可以開**多個** `@server` 區塊(相同 `group` / `prefix` 但不同 `tags:`),把同一模組再拆成子組。
|
||||
|
||||
### 2. 欄位中文 description
|
||||
|
||||
go-doc 把欄位的 description **只認 backtick 結尾的同一行 `//` 註解**,寫在前一行不會被解析。
|
||||
|
||||
```go
|
||||
// ❌ 不會被當 description
|
||||
RegisterReq {
|
||||
// 租戶 slug
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
}
|
||||
|
||||
// ✅ 正確寫法(backtick 行末 //)
|
||||
RegisterReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug(小寫英數)
|
||||
Email string `json:"email" validate:"required,email"` // 電子郵件
|
||||
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼(8-128 字元)
|
||||
}
|
||||
```
|
||||
|
||||
**所有 Request 型別**(命名 `*Req` / `*Query` / `*Path` 的 struct)的**每個欄位**都要加中文 description。Response / Data 型別可選。
|
||||
|
||||
### 3. Enum 列舉值(Swagger UI 下拉選單)
|
||||
|
||||
`validate:"oneof=A B C"` 是 runtime 驗證用的,**go-doc 不會解析**。要讓 Swagger UI 顯示下拉選單,必須在 tag 內加 `options=A|B|C`(管道分隔):
|
||||
|
||||
```go
|
||||
// ❌ 只有 validate 不會出 enum
|
||||
Provider string `json:"provider" validate:"required,oneof=google"`
|
||||
|
||||
// ✅ 同時保留兩者:options= 給 go-doc,validate= 給 runtime
|
||||
Provider string `json:"provider,options=google" validate:"required,oneof=google"`
|
||||
Source string `json:"source,optional,options=manual|zitadel|ldap|scim" validate:"omitempty,oneof=manual zitadel ldap scim"`
|
||||
Status string `form:"status,optional,options=open|close" validate:"omitempty,oneof=open close"`
|
||||
```
|
||||
|
||||
**規則**:只要有 `oneof=...`,就一定要在同欄位 tag 加對應 `options=...`,並把可選值也寫進行末註解。
|
||||
|
||||
### 4. 修改流程
|
||||
|
||||
每次改 `.api` 後**必跑**:
|
||||
|
||||
```bash
|
||||
make gen-api # 同步 internal/types/types.go(json tag 帶 options= 給 go-zero runtime)
|
||||
make gen-doc # 同步 docs/openapi/gateway.yaml(gitignore,本地驗證用)
|
||||
go build ./... # 確保編譯通過
|
||||
```
|
||||
|
||||
驗證:把 `docs/openapi/gateway.yaml` 丟進 https://editor.swagger.io 或 Swagger UI,確認分組、description、enum 都正確顯示。
|
||||
|
||||
## 與 runtime 對齊
|
||||
|
||||
Handler 使用 `response.Write` 輸出:
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ syntax = "v1"
|
|||
|
||||
type (
|
||||
RegisterReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
InviteCode string `json:"invite_code" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Language string `json:"language,optional"`
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"`
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug(小寫英數)
|
||||
InviteCode string `json:"invite_code" validate:"required"` // 邀請碼
|
||||
Email string `json:"email" validate:"required,email"` // 電子郵件
|
||||
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼(8-128 字元)
|
||||
DisplayName string `json:"display_name,optional"` // 顯示名稱(可選)
|
||||
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en(可選)
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"` // 使用者接受的服務條款版本
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"` // 是否同意接收行銷訊息
|
||||
}
|
||||
|
||||
RegisterData {
|
||||
|
|
@ -19,14 +19,14 @@ type (
|
|||
}
|
||||
|
||||
RegisterConfirmReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
ChallengeID string `json:"challenge_id" validate:"required"`
|
||||
Code string `json:"code" validate:"required,len=6"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
ChallengeID string `json:"challenge_id" validate:"required"` // 註冊流程的 OTP challenge ID(由 /register 回傳)
|
||||
Code string `json:"code" validate:"required,len=6"` // 6 位數 OTP 驗證碼
|
||||
}
|
||||
|
||||
RegisterResendReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
ChallengeID string `json:"challenge_id" validate:"required"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
ChallengeID string `json:"challenge_id" validate:"required"` // 註冊流程的 OTP challenge ID
|
||||
}
|
||||
|
||||
AuthTokenData {
|
||||
|
|
@ -38,13 +38,13 @@ type (
|
|||
}
|
||||
|
||||
RegisterSocialStartReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
InviteCode string `json:"invite_code" validate:"required"`
|
||||
Provider string `json:"provider" validate:"required,oneof=google"`
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"`
|
||||
Language string `json:"language,optional"`
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"`
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
InviteCode string `json:"invite_code" validate:"required"` // 邀請碼
|
||||
Provider string `json:"provider,options=google" validate:"required,oneof=google"` // 第三方登入提供者;可選值: google
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"` // 使用者接受的服務條款版本
|
||||
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en(可選)
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"` // OAuth 完成後要 redirect 回的 URI
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"` // 是否同意接收行銷訊息
|
||||
}
|
||||
|
||||
RegisterSocialStartData {
|
||||
|
|
@ -54,29 +54,29 @@ type (
|
|||
}
|
||||
|
||||
RegisterSocialCallbackReq {
|
||||
Code string `form:"code" validate:"required"`
|
||||
State string `form:"state" validate:"required"`
|
||||
Code string `form:"code" validate:"required"` // IdP 回傳的 OAuth authorization code
|
||||
State string `form:"state" validate:"required"` // IdP 回傳的 OAuth state(對應 session)
|
||||
}
|
||||
|
||||
LoginReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
Email string `json:"email" validate:"required,email"` // 電子郵件
|
||||
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼(8-128 字元)
|
||||
}
|
||||
|
||||
TokenRefreshReq {
|
||||
RefreshToken string `json:"refresh_token" validate:"required"`
|
||||
RefreshToken string `json:"refresh_token" validate:"required"` // 先前核發的 refresh token
|
||||
}
|
||||
|
||||
TokenExchangeReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
IDToken string `json:"id_token" validate:"required"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
IDToken string `json:"id_token" validate:"required"` // ZITADEL 發行的 id_token
|
||||
}
|
||||
|
||||
LoginSocialStartReq {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
Provider string `json:"provider" validate:"required,oneof=google"`
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
Provider string `json:"provider,options=google" validate:"required,oneof=google"` // 第三方登入提供者;可選值: google
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"` // OAuth 完成後要 redirect 回的 URI
|
||||
}
|
||||
|
||||
LoginSocialStartData {
|
||||
|
|
@ -86,8 +86,8 @@ type (
|
|||
}
|
||||
|
||||
LoginSocialCallbackReq {
|
||||
Code string `form:"code" validate:"required"`
|
||||
State string `form:"state" validate:"required"`
|
||||
Code string `form:"code" validate:"required"` // IdP 回傳的 OAuth authorization code
|
||||
State string `form:"state" validate:"required"` // IdP 回傳的 OAuth state(對應 session)
|
||||
}
|
||||
|
||||
LogoutData {
|
||||
|
|
@ -129,6 +129,8 @@ type (
|
|||
@server(
|
||||
group: auth
|
||||
prefix: /api/v1/auth
|
||||
tags: "Auth - 認證"
|
||||
summary: "註冊 / 登入 / Token / 登出"
|
||||
)
|
||||
service gateway {
|
||||
@doc "Email 註冊(建立 ZITADEL + member,寄 registration OTP)"
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@ type (
|
|||
}
|
||||
|
||||
UpdateMemberMeReq {
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Avatar string `json:"avatar,optional"`
|
||||
Language string `json:"language,optional"`
|
||||
Currency string `json:"currency,optional"`
|
||||
Phone string `json:"phone,optional"`
|
||||
DisplayName string `json:"display_name,optional"` // 顯示名稱(可選)
|
||||
Avatar string `json:"avatar,optional"` // 頭像 URL(可選)
|
||||
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en(可選)
|
||||
Currency string `json:"currency,optional"` // 幣別代碼,如 TWD / USD(可選)
|
||||
Phone string `json:"phone,optional"` // 聯絡電話 E.164 格式(可選)
|
||||
}
|
||||
|
||||
VerificationStartReq {
|
||||
Target string `json:"target"`
|
||||
Target string `json:"target"` // 驗證目標:email 地址或 E.164 手機號(依端點而定)
|
||||
}
|
||||
|
||||
VerificationStartData {
|
||||
|
|
@ -39,8 +39,8 @@ type (
|
|||
}
|
||||
|
||||
VerificationConfirmReq {
|
||||
ChallengeID string `json:"challenge_id"`
|
||||
Code string `json:"code"`
|
||||
ChallengeID string `json:"challenge_id"` // 驗證流程的 OTP challenge ID(由 /start 端點回傳)
|
||||
Code string `json:"code"` // 6 位數 OTP 驗證碼
|
||||
}
|
||||
|
||||
TOTPStatusData {
|
||||
|
|
@ -61,7 +61,7 @@ type (
|
|||
}
|
||||
|
||||
TOTPEnrollConfirmReq {
|
||||
Code string `json:"code"`
|
||||
Code string `json:"code"` // Google Authenticator 顯示的 6 位數 TOTP 碼
|
||||
}
|
||||
|
||||
TOTPEnrollConfirmData {
|
||||
|
|
@ -69,7 +69,7 @@ type (
|
|||
}
|
||||
|
||||
TOTPVerifyReq {
|
||||
Code string `json:"code"`
|
||||
Code string `json:"code"` // TOTP 6 位數碼,或 8 位數備援碼
|
||||
}
|
||||
|
||||
TOTPBackupCodesData {
|
||||
|
|
@ -117,6 +117,8 @@ type (
|
|||
@server(
|
||||
group: member
|
||||
prefix: /api/v1/members
|
||||
tags: "Member - 會員"
|
||||
summary: "Profile / 業務 Email 與 Phone 驗證 / TOTP MFA"
|
||||
)
|
||||
service gateway {
|
||||
@doc "取得當前會員 profile(Bearer JWT;本機 dev 可 fallback X-Tenant-ID + X-UID)"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ type PingOKStatus {
|
|||
prefix: /api/v1
|
||||
schemes: https
|
||||
timeout: 3s
|
||||
tags: "Normal - 公開"
|
||||
summary: "健康檢查 / Ping"
|
||||
)
|
||||
service gateway {
|
||||
@doc(
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ type (
|
|||
// ===== Permission catalog =====
|
||||
|
||||
PermissionCatalogQuery {
|
||||
Status string `form:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
Type string `form:"type,optional" validate:"omitempty,oneof=backend_user frontend_user"`
|
||||
Tree bool `form:"tree,optional"`
|
||||
Status string `form:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 狀態篩選;可選值: open / close
|
||||
Type string `form:"type,optional,options=backend_user|frontend_user" validate:"omitempty,oneof=backend_user frontend_user"` // 權限類型篩選;可選值: backend_user / frontend_user
|
||||
Tree bool `form:"tree,optional"` // 是否回傳樹狀結構(false 則扁平 list)
|
||||
}
|
||||
|
||||
PermissionNode {
|
||||
|
|
@ -28,7 +28,7 @@ type (
|
|||
// ===== Me permissions =====
|
||||
|
||||
MePermissionsQuery {
|
||||
IncludeTree bool `form:"include_tree,optional"`
|
||||
IncludeTree bool `form:"include_tree,optional"` // 是否同時回傳 permission tree(true 會多包一份樹狀結構)
|
||||
}
|
||||
|
||||
MePermissionsData {
|
||||
|
|
@ -58,48 +58,48 @@ type (
|
|||
}
|
||||
|
||||
CreateRoleReq {
|
||||
Key string `json:"key" validate:"required,min=2,max=64"`
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Status string `json:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
Key string `json:"key" validate:"required,min=2,max=64"` // 角色 key(2-64 字元,不可以 system. / platform_ 開頭)
|
||||
DisplayName string `json:"display_name,optional"` // 角色顯示名稱(給人看的)
|
||||
Status string `json:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 角色狀態;可選值: open / close(預設 open)
|
||||
}
|
||||
|
||||
UpdateRoleReq {
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Status string `json:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
DisplayName string `json:"display_name,optional"` // 角色顯示名稱
|
||||
Status string `json:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 角色狀態;可選值: open / close
|
||||
}
|
||||
|
||||
UpdateRoleByIDReq {
|
||||
ID string `path:"id"`
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Status string `json:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
DisplayName string `json:"display_name,optional"` // 角色顯示名稱
|
||||
Status string `json:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 角色狀態;可選值: open / close
|
||||
}
|
||||
|
||||
DeleteRoleByIDReq {
|
||||
ID string `path:"id"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
}
|
||||
|
||||
GetRolePermissionsByIDReq {
|
||||
ID string `path:"id"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
}
|
||||
|
||||
ReplaceRolePermissionsByIDReq {
|
||||
ID string `path:"id"`
|
||||
PermissionIDs []string `json:"permission_ids"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
PermissionIDs []string `json:"permission_ids"` // 要勾選的 Permission ID 陣列(全量取代,會自動補齊父權限)
|
||||
}
|
||||
|
||||
ListUserRolesReq {
|
||||
UID string `path:"uid"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
}
|
||||
|
||||
AssignUserRoleByUIDReq {
|
||||
UID string `path:"uid"`
|
||||
RoleID string `json:"role_id" validate:"required"`
|
||||
Source string `json:"source,optional" validate:"omitempty,oneof=manual zitadel ldap scim"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
RoleID string `json:"role_id" validate:"required"` // 要指派的 Role ID
|
||||
Source string `json:"source,optional,options=manual|zitadel|ldap|scim" validate:"omitempty,oneof=manual zitadel ldap scim"` // 指派來源;可選值: manual / zitadel / ldap / scim(預設 manual)
|
||||
}
|
||||
|
||||
RevokeUserRoleByIDReq {
|
||||
UID string `path:"uid"`
|
||||
RoleID string `path:"role_id"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
RoleID string `path:"role_id"` // Role ID(path)
|
||||
}
|
||||
|
||||
// ===== Role permissions =====
|
||||
|
|
@ -109,13 +109,13 @@ type (
|
|||
}
|
||||
|
||||
ReplaceRolePermissionsReq {
|
||||
PermissionIDs []string `json:"permission_ids"`
|
||||
PermissionIDs []string `json:"permission_ids"` // 要勾選的 Permission ID 陣列(全量取代,會自動補齊父權限)
|
||||
}
|
||||
|
||||
// ===== User roles =====
|
||||
|
||||
UIDPath {
|
||||
UID string `path:"uid"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
}
|
||||
|
||||
UserRoleData {
|
||||
|
|
@ -135,13 +135,13 @@ type (
|
|||
}
|
||||
|
||||
AssignUserRoleReq {
|
||||
RoleID string `json:"role_id" validate:"required"`
|
||||
Source string `json:"source,optional" validate:"omitempty,oneof=manual zitadel ldap scim"`
|
||||
RoleID string `json:"role_id" validate:"required"` // 要指派的 Role ID
|
||||
Source string `json:"source,optional,options=manual|zitadel|ldap|scim" validate:"omitempty,oneof=manual zitadel ldap scim"` // 指派來源;可選值: manual / zitadel / ldap / scim(預設 manual)
|
||||
}
|
||||
|
||||
UserRoleIDPath {
|
||||
UID string `path:"uid"`
|
||||
RoleID string `path:"role_id"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
RoleID string `path:"role_id"` // Role ID(path)
|
||||
}
|
||||
|
||||
// ===== Role mappings =====
|
||||
|
|
@ -165,26 +165,26 @@ type (
|
|||
}
|
||||
|
||||
RoleMappingListQuery {
|
||||
Source string `form:"source,optional" validate:"omitempty,oneof=zitadel ldap scim"`
|
||||
Offset int64 `form:"offset,optional"`
|
||||
Limit int64 `form:"limit,optional"`
|
||||
Source string `form:"source,optional,options=zitadel|ldap|scim" validate:"omitempty,oneof=zitadel ldap scim"` // 外部來源篩選;可選值: zitadel / ldap / scim
|
||||
Offset int64 `form:"offset,optional"` // 分頁起點(從 0 起算)
|
||||
Limit int64 `form:"limit,optional"` // 每頁筆數(最大值由 server 限制)
|
||||
}
|
||||
|
||||
UpsertRoleMappingReq {
|
||||
ExternalSource string `json:"external_source" validate:"required,oneof=zitadel ldap scim"`
|
||||
ExternalKey string `json:"external_key" validate:"required"`
|
||||
InternalRoleKey string `json:"internal_role_key" validate:"required"`
|
||||
ExternalSource string `json:"external_source,options=zitadel|ldap|scim" validate:"required,oneof=zitadel ldap scim"` // 外部來源;可選值: zitadel / ldap / scim
|
||||
ExternalKey string `json:"external_key" validate:"required"` // 外部群組 / claim 的 key(例如 zitadel role 名稱)
|
||||
InternalRoleKey string `json:"internal_role_key" validate:"required"` // 對映的內部角色 key(必須存在於 roles 集合)
|
||||
}
|
||||
|
||||
DeleteRoleMappingReq {
|
||||
ExternalSource string `json:"external_source" validate:"required,oneof=zitadel ldap scim"`
|
||||
ExternalKey string `json:"external_key" validate:"required"`
|
||||
ExternalSource string `json:"external_source,options=zitadel|ldap|scim" validate:"required,oneof=zitadel ldap scim"` // 外部來源;可選值: zitadel / ldap / scim
|
||||
ExternalKey string `json:"external_key" validate:"required"` // 外部群組 / claim 的 key
|
||||
}
|
||||
|
||||
// ===== Policy reload =====
|
||||
|
||||
PolicyReloadReq {
|
||||
TenantID string `json:"tenant_id,optional"`
|
||||
TenantID string `json:"tenant_id,optional"` // 指定要 reload 的租戶 ID;留空則 reload 所有租戶
|
||||
}
|
||||
|
||||
PolicyReloadData {
|
||||
|
|
@ -258,6 +258,8 @@ type (
|
|||
@server(
|
||||
group: permission
|
||||
prefix: /api/v1/permissions
|
||||
tags: "Permission - 權限"
|
||||
summary: "Catalog / 角色 / 使用者角色 / 外部映射 / Policy"
|
||||
)
|
||||
service gateway {
|
||||
@doc "取得全局 Permission Catalog(樹狀或扁平;可篩 status/type)"
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ type APIErrorStatus struct {
|
|||
}
|
||||
|
||||
type AssignUserRoleByUIDReq struct {
|
||||
UID string `path:"uid"`
|
||||
RoleID string `json:"role_id" validate:"required"`
|
||||
Source string `json:"source,optional" validate:"omitempty,oneof=manual zitadel ldap scim"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
RoleID string `json:"role_id" validate:"required"` // 要指派的 Role ID
|
||||
Source string `json:"source,optional,options=manual|zitadel|ldap|scim" validate:"omitempty,oneof=manual zitadel ldap scim"` // 指派來源;可選值: manual / zitadel / ldap / scim(預設 manual)
|
||||
}
|
||||
|
||||
type AssignUserRoleReq struct {
|
||||
RoleID string `json:"role_id" validate:"required"`
|
||||
Source string `json:"source,optional" validate:"omitempty,oneof=manual zitadel ldap scim"`
|
||||
RoleID string `json:"role_id" validate:"required"` // 要指派的 Role ID
|
||||
Source string `json:"source,optional,options=manual|zitadel|ldap|scim" validate:"omitempty,oneof=manual zitadel ldap scim"` // 指派來源;可選值: manual / zitadel / ldap / scim(預設 manual)
|
||||
}
|
||||
|
||||
type AuthTokenData struct {
|
||||
|
|
@ -35,18 +35,18 @@ type AuthTokenOKStatus struct {
|
|||
}
|
||||
|
||||
type CreateRoleReq struct {
|
||||
Key string `json:"key" validate:"required,min=2,max=64"`
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Status string `json:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
Key string `json:"key" validate:"required,min=2,max=64"` // 角色 key(2-64 字元,不可以 system. / platform_ 開頭)
|
||||
DisplayName string `json:"display_name,optional"` // 角色顯示名稱(給人看的)
|
||||
Status string `json:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 角色狀態;可選值: open / close(預設 open)
|
||||
}
|
||||
|
||||
type DeleteRoleByIDReq struct {
|
||||
ID string `path:"id"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
}
|
||||
|
||||
type DeleteRoleMappingReq struct {
|
||||
ExternalSource string `json:"external_source" validate:"required,oneof=zitadel ldap scim"`
|
||||
ExternalKey string `json:"external_key" validate:"required"`
|
||||
ExternalSource string `json:"external_source,options=zitadel|ldap|scim" validate:"required,oneof=zitadel ldap scim"` // 外部來源;可選值: zitadel / ldap / scim
|
||||
ExternalKey string `json:"external_key" validate:"required"` // 外部群組 / claim 的 key
|
||||
}
|
||||
|
||||
type EmptyOKStatus struct {
|
||||
|
|
@ -62,22 +62,22 @@ type ErrorDetail struct {
|
|||
}
|
||||
|
||||
type GetRolePermissionsByIDReq struct {
|
||||
ID string `path:"id"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
}
|
||||
|
||||
type ListUserRolesReq struct {
|
||||
UID string `path:"uid"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
}
|
||||
|
||||
type LoginReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
Email string `json:"email" validate:"required,email"` // 電子郵件
|
||||
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼(8-128 字元)
|
||||
}
|
||||
|
||||
type LoginSocialCallbackReq struct {
|
||||
Code string `form:"code" validate:"required"`
|
||||
State string `form:"state" validate:"required"`
|
||||
Code string `form:"code" validate:"required"` // IdP 回傳的 OAuth authorization code
|
||||
State string `form:"state" validate:"required"` // IdP 回傳的 OAuth state(對應 session)
|
||||
}
|
||||
|
||||
type LoginSocialStartData struct {
|
||||
|
|
@ -93,9 +93,9 @@ type LoginSocialStartOKStatus struct {
|
|||
}
|
||||
|
||||
type LoginSocialStartReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
Provider string `json:"provider" validate:"required,oneof=google"`
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
Provider string `json:"provider,options=google" validate:"required,oneof=google"` // 第三方登入提供者;可選值: google
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"` // OAuth 完成後要 redirect 回的 URI
|
||||
}
|
||||
|
||||
type LogoutData struct {
|
||||
|
|
@ -123,7 +123,7 @@ type MePermissionsOKStatus struct {
|
|||
}
|
||||
|
||||
type MePermissionsQuery struct {
|
||||
IncludeTree bool `form:"include_tree,optional"`
|
||||
IncludeTree bool `form:"include_tree,optional"` // 是否同時回傳 permission tree(true 會多包一份樹狀結構)
|
||||
}
|
||||
|
||||
type MemberMeData struct {
|
||||
|
|
@ -164,9 +164,9 @@ type PermissionCatalogOKStatus struct {
|
|||
}
|
||||
|
||||
type PermissionCatalogQuery struct {
|
||||
Status string `form:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
Type string `form:"type,optional" validate:"omitempty,oneof=backend_user frontend_user"`
|
||||
Tree bool `form:"tree,optional"`
|
||||
Status string `form:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 狀態篩選;可選值: open / close
|
||||
Type string `form:"type,optional,options=backend_user|frontend_user" validate:"omitempty,oneof=backend_user frontend_user"` // 權限類型篩選;可選值: backend_user / frontend_user
|
||||
Tree bool `form:"tree,optional"` // 是否回傳樹狀結構(false 則扁平 list)
|
||||
}
|
||||
|
||||
type PermissionNode struct {
|
||||
|
|
@ -202,13 +202,13 @@ type PolicyReloadOKStatus struct {
|
|||
}
|
||||
|
||||
type PolicyReloadReq struct {
|
||||
TenantID string `json:"tenant_id,optional"`
|
||||
TenantID string `json:"tenant_id,optional"` // 指定要 reload 的租戶 ID;留空則 reload 所有租戶
|
||||
}
|
||||
|
||||
type RegisterConfirmReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
ChallengeID string `json:"challenge_id" validate:"required"`
|
||||
Code string `json:"code" validate:"required,len=6"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
ChallengeID string `json:"challenge_id" validate:"required"` // 註冊流程的 OTP challenge ID(由 /register 回傳)
|
||||
Code string `json:"code" validate:"required,len=6"` // 6 位數 OTP 驗證碼
|
||||
}
|
||||
|
||||
type RegisterData struct {
|
||||
|
|
@ -224,24 +224,24 @@ type RegisterOKStatus struct {
|
|||
}
|
||||
|
||||
type RegisterReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
InviteCode string `json:"invite_code" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8,max=128"`
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Language string `json:"language,optional"`
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"`
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug(小寫英數)
|
||||
InviteCode string `json:"invite_code" validate:"required"` // 邀請碼
|
||||
Email string `json:"email" validate:"required,email"` // 電子郵件
|
||||
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼(8-128 字元)
|
||||
DisplayName string `json:"display_name,optional"` // 顯示名稱(可選)
|
||||
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en(可選)
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"` // 使用者接受的服務條款版本
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"` // 是否同意接收行銷訊息
|
||||
}
|
||||
|
||||
type RegisterResendReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
ChallengeID string `json:"challenge_id" validate:"required"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
ChallengeID string `json:"challenge_id" validate:"required"` // 註冊流程的 OTP challenge ID
|
||||
}
|
||||
|
||||
type RegisterSocialCallbackReq struct {
|
||||
Code string `form:"code" validate:"required"`
|
||||
State string `form:"state" validate:"required"`
|
||||
Code string `form:"code" validate:"required"` // IdP 回傳的 OAuth authorization code
|
||||
State string `form:"state" validate:"required"` // IdP 回傳的 OAuth state(對應 session)
|
||||
}
|
||||
|
||||
type RegisterSocialStartData struct {
|
||||
|
|
@ -257,27 +257,27 @@ type RegisterSocialStartOKStatus struct {
|
|||
}
|
||||
|
||||
type RegisterSocialStartReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
InviteCode string `json:"invite_code" validate:"required"`
|
||||
Provider string `json:"provider" validate:"required,oneof=google"`
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"`
|
||||
Language string `json:"language,optional"`
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"`
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
InviteCode string `json:"invite_code" validate:"required"` // 邀請碼
|
||||
Provider string `json:"provider,options=google" validate:"required,oneof=google"` // 第三方登入提供者;可選值: google
|
||||
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"` // 使用者接受的服務條款版本
|
||||
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en(可選)
|
||||
RedirectURI string `json:"redirect_uri" validate:"required,url"` // OAuth 完成後要 redirect 回的 URI
|
||||
MarketingOptIn bool `json:"marketing_opt_in,optional"` // 是否同意接收行銷訊息
|
||||
}
|
||||
|
||||
type ReplaceRolePermissionsByIDReq struct {
|
||||
ID string `path:"id"`
|
||||
PermissionIDs []string `json:"permission_ids"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
PermissionIDs []string `json:"permission_ids"` // 要勾選的 Permission ID 陣列(全量取代,會自動補齊父權限)
|
||||
}
|
||||
|
||||
type ReplaceRolePermissionsReq struct {
|
||||
PermissionIDs []string `json:"permission_ids"`
|
||||
PermissionIDs []string `json:"permission_ids"` // 要勾選的 Permission ID 陣列(全量取代,會自動補齊父權限)
|
||||
}
|
||||
|
||||
type RevokeUserRoleByIDReq struct {
|
||||
UID string `path:"uid"`
|
||||
RoleID string `path:"role_id"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
RoleID string `path:"role_id"` // Role ID(path)
|
||||
}
|
||||
|
||||
type RoleData struct {
|
||||
|
|
@ -327,9 +327,9 @@ type RoleMappingListOKStatus struct {
|
|||
}
|
||||
|
||||
type RoleMappingListQuery struct {
|
||||
Source string `form:"source,optional" validate:"omitempty,oneof=zitadel ldap scim"`
|
||||
Offset int64 `form:"offset,optional"`
|
||||
Limit int64 `form:"limit,optional"`
|
||||
Source string `form:"source,optional,options=zitadel|ldap|scim" validate:"omitempty,oneof=zitadel ldap scim"` // 外部來源篩選;可選值: zitadel / ldap / scim
|
||||
Offset int64 `form:"offset,optional"` // 分頁起點(從 0 起算)
|
||||
Limit int64 `form:"limit,optional"` // 每頁筆數(最大值由 server 限制)
|
||||
}
|
||||
|
||||
type RoleMappingOKStatus struct {
|
||||
|
|
@ -375,7 +375,7 @@ type TOTPEnrollConfirmOKStatus struct {
|
|||
}
|
||||
|
||||
type TOTPEnrollConfirmReq struct {
|
||||
Code string `json:"code"`
|
||||
Code string `json:"code"` // Google Authenticator 顯示的 6 位數 TOTP 碼
|
||||
}
|
||||
|
||||
type TOTPEnrollStartData struct {
|
||||
|
|
@ -408,45 +408,45 @@ type TOTPStatusOKStatus struct {
|
|||
}
|
||||
|
||||
type TOTPVerifyReq struct {
|
||||
Code string `json:"code"`
|
||||
Code string `json:"code"` // TOTP 6 位數碼,或 8 位數備援碼
|
||||
}
|
||||
|
||||
type TokenExchangeReq struct {
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"`
|
||||
IDToken string `json:"id_token" validate:"required"`
|
||||
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
|
||||
IDToken string `json:"id_token" validate:"required"` // ZITADEL 發行的 id_token
|
||||
}
|
||||
|
||||
type TokenRefreshReq struct {
|
||||
RefreshToken string `json:"refresh_token" validate:"required"`
|
||||
RefreshToken string `json:"refresh_token" validate:"required"` // 先前核發的 refresh token
|
||||
}
|
||||
|
||||
type UIDPath struct {
|
||||
UID string `path:"uid"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
}
|
||||
|
||||
type UpdateMemberMeReq struct {
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Avatar string `json:"avatar,optional"`
|
||||
Language string `json:"language,optional"`
|
||||
Currency string `json:"currency,optional"`
|
||||
Phone string `json:"phone,optional"`
|
||||
DisplayName string `json:"display_name,optional"` // 顯示名稱(可選)
|
||||
Avatar string `json:"avatar,optional"` // 頭像 URL(可選)
|
||||
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en(可選)
|
||||
Currency string `json:"currency,optional"` // 幣別代碼,如 TWD / USD(可選)
|
||||
Phone string `json:"phone,optional"` // 聯絡電話 E.164 格式(可選)
|
||||
}
|
||||
|
||||
type UpdateRoleByIDReq struct {
|
||||
ID string `path:"id"`
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Status string `json:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
ID string `path:"id"` // Role ID(path)
|
||||
DisplayName string `json:"display_name,optional"` // 角色顯示名稱
|
||||
Status string `json:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 角色狀態;可選值: open / close
|
||||
}
|
||||
|
||||
type UpdateRoleReq struct {
|
||||
DisplayName string `json:"display_name,optional"`
|
||||
Status string `json:"status,optional" validate:"omitempty,oneof=open close"`
|
||||
DisplayName string `json:"display_name,optional"` // 角色顯示名稱
|
||||
Status string `json:"status,optional,options=open|close" validate:"omitempty,oneof=open close"` // 角色狀態;可選值: open / close
|
||||
}
|
||||
|
||||
type UpsertRoleMappingReq struct {
|
||||
ExternalSource string `json:"external_source" validate:"required,oneof=zitadel ldap scim"`
|
||||
ExternalKey string `json:"external_key" validate:"required"`
|
||||
InternalRoleKey string `json:"internal_role_key" validate:"required"`
|
||||
ExternalSource string `json:"external_source,options=zitadel|ldap|scim" validate:"required,oneof=zitadel ldap scim"` // 外部來源;可選值: zitadel / ldap / scim
|
||||
ExternalKey string `json:"external_key" validate:"required"` // 外部群組 / claim 的 key(例如 zitadel role 名稱)
|
||||
InternalRoleKey string `json:"internal_role_key" validate:"required"` // 對映的內部角色 key(必須存在於 roles 集合)
|
||||
}
|
||||
|
||||
type UserRoleData struct {
|
||||
|
|
@ -462,8 +462,8 @@ type UserRoleData struct {
|
|||
}
|
||||
|
||||
type UserRoleIDPath struct {
|
||||
UID string `path:"uid"`
|
||||
RoleID string `path:"role_id"`
|
||||
UID string `path:"uid"` // 使用者 UID(path)
|
||||
RoleID string `path:"role_id"` // Role ID(path)
|
||||
}
|
||||
|
||||
type UserRoleListData struct {
|
||||
|
|
@ -483,8 +483,8 @@ type UserRoleOKStatus struct {
|
|||
}
|
||||
|
||||
type VerificationConfirmReq struct {
|
||||
ChallengeID string `json:"challenge_id"`
|
||||
Code string `json:"code"`
|
||||
ChallengeID string `json:"challenge_id"` // 驗證流程的 OTP challenge ID(由 /start 端點回傳)
|
||||
Code string `json:"code"` // 6 位數 OTP 驗證碼
|
||||
}
|
||||
|
||||
type VerificationStartData struct {
|
||||
|
|
@ -499,5 +499,5 @@ type VerificationStartOKStatus struct {
|
|||
}
|
||||
|
||||
type VerificationStartReq struct {
|
||||
Target string `json:"target"`
|
||||
Target string `json:"target"` // 驗證目標:email 地址或 E.164 手機號(依端點而定)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue