Skip to content

Commit 09503a5

Browse files
committed
feat: access restriction
1 parent 1e49f68 commit 09503a5

14 files changed

Lines changed: 447 additions & 73 deletions

File tree

‎ui/src/api/API_README.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,15 @@ Workspace 与 System 资源管理的 `application/application.ts` 提供 `getAcc
383383
概览使用 `access_token` 拼接 Web 链接,不使用智能体 ID 或 API Key 替代;API 文档地址使用 Chat 部署前缀。
384384
两范围的 `putAccessToken` 向同一路径提交 `{ access_token_reset: true }` 重置链接,或 `{ is_active: boolean }` 修改 Web 访问状态;
385385
载荷复用 `ApplicationAccessTokenPayload`,使用返回的访问凭证与启用状态回显,不二次查询。
386+
访问限制复用上述 GET/PUT,读取与保存 `access_num`、`white_active`、`white_list`、`authentication` 和
387+
`authentication_value`。提问次数为 0~10000000 的整数;社区版仅编辑认证开关,保留已有 `authentication_value`,
388+
不生成登录配置字段,身份认证读写待后台联调。Xpack 版认证配置使用 `type: 'login'`、`login_value`、
389+
`max_attempts`,保留原配置中的其他字段;已有认证 `type` 不自动转换,缺省为 `login`。
390+
保存访问限制携带原 `language`,避免后台省略字段时清空语言配置。
391+
默认登录方式与账号锁定暂按 `authentication_value.default_value / failed_attempts / lock_time` 提交,
392+
字段及认证配置生效能力待后台联调;前端不因认证字段缺失禁用编辑。System 资源管理的访问限制仍需部署环境联调。
393+
Xpack 版可用登录方式统一调用 `system/chat-management/chat-user-auth.ts` 的 `getChatUserAuthType`,
394+
GET `/chat_user/auth/types` 返回 `{ label, value }[]`,两种智能体入口共用。
386395

387396
展示设置共用 `ApplicationDisplaySetting`;`ApplicationAccessToken` 兼容可选展示字段,缺失字段由弹窗补齐默认值。
388397
两范围的 `putDisplaySetting` 沿用 V2 的 `/<applicationId>/setting` PUT 接口,提交 FormData:

‎ui/src/api/admin/system/chat-management/chat-user-auth.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { get, post, put } from '../../core/request'
2-
import type { AuthProviderSettingPayload, AuthProviderType } from '@/api/types'
2+
import type { AuthProviderSettingPayload, AuthProviderType, OptionItem } from '@/api/types'
33

44
const prefix = '/chat_user/auth'
55

6+
/** 获取对话用户可用的登录方式。 */
7+
const getChatUserAuthType = () => get<OptionItem<string>[]>(`${prefix}/types`)
8+
69
/** 获取对话用户指定认证源配置。 */
710
const getAuthSetting = (authType: AuthProviderType) => {
811
return get<Partial<AuthProviderSettingPayload>>(`${prefix}/${authType}/detail`)
@@ -17,4 +20,4 @@ const postAuthSettingConnection = (payload: AuthProviderSettingPayload) => {
1720
const putAuthSetting = (authType: AuthProviderType, payload: AuthProviderSettingPayload) => {
1821
return put<AuthProviderSettingPayload, boolean>(`${prefix}/${authType}/info`, payload)
1922
}
20-
export default { getAuthSetting, postAuthSettingConnection, putAuthSetting }
23+
export default { getChatUserAuthType, getAuthSetting, postAuthSettingConnection, putAuthSetting }

‎ui/src/api/admin/system/resource-management/application/application.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ const putApplicationPortal = (applicationId: string, isPortal: boolean) => Promi
3232
/** 获取 System 资源管理智能体详情。 */
3333
const getApplicationDetail = (applicationId: string) => get<ApplicationDetail>(`${prefix}/${applicationId}`)
3434

35-
/** 获取 System 资源管理智能体公开访问凭证。 */
35+
/** 获取 System 资源管理智能体公开访问凭证与访问限制。 */
36+
// TODO:访问限制接口待联调。
37+
// TODO:社区版身份认证读写待后台联调。
3638
const getAccessToken = (applicationId: string) => get<ApplicationAccessToken>(`${prefix}/${applicationId}/access_token`)
3739

38-
/** 修改 System 资源管理智能体公开访问状态或重置凭证,返回更新后的配置。 */
40+
/** 修改 System 资源管理智能体访问配置或重置凭证,返回更新后的配置。 */
41+
// TODO:身份认证、默认登录方式及账号锁定配置待后台联调。
3942
const putAccessToken = (applicationId: string, payload: ApplicationAccessTokenPayload) =>
4043
put<ApplicationAccessTokenPayload, ApplicationAccessToken>(`${prefix}/${applicationId}/access_token`, payload)
4144

‎ui/src/api/admin/workspace/application/application.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ const getApplicationDetail = (applicationId: string) => {
2525
return get<ApplicationDetail>(`${getPrefix()}/${applicationId}`)
2626
}
2727

28-
/** 获取智能体公开访问凭证。 */
28+
/** 获取智能体公开访问凭证与访问限制。 */
29+
// TODO:社区版身份认证读写待后台联调。
2930
const getAccessToken = (applicationId: string) => get<ApplicationAccessToken>(`${getPrefix()}/${applicationId}/access_token`)
3031

31-
/** 修改智能体公开访问状态或重置凭证,返回更新后的配置。 */
32+
/** 修改智能体访问配置或重置凭证,返回更新后的配置。 */
33+
// TODO:身份认证、默认登录方式及账号锁定配置待后台联调。
3234
const putAccessToken = (applicationId: string, payload: ApplicationAccessTokenPayload) =>
3335
put<ApplicationAccessTokenPayload, ApplicationAccessToken>(`${getPrefix()}/${applicationId}/access_token`, payload)
3436

‎ui/src/api/types/application.ts‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,39 @@ export interface ApplicationDisplaySetting {
4949
show_user_avatar: boolean
5050
}
5151

52-
/** 智能体公开访问链接使用的凭证。 */
52+
/** 智能体登录认证配置;保留后端返回的其他认证字段。 */
53+
export interface ApplicationAuthenticationConfig extends Dict<unknown> {
54+
type?: 'login' | 'password'
55+
login_value?: string[]
56+
max_attempts?: number
57+
// TODO:默认登录方式、账号锁定字段待后台联调。
58+
default_value?: string
59+
failed_attempts?: number
60+
lock_time?: number
61+
}
62+
63+
/** 智能体公开访问凭证与访问限制配置。 */
5364
export interface ApplicationAccessToken extends Partial<ApplicationDisplaySetting> {
5465
application_id: string
5566
access_token: string
5667
is_active: boolean
68+
access_num: number
69+
white_active: boolean
70+
white_list: string[]
71+
authentication?: boolean
72+
authentication_value?: ApplicationAuthenticationConfig | null
5773
}
5874

59-
/** 智能体公开访问状态修改或令牌重置参数。 */
75+
/** 智能体访问配置修改或令牌重置参数。 */
6076
export interface ApplicationAccessTokenPayload {
6177
is_active?: boolean
6278
access_token_reset?: boolean
79+
access_num?: number
80+
white_active?: boolean
81+
white_list?: string[]
82+
language?: string | null
83+
authentication?: boolean
84+
authentication_value?: ApplicationAuthenticationConfig
6385
}
6486

6587
export interface ApplicationDetail extends Omit<ApplicationFormPayload, 'desc'> {

‎ui/src/components/business/api-key-list/EditApiKeyDrawer.vue‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,9 @@ defineExpose({ open })
134134
<el-input :model-value="form.secret_key" disabled>
135135
<template #suffix>
136136
<!-- 复制 API Key -->
137-
<MkTooltip content="复制">
138-
<el-button link @click="copyText(form.secret_key)">
139-
<MkIcon name="icon_copy_outlined" />
140-
</el-button>
141-
</MkTooltip>
137+
<el-button text @click="copyText(form.secret_key)" class="-mr-1">
138+
<MkIcon name="icon_copy_outlined" />
139+
</el-button>
142140
</template>
143141
</el-input>
144142
</el-form-item>

‎ui/src/components/business/api-key-list/index.vue‎

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ const props = defineProps<{
1818
}>()
1919
const loading = ref(false)
2020
const apiKeys = ref<ApiKeyRecord[]>([])
21-
const revealedIds = ref(new Set<string>())
21+
const createdApiKey = ref('')
22+
const createdDialogVisible = ref(false)
2223
const pagination = ref({ currentPage: 1, pageSize: 20, total: 0 })
2324
const editDrawerRef = useTemplateRef<InstanceType<typeof EditApiKeyDrawer>>('editDrawerRef')
2425
const apiKeyRows = computed(() => apiKeys.value.map((record) => ({ ...record, expiration: getApiKeyExpiration(record) })))
2526
2627
function fetchApiKeys() {
27-
revealedIds.value.clear()
2828
const request =
2929
props.applicationId !== undefined
3030
? (props.api as typeof ApplicationApiKeyApi | typeof SystemResourceApiKeyApi).getApiKeyPage(props.applicationId, pagination.value)
@@ -51,8 +51,9 @@ function handleCreate() {
5151
? (props.api as typeof ApplicationApiKeyApi | typeof SystemResourceApiKeyApi).postApiKey(props.applicationId)
5252
: (props.api as typeof SystemApiKeyApi).postApiKey()
5353
return request
54-
.then(() => {
55-
MsgSuccess('创建成功')
54+
.then((record) => {
55+
createdApiKey.value = record.secret_key
56+
createdDialogVisible.value = true
5657
pagination.value.currentPage = 1
5758
return fetchApiKeys()
5859
})
@@ -108,11 +109,6 @@ function handleOpenEdit(record: ApiKeyRecord) {
108109
editDrawerRef.value?.open(record)
109110
}
110111
111-
function handleToggleVisible(id: string) {
112-
if (revealedIds.value.has(id)) revealedIds.value.delete(id)
113-
else revealedIds.value.add(id)
114-
}
115-
116112
onMounted(loadApiKeys)
117113
</script>
118114

@@ -130,24 +126,16 @@ onMounted(loadApiKeys)
130126
@current-change="loadApiKeys"
131127
@size-change="loadApiKeys"
132128
>
133-
<el-table-column label="API Key" min-width="230">
129+
<el-table-column label="API Key" min-width="160">
134130
<template #default="{ row }">
135131
<div class="flex-align-center gap-2">
136-
<span class="min-w-0 truncate" :title="revealedIds.has(row.id) ? row.secret_key : '********'">
137-
{{ revealedIds.has(row.id) ? row.secret_key : '********' }}
132+
<span>
133+
{{ `******${row.secret_key.slice(-4)}` }}
138134
</span>
139135
<!-- 复制完整 API Key -->
140-
<MkTooltip content="复制">
141-
<el-button link @click="copyText(row.secret_key)">
142-
<MkIcon name="icon_copy_outlined" />
143-
</el-button>
144-
</MkTooltip>
145-
<!-- 切换 API Key 显隐 -->
146-
<MkTooltip :content="revealedIds.has(row.id) ? '点击隐藏' : '点击显示'">
147-
<el-button link @click="handleToggleVisible(row.id)">
148-
<MkIcon :name="revealedIds.has(row.id) ? 'icon_visible_outlined' : 'icon_invisible_outlined'" />
149-
</el-button>
150-
</MkTooltip>
136+
<el-button text @click="copyText(row.secret_key)">
137+
<MkIcon name="icon_copy_outlined" />
138+
</el-button>
151139
</div>
152140
</template>
153141
</el-table-column>
@@ -202,5 +190,20 @@ onMounted(loadApiKeys)
202190
</el-table-column>
203191
</MkTable>
204192
</div>
193+
<MkDialog v-model="createdDialogVisible" title="API Key" align-center @closed="createdApiKey = ''">
194+
<template #subtitle>请安全保存此密钥,不要泄露。</template>
195+
<el-input :model-value="createdApiKey" readonly>
196+
<template #suffix>
197+
<!-- 复制新建的完整 API Key -->
198+
<el-button text @click="copyText(createdApiKey)" class="-mr-1">
199+
<MkIcon name="icon_copy_outlined" />
200+
</el-button>
201+
</template>
202+
</el-input>
203+
<template #footer>
204+
<!-- 关闭创建结果弹窗 -->
205+
<el-button plain @click="createdDialogVisible = false">知道了</el-button>
206+
</template>
207+
</MkDialog>
205208
<EditApiKeyDrawer v-bind="props" ref="editDrawerRef" @refresh="loadApiKeys" />
206209
</template>

‎ui/src/layout/avatar-dropdown/ApiKeyDialog.vue‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@ defineExpose({ open })
2424
<el-input :model-value="apiDocUrl" readonly>
2525
<template #suffix>
2626
<!-- 复制 API 文档地址 -->
27-
<MkTooltip content="复制">
28-
<el-button link @click="copyText(apiDocUrl)">
29-
<MkIcon name="icon_copy_outlined" />
30-
</el-button>
31-
</MkTooltip>
27+
<el-button text @click="copyText(apiDocUrl)" class="-mr-1">
28+
<MkIcon name="icon_copy_outlined" />
29+
</el-button>
3230
</template>
3331
</el-input>
3432
</div>

‎ui/src/views/VIEW_README.md‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,20 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排
344344
Workspace 与 System 资源管理共用智能体弹窗,分别使用各自接口,不回退或使用头像菜单的全局系统 API Key。
345345
嵌入第三方弹窗接收概览已有的 Web 链接与访问凭证,不额外请求接口;按全屏、浮窗、移动端单选切换并复制代码,
346346
关闭后恢复全屏模式。iframe 沿用 Web 链接,移动端增加 `mode=mobile`;浮窗使用 Chat API 前缀下的
347-
`/embed`,携带协议、主机和访问凭证,生成 HTML 时转义属性值。无访问凭证时禁用入口。
347+
`/embed`,携带协议、主机和访问凭证;若传入链接含查询参数则一并保留,生成 HTML 时转义属性值。无访问凭证时禁用入口。
348348
显示设置每次打开通过当前范围的 Application API 查询访问配置,支持主题色、语言、五类图片、
349349
头像与对话展示开关、浮窗位置及免责声明,本地草稿实时预览。恢复默认仅改草稿,保存成功关闭,
350350
失败保留输入;图片限 JPG/PNG/GIF、10 MB,替换、恢复默认、关闭及卸载时释放临时 URL。
351-
展示配置保存协议及后端支持情况见 API 文档。访问限制弹窗业务尚待接入,不模拟保存。
351+
展示配置保存协议及后端支持情况见 API 文档。
352+
访问限制位于本地 `access-restriction/`,入口按 `auth.isCE` 选择社区版 `AccessRestrictionDrawer.vue`,
353+
其他版本使用 `XpackAccessRestrictionDrawer.vue`;按需挂载,关闭动画后卸载。
354+
每次打开通过当前范围 Application API 查询配置;支持提问次数、登录认证及嵌入白名单,开关只修改草稿,保存时统一提交。
355+
社区版身份认证只有开关,不查询登录方式、不生成登录配置字段,保存时保留已有认证配置。
356+
Xpack 版可用登录方式查询 `/chat_user/auth/types`,开启认证时至少选一种;账号登录的验证码阈值沿用 -1~10。
357+
默认登录方式从已勾选的登录方式中选择,取消对应登录方式时同步调整默认值;账号锁定次数与分钟数正常编辑。
358+
前端开放认证配置,不因字段缺失或旧版密码模式禁用;保留已有认证类型,字段生效能力由后台联调确认。
359+
未取得完整配置时不提交保存,不提供失败空态或重试按钮;提交失败保留草稿。
360+
白名单逐行去空白,保存时保留原语言和其他认证字段。
352361
三类场景统一调用 `getMonitoring(workspaceId, range, applicationId)`;全部智能体使用空 ID,
353362
两类首页不提交智能体筛选,概览传当前智能体 ID。页面直接传入完整 API,不包装接口。
354363
排行由概览本地 `components/ApplicationRanking.vue` 调用 Overview API,展示用户 Tokens 消耗与提问次数两类 TOP5。

‎ui/src/views/application-detail/overview/access-restriction/AccessRestrictionDialog.vue‎

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)