mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-09-01 14:53:07 +08:00
feat(tenant): optional legacy auto-create API key on tenant creation
Add a tenant.auto_create_api_key setting (env WEKNORA_TENANT_AUTO_CREATE_API_KEY, default false) that restores the pre-break-change behaviour of minting a full_access API key when a tenant is created and returning its plaintext token in the create response (data.api_key). - system_setting: register tenant.auto_create_api_key bool spec - handler: gate auto key creation behind the setting; embed the token via tenantWithAPIKey without re-adding a persisted api_key column; key-creation failures log a warning and do not fail tenant creation - client: add optional Tenant.APIKey field populated only on create - docs/env: document the switch (.env.example, docker-compose, tenant.md, regenerated swagger)
This commit is contained in:
@@ -527,6 +527,14 @@ WEKNORA_SANDBOX_TIMEOUT=60
|
||||
# < 0 :彻底关闭限额(不建议在共享部署中使用)
|
||||
# WEKNORA_TENANT_MAX_OWNED_PER_USER=
|
||||
|
||||
# 创建租户时是否自动生成一个全量权限(full_access)的 API Key,
|
||||
# 并在「创建租户」接口的响应中返回其明文 token(字段 api_key)。
|
||||
# 用于兼容旧版本「创建租户即下发默认 API Key」的行为(破坏性变更的回退开关)。
|
||||
# - true :创建租户时自动创建 full_access API Key 并返回 token
|
||||
# - false(默认):不自动创建,需通过 API Key 管理页/接口显式创建
|
||||
# 也可在系统设置中以 tenant.auto_create_api_key 实时调整,无需重启。
|
||||
# WEKNORA_TENANT_AUTO_CREATE_API_KEY=false
|
||||
|
||||
# APK 镜像源设置(可选)
|
||||
APK_MIRROR_ARG=mirrors.tencent.com
|
||||
|
||||
|
||||
@@ -42,6 +42,11 @@ type Tenant struct {
|
||||
StorageQuota int64 `yaml:"storage_quota" json:"storage_quota" gorm:"default:10737418240"`
|
||||
// Storage used (Bytes)
|
||||
StorageUsed int64 `yaml:"storage_used" json:"storage_used" gorm:"default:0"`
|
||||
// APIKey is only populated by CreateTenant when the server has
|
||||
// tenant.auto_create_api_key (env WEKNORA_TENANT_AUTO_CREATE_API_KEY)
|
||||
// enabled: it carries the plaintext token of an auto-created full_access
|
||||
// key. Empty otherwise. Save it on receipt — it is never returned again.
|
||||
APIKey string `yaml:"api_key,omitempty" json:"api_key,omitempty"`
|
||||
// Creation timestamp
|
||||
CreatedAt time.Time `yaml:"created_at" json:"created_at"`
|
||||
// Last update timestamp
|
||||
|
||||
@@ -190,8 +190,11 @@ services:
|
||||
# - WEKNORA_TENANT_ENABLE_RBAC: 是否启用租户角色强制鉴权(true / false),默认 true
|
||||
# - WEKNORA_TENANT_MAX_OWNED_PER_USER: 单个非超管自助创建租户的上限
|
||||
# >0 强制限额;=0 走 handler 默认;<0 关闭限额(不建议共享部署使用)
|
||||
# - WEKNORA_TENANT_AUTO_CREATE_API_KEY: 创建租户时自动生成 full_access API Key 并在响应中返回 token
|
||||
# (兼容旧版默认下发 API Key 行为,破坏性变更回退开关;true / false,默认 false)
|
||||
- WEKNORA_TENANT_ENABLE_RBAC=${WEKNORA_TENANT_ENABLE_RBAC:-}
|
||||
- WEKNORA_TENANT_MAX_OWNED_PER_USER=${WEKNORA_TENANT_MAX_OWNED_PER_USER:-}
|
||||
- WEKNORA_TENANT_AUTO_CREATE_API_KEY=${WEKNORA_TENANT_AUTO_CREATE_API_KEY:-}
|
||||
- WEKNORA_ASYNQ_CONCURRENCY=${WEKNORA_ASYNQ_CONCURRENCY:-32}
|
||||
- WEKNORA_WIKI_ASYNQ_CONCURRENCY=${WEKNORA_WIKI_ASYNQ_CONCURRENCY:-16}
|
||||
- WEKNORA_MODEL_MAX_CONCURRENCY=${WEKNORA_MODEL_MAX_CONCURRENCY:-32}
|
||||
|
||||
+25
-3
@@ -110,7 +110,9 @@ curl --location 'http://localhost:8080/api/v1/tenants/search?keyword=weknora&pag
|
||||
|
||||
## POST `/tenants` - 创建新租户
|
||||
|
||||
创建一个新的租户。**不会**自动发放 API Key;请在创建后通过 `POST /tenants/:id/api-keys` 创建密钥。从旧版本升级时,原有 `tenants.api_key` 会迁移到 `tenant_api_keys` 表并继续可用,直至被吊销。
|
||||
创建一个新的租户。**默认不会**自动发放 API Key;请在创建后通过 `POST /tenants/:id/api-keys` 创建密钥。从旧版本升级时,原有 `tenants.api_key` 会迁移到 `tenant_api_keys` 表并继续可用,直至被吊销。
|
||||
|
||||
> **兼容旧行为(可选)**:如需恢复旧版「创建租户即下发默认 API Key」的行为,可将系统设置 `tenant.auto_create_api_key` 置为 `true`(或设置环境变量 `WEKNORA_TENANT_AUTO_CREATE_API_KEY=true`)。开启后,创建租户会自动生成一个 `full_access` 权限的 API Key,并在响应体 `data.api_key` 中返回其明文 token(仅本次创建响应返回,请妥善保存)。默认 `false`。
|
||||
|
||||
**参数说明(请求体)**:
|
||||
|
||||
@@ -146,7 +148,7 @@ curl --location 'http://localhost:8080/api/v1/tenants' \
|
||||
}'
|
||||
```
|
||||
|
||||
**响应**:
|
||||
**响应**(默认,不含 API Key):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -154,7 +156,6 @@ curl --location 'http://localhost:8080/api/v1/tenants' \
|
||||
"id": 10000,
|
||||
"name": "weknora",
|
||||
"description": "weknora tenants",
|
||||
"api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG",
|
||||
"status": "active",
|
||||
"retriever_engines": {
|
||||
"engines": [
|
||||
@@ -179,6 +180,27 @@ curl --location 'http://localhost:8080/api/v1/tenants' \
|
||||
}
|
||||
```
|
||||
|
||||
当开启 `tenant.auto_create_api_key`(或 `WEKNORA_TENANT_AUTO_CREATE_API_KEY=true`)时,响应的 `data` 中会额外包含 `api_key` 字段(`full_access` 密钥的明文 token):
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 10000,
|
||||
"name": "weknora",
|
||||
"description": "weknora tenants",
|
||||
"api_key": "sk-aaLRAgvCRJcmtiL2vLMeB1FB5UV0Q-qB7DlTE1pJ9KA93XZG",
|
||||
"status": "active",
|
||||
"business": "wechat",
|
||||
"storage_quota": 10737418240,
|
||||
"storage_used": 0,
|
||||
"created_at": "2025-08-11T20:37:28.396980093+08:00",
|
||||
"updated_at": "2025-08-11T20:37:28.396980301+08:00",
|
||||
"deleted_at": null
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
## GET `/tenants/:id` - 获取指定租户信息
|
||||
|
||||
获取指定 ID 的租户详情。只能访问自己所属租户;访问其他租户需要跨租户权限,否则返回 403。
|
||||
|
||||
+64
-2
@@ -3956,6 +3956,50 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/knowledge-bases/{id}/duplicate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "创建一个只包含设置的新知识库副本,不复制知识、FAQ 内容、分块、索引、Wiki 页面、分享或置顶状态",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"知识库"
|
||||
],
|
||||
"summary": "创建知识库副本",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "源知识库 ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "创建后的知识库副本",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/knowledge-bases/{id}/faq/entries": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -11494,7 +11538,7 @@ const docTemplate = `{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "创建新的租户。任意已登录用户均可调用以建立自己的新工作区,\n调用方会被自动设为该租户的 Owner。跨租户超管仍可像以前一样\n通过本接口创建任意租户。",
|
||||
"description": "创建新的租户。任意已登录用户均可调用以建立自己的新工作区,\n调用方会被自动设为该租户的 Owner。跨租户超管仍可像以前一样\n通过本接口创建任意租户。\n当 tenant.auto_create_api_key(或 WEKNORA_TENANT_AUTO_CREATE_API_KEY)\n开启时,会自动创建一个 full_access API Key,并在响应体的 data.api_key 字段返回其明文 token。",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -11518,7 +11562,7 @@ const docTemplate = `{
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "创建的租户",
|
||||
"description": "创建的租户(可选含 api_key)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
@@ -16198,6 +16242,10 @@ const docTemplate = `{
|
||||
"interface_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"max_concurrency": {
|
||||
"description": "MaxConcurrency caps concurrent in-flight BACKGROUND (ingestion /\nenrichment) calls to THIS specific model, keyed by model ID and shared\nacross all replicas. 0 (the default) means \"fall back to the\nprocess-wide model.max_concurrency\". Interactive user-facing calls are\nnever gated. Only chat / vlm / embedding honour this (see limiter.Gate).",
|
||||
"type": "integer"
|
||||
},
|
||||
"parameter_size": {
|
||||
"description": "Ollama model parameter size (e.g., \"7B\", \"13B\", \"70B\")",
|
||||
"type": "string"
|
||||
@@ -20348,9 +20396,23 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mentioned_items": {
|
||||
"description": "Optional scoped tag mentions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/internal_handler_session.MentionedItemRequest"
|
||||
}
|
||||
},
|
||||
"query": {
|
||||
"description": "Query text to search for",
|
||||
"type": "string"
|
||||
},
|
||||
"tag_ids": {
|
||||
"description": "Tag IDs for filtering within a single KB",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+64
-2
@@ -3949,6 +3949,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/knowledge-bases/{id}/duplicate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "创建一个只包含设置的新知识库副本,不复制知识、FAQ 内容、分块、索引、Wiki 页面、分享或置顶状态",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"知识库"
|
||||
],
|
||||
"summary": "创建知识库副本",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "源知识库 ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "创建后的知识库副本",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/knowledge-bases/{id}/faq/entries": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -11487,7 +11531,7 @@
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "创建新的租户。任意已登录用户均可调用以建立自己的新工作区,\n调用方会被自动设为该租户的 Owner。跨租户超管仍可像以前一样\n通过本接口创建任意租户。",
|
||||
"description": "创建新的租户。任意已登录用户均可调用以建立自己的新工作区,\n调用方会被自动设为该租户的 Owner。跨租户超管仍可像以前一样\n通过本接口创建任意租户。\n当 tenant.auto_create_api_key(或 WEKNORA_TENANT_AUTO_CREATE_API_KEY)\n开启时,会自动创建一个 full_access API Key,并在响应体的 data.api_key 字段返回其明文 token。",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -11511,7 +11555,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "创建的租户",
|
||||
"description": "创建的租户(可选含 api_key)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
@@ -16191,6 +16235,10 @@
|
||||
"interface_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"max_concurrency": {
|
||||
"description": "MaxConcurrency caps concurrent in-flight BACKGROUND (ingestion /\nenrichment) calls to THIS specific model, keyed by model ID and shared\nacross all replicas. 0 (the default) means \"fall back to the\nprocess-wide model.max_concurrency\". Interactive user-facing calls are\nnever gated. Only chat / vlm / embedding honour this (see limiter.Gate).",
|
||||
"type": "integer"
|
||||
},
|
||||
"parameter_size": {
|
||||
"description": "Ollama model parameter size (e.g., \"7B\", \"13B\", \"70B\")",
|
||||
"type": "string"
|
||||
@@ -20341,9 +20389,23 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mentioned_items": {
|
||||
"description": "Optional scoped tag mentions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/internal_handler_session.MentionedItemRequest"
|
||||
}
|
||||
},
|
||||
"query": {
|
||||
"description": "Query text to search for",
|
||||
"type": "string"
|
||||
},
|
||||
"tag_ids": {
|
||||
"description": "Tag IDs for filtering within a single KB",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+49
-1
@@ -2061,6 +2061,14 @@ definitions:
|
||||
type: object
|
||||
interface_type:
|
||||
type: string
|
||||
max_concurrency:
|
||||
description: |-
|
||||
MaxConcurrency caps concurrent in-flight BACKGROUND (ingestion /
|
||||
enrichment) calls to THIS specific model, keyed by model ID and shared
|
||||
across all replicas. 0 (the default) means "fall back to the
|
||||
process-wide model.max_concurrency". Interactive user-facing calls are
|
||||
never gated. Only chat / vlm / embedding honour this (see limiter.Gate).
|
||||
type: integer
|
||||
parameter_size:
|
||||
description: Ollama model parameter size (e.g., "7B", "13B", "70B")
|
||||
type: string
|
||||
@@ -5123,9 +5131,19 @@ definitions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
mentioned_items:
|
||||
description: Optional scoped tag mentions
|
||||
items:
|
||||
$ref: '#/definitions/internal_handler_session.MentionedItemRequest'
|
||||
type: array
|
||||
query:
|
||||
description: Query text to search for
|
||||
type: string
|
||||
tag_ids:
|
||||
description: Tag IDs for filtering within a single KB
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
@@ -7609,6 +7627,34 @@ paths:
|
||||
summary: 更新知识库
|
||||
tags:
|
||||
- 知识库
|
||||
/knowledge-bases/{id}/duplicate:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建一个只包含设置的新知识库副本,不复制知识、FAQ 内容、分块、索引、Wiki 页面、分享或置顶状态
|
||||
parameters:
|
||||
- description: 源知识库 ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: 创建后的知识库副本
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: 请求参数错误
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: 创建知识库副本
|
||||
tags:
|
||||
- 知识库
|
||||
/knowledge-bases/{id}/faq/entries:
|
||||
delete:
|
||||
consumes:
|
||||
@@ -12490,6 +12536,8 @@ paths:
|
||||
创建新的租户。任意已登录用户均可调用以建立自己的新工作区,
|
||||
调用方会被自动设为该租户的 Owner。跨租户超管仍可像以前一样
|
||||
通过本接口创建任意租户。
|
||||
当 tenant.auto_create_api_key(或 WEKNORA_TENANT_AUTO_CREATE_API_KEY)
|
||||
开启时,会自动创建一个 full_access API Key,并在响应体的 data.api_key 字段返回其明文 token。
|
||||
parameters:
|
||||
- description: 租户信息
|
||||
in: body
|
||||
@@ -12501,7 +12549,7 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: 创建的租户
|
||||
description: 创建的租户(可选含 api_key)
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
|
||||
@@ -166,6 +166,23 @@ var registry = map[string]settingSpec{
|
||||
"仅在创建时读取,修改后只对之后新建的租户生效,不会回写已存在的租户。" +
|
||||
"0 或负数表示使用内置默认值 10GB。",
|
||||
},
|
||||
// tenant.auto_create_api_key restores the legacy behaviour where creating
|
||||
// a tenant also minted a full-access API key and returned its plaintext
|
||||
// token in the create response. Newer versions stopped doing this (keys
|
||||
// are created explicitly via tenant_api_keys), which is a breaking change
|
||||
// for integrations that relied on the create response carrying a key.
|
||||
// Deployments that need the old behaviour set this to true (or the
|
||||
// WEKNORA_TENANT_AUTO_CREATE_API_KEY env var). Default false keeps the
|
||||
// current, safer no-implicit-key behaviour. Read at create time only.
|
||||
"tenant.auto_create_api_key": {
|
||||
Type: "bool",
|
||||
EnvName: "WEKNORA_TENANT_AUTO_CREATE_API_KEY",
|
||||
Default: false,
|
||||
Category: "tenant",
|
||||
Description: "创建租户时是否自动生成一个全量权限(full_access)的 API Key,并在创建接口的响应中返回其明文 token。" +
|
||||
"用于兼容旧版本「创建租户即下发默认 API Key」的行为(属于破坏性变更的回退开关)。" +
|
||||
"每次创建租户时实时读取,修改后立即生效。默认 false(不自动创建,需通过 API Key 管理显式创建)。",
|
||||
},
|
||||
// asynq.concurrency is the asynq worker pool size (parallel in-flight
|
||||
// tasks). Read once when the asynq server starts — changing it in the
|
||||
// UI requires a process restart to take effect. Mirrors
|
||||
|
||||
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -202,11 +203,13 @@ func (h *TenantHandler) resolveMaxOwnedTenantsPerUser(ctx context.Context) int {
|
||||
// @Description 创建新的租户。任意已登录用户均可调用以建立自己的新工作区,
|
||||
// @Description 调用方会被自动设为该租户的 Owner。跨租户超管仍可像以前一样
|
||||
// @Description 通过本接口创建任意租户。
|
||||
// @Description 当 tenant.auto_create_api_key(或 WEKNORA_TENANT_AUTO_CREATE_API_KEY)
|
||||
// @Description 开启时,会自动创建一个 full_access API Key,并在响应体的 data.api_key 字段返回其明文 token。
|
||||
// @Tags 租户管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body handler.createTenantRequest true "租户信息"
|
||||
// @Success 201 {object} map[string]interface{} "创建的租户"
|
||||
// @Success 201 {object} map[string]interface{} "创建的租户(可选含 api_key)"
|
||||
// @Failure 400 {object} errors.AppError "请求参数错误"
|
||||
// @Security Bearer
|
||||
// @Router /tenants [post]
|
||||
@@ -405,12 +408,79 @@ func (h *TenantHandler) CreateTenant(c *gin.Context) {
|
||||
createdTenant.ID,
|
||||
secutils.SanitizeForLog(createdTenant.Name),
|
||||
)
|
||||
|
||||
// data carries the created tenant. When the legacy auto-create-key
|
||||
// behaviour is enabled we embed the plaintext token as data.api_key so
|
||||
// the response shape mirrors the pre-break-change behaviour integrations
|
||||
// relied on.
|
||||
var data any = createdTenant
|
||||
|
||||
// Optional legacy compatibility: mint a full-access API key on tenant
|
||||
// creation and return its plaintext token, gated by the
|
||||
// tenant.auto_create_api_key setting (env WEKNORA_TENANT_AUTO_CREATE_API_KEY).
|
||||
// Default off — modern deployments create keys explicitly via
|
||||
// tenant_api_keys. Failing to create the convenience key must NOT fail
|
||||
// the whole tenant creation (the tenant is fully usable without a key);
|
||||
// we log a warning and return the tenant as usual.
|
||||
if h.autoCreateTenantAPIKey(ctx) && h.apiKeyService != nil {
|
||||
result, keyErr := h.apiKeyService.CreateAPIKey(ctx, interfaces.TenantAPIKeyCreateRequest{
|
||||
TenantID: createdTenant.ID,
|
||||
Name: "default",
|
||||
FullAccess: true,
|
||||
})
|
||||
if keyErr != nil {
|
||||
logger.Errorf(ctx,
|
||||
"Auto-create default API key failed for tenant %d: %v — returning tenant without key",
|
||||
createdTenant.ID, keyErr)
|
||||
} else if merged, mErr := tenantWithAPIKey(createdTenant, result.Token); mErr != nil {
|
||||
// Round-trip failure is unexpected; degrade gracefully by
|
||||
// returning the tenant without embedding the key rather than
|
||||
// failing the whole request.
|
||||
logger.Errorf(ctx, "Failed to embed api_key into tenant response for tenant %d: %v",
|
||||
createdTenant.ID, mErr)
|
||||
} else {
|
||||
data = merged
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"success": true,
|
||||
"data": createdTenant,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
// tenantWithAPIKey returns the tenant serialized as a map with an extra
|
||||
// api_key field, so the create response can embed the plaintext token inside
|
||||
// data (mirroring the pre-break-change shape) without adding a persisted
|
||||
// api_key column back onto types.Tenant.
|
||||
func tenantWithAPIKey(tenant *types.Tenant, token string) (map[string]any, error) {
|
||||
raw, err := json.Marshal(tenant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := map[string]any{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m["api_key"] = token
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// autoCreateTenantAPIKey resolves whether tenant creation should also mint a
|
||||
// full-access API key (legacy compatibility). 3-tier resolver:
|
||||
// system_settings DB row > WEKNORA_TENANT_AUTO_CREATE_API_KEY env > false.
|
||||
func (h *TenantHandler) autoCreateTenantAPIKey(ctx context.Context) bool {
|
||||
if h.systemSettingSvc == nil {
|
||||
return false
|
||||
}
|
||||
return h.systemSettingSvc.GetBool(
|
||||
ctx,
|
||||
"tenant.auto_create_api_key",
|
||||
"WEKNORA_TENANT_AUTO_CREATE_API_KEY",
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// GetTenant godoc
|
||||
// @Summary 获取租户详情
|
||||
// @Description 根据ID获取租户详情
|
||||
|
||||
Reference in New Issue
Block a user