feat(access): add guest chat ACL (#235)

This commit is contained in:
BBQ
2026-03-14 17:15:41 +08:00
committed by GitHub
parent c8728ffc2c
commit 839e63acda
86 changed files with 6886 additions and 2554 deletions
+60
View File
@@ -559,6 +559,7 @@
"skills": "Skills",
"email": "Email",
"files": "Files",
"access": "Access",
"terminal": "Terminal",
"settings": "Settings"
},
@@ -767,6 +768,65 @@
"deleteBotDescription": "Deleting this bot cannot be undone. Proceed with caution.",
"deleteBot": "Delete Bot"
},
"access": {
"title": "Access Control",
"subtitle": "Guest chat trigger access is controlled by allow guest, whitelist, and blacklist together.",
"allowGuestDescription": "When enabled, guests may trigger chat by default. Blacklist rules still take precedence.",
"saveGuestAccess": "Save Guest Access",
"guestAccessSaved": "Guest access updated",
"guestRulesTitle": "Rule Summary",
"guestRulesDescription": "Owners and members always bypass these rules. The rules below only apply to guest chat triggers.",
"whitelistTitle": "Whitelist",
"whitelistDescription": "Whitelisted guests can still trigger chat even when guest access is not open.",
"blacklistTitle": "Blacklist",
"blacklistDescription": "Blacklisted guests are denied chat trigger even when guest access is open.",
"userSelector": "Select User",
"identitySelector": "Select Platform Identity",
"selectUser": "Search and select a user",
"selectIdentity": "Search and select a platform identity",
"searchUser": "Search users",
"searchIdentity": "Search platform identities",
"noUserCandidates": "No user candidates",
"noIdentityCandidates": "No platform identity candidates",
"userId": "User ID",
"userIdPlaceholder": "Enter user ID",
"channelIdentityId": "Channel Identity ID",
"channelIdentityIdPlaceholder": "Enter channel identity ID",
"addWhitelist": "Add to Whitelist",
"addBlacklist": "Add to Blacklist",
"clearSelection": "Clear Selection",
"whitelistEmpty": "No whitelist rules yet",
"blacklistEmpty": "No blacklist rules yet",
"validation": "Fill exactly one subject: user_id or channel_identity_id",
"validationConversationRequiresChannel": "Select a source platform before restricting by conversation or thread ID",
"validationThreadRequiresConversation": "Thread ID requires a conversation ID",
"whitelistSaved": "Whitelist updated",
"blacklistSaved": "Blacklist updated",
"saveFailed": "Failed to save access rule",
"deleteSuccess": "Rule deleted",
"deleteFailed": "Failed to delete rule",
"sourceScopeTitle": "Source Scope",
"sourceScopeDescription": "Optionally restrict a rule to a platform, conversation type, or specific conversation/thread.",
"sourceChannel": "Source Platform",
"anyChannel": "Any platform",
"conversationType": "Conversation Type",
"anyConversationType": "Any conversation type",
"privateConversationType": "Private",
"groupConversationType": "Group",
"threadConversationType": "Thread",
"observedConversation": "Observed Conversation",
"selectObservedConversation": "Select an observed conversation",
"searchObservedConversation": "Search observed conversations",
"noObservedConversations": "No observed conversations",
"selectIdentityFirst": "Select a platform identity first to load observed conversations",
"observedConversationHint": "Choosing an observed conversation auto-fills the platform, conversation ID, and thread ID.",
"conversationId": "Conversation ID",
"conversationIdPlaceholder": "Enter conversation ID",
"threadId": "Thread ID",
"threadIdPlaceholder": "Enter thread ID",
"clearScope": "Clear Scope",
"lastObserved": "Last seen"
},
"channels": {
"title": "Platforms",
"addChannel": "Add Platform",
+60
View File
@@ -555,6 +555,7 @@
"skills": "技能",
"email": "邮件",
"files": "文件",
"access": "访问控制",
"terminal": "终端",
"settings": "设置"
},
@@ -763,6 +764,65 @@
"deleteBotDescription": "删除此 Bot 后无法恢复,请谨慎操作。",
"deleteBot": "删除 Bot"
},
"access": {
"title": "访问控制",
"subtitle": "游客聊天触发权限由 allow guest、白名单和黑名单共同决定。",
"allowGuestDescription": "开启后,游客默认可触发聊天;黑名单仍然会优先阻止。",
"saveGuestAccess": "保存游客访问设置",
"guestAccessSaved": "游客访问设置已保存",
"guestRulesTitle": "规则说明",
"guestRulesDescription": "Owner 和成员始终可用;这里的规则只作用于游客的聊天触发。",
"whitelistTitle": "白名单",
"whitelistDescription": "当未开放游客时,白名单中的游客仍可触发聊天。",
"blacklistTitle": "黑名单",
"blacklistDescription": "即使已开放游客,黑名单中的游客也会被拒绝触发聊天。",
"userSelector": "选择用户",
"identitySelector": "选择平台身份",
"selectUser": "搜索并选择用户",
"selectIdentity": "搜索并选择平台身份",
"searchUser": "搜索用户",
"searchIdentity": "搜索平台身份",
"noUserCandidates": "暂无可选用户",
"noIdentityCandidates": "暂无可选平台身份",
"userId": "用户 ID",
"userIdPlaceholder": "输入用户 ID",
"channelIdentityId": "Channel Identity ID",
"channelIdentityIdPlaceholder": "输入 channel identity ID",
"addWhitelist": "添加到白名单",
"addBlacklist": "添加到黑名单",
"clearSelection": "清空选择",
"whitelistEmpty": "暂无白名单规则",
"blacklistEmpty": "暂无黑名单规则",
"validation": "请只填写一个主体:user_id 或 channel_identity_id",
"validationConversationRequiresChannel": "按会话或线程限制时,请先选择来源平台",
"validationThreadRequiresConversation": "填写线程 ID 前必须先填写会话 ID",
"whitelistSaved": "白名单已更新",
"blacklistSaved": "黑名单已更新",
"saveFailed": "保存访问规则失败",
"deleteSuccess": "规则已删除",
"deleteFailed": "删除规则失败",
"sourceScopeTitle": "来源范围",
"sourceScopeDescription": "可选地将规则限制到某个平台、会话类型或指定会话/线程。",
"sourceChannel": "来源平台",
"anyChannel": "任意平台",
"conversationType": "会话类型",
"anyConversationType": "任意会话类型",
"privateConversationType": "私聊",
"groupConversationType": "群聊",
"threadConversationType": "线程",
"observedConversation": "历史会话",
"selectObservedConversation": "选择一个历史会话",
"searchObservedConversation": "搜索历史会话",
"noObservedConversations": "暂无历史会话",
"selectIdentityFirst": "请先选择平台身份以加载历史会话",
"observedConversationHint": "选择历史会话后会自动填充平台、会话 ID 和线程 ID。",
"conversationId": "会话 ID",
"conversationIdPlaceholder": "输入会话 ID",
"threadId": "线程 ID",
"threadIdPlaceholder": "输入线程 ID",
"clearScope": "清空范围",
"lastObserved": "最近出现"
},
"channels": {
"title": "平台",
"addChannel": "添加平台",
File diff suppressed because it is too large Load Diff
@@ -258,18 +258,6 @@
</div>
</template>
<!-- Allow Guest: only for public bot -->
<template v-if="isPublicBot">
<div class="flex items-center justify-between">
<Label>{{ $t('bots.settings.allowGuest') }}</Label>
<Switch
:model-value="form.allow_guest"
@update:model-value="(val) => form.allow_guest = !!val"
/>
</div>
<Separator />
</template>
<!-- Save -->
<div class="flex justify-end">
<Button
@@ -352,8 +340,6 @@ const props = defineProps<{
botType?: string
}>()
const isPublicBot = computed(() => props.botType === 'public')
const { t } = useI18n()
const router = useRouter()
@@ -567,7 +553,6 @@ const form = reactive({
max_context_load_time: 0,
max_context_tokens: 0,
language: '',
allow_guest: false,
reasoning_enabled: false,
reasoning_effort: 'medium',
})
@@ -582,7 +567,6 @@ watch(settings, (val) => {
form.max_context_load_time = val.max_context_load_time ?? 0
form.max_context_tokens = val.max_context_tokens ?? 0
form.language = val.language ?? ''
form.allow_guest = val.allow_guest ?? false
form.reasoning_enabled = val.reasoning_enabled ?? false
form.reasoning_effort = val.reasoning_effort || 'medium'
}
@@ -602,9 +586,6 @@ const hasChanges = computed(() => {
|| form.language !== (s.language ?? '')
|| form.reasoning_enabled !== (s.reasoning_enabled ?? false)
|| form.reasoning_effort !== (s.reasoning_effort || 'medium')
if (isPublicBot.value) {
changed = changed || form.allow_guest !== (s.allow_guest ?? false)
}
return changed
})
+2
View File
@@ -250,6 +250,7 @@ import BotSchedule from './components/bot-schedule.vue'
import BotContainer from './components/bot-container.vue'
import BotTerminal from './components/bot-terminal.vue'
import BotFiles from './components/bot-files.vue'
import BotAccess from './components/bot-access.vue'
import { resolveApiErrorMessage } from '@/utils/api-error'
import { useAvatarInitials } from '@/composables/useAvatarInitials'
import { useSyncedQueryParam } from '@/composables/useSyncedQueryParam'
@@ -289,6 +290,7 @@ const tabList = computed(() => {
{ value: 'schedule', label: 'bots.tabs.schedule', component: BotSchedule, params: { 'bot-id': bot_id } },
{ value: 'history', label: 'bots.tabs.history', component: BotHistory, params: { 'bot-id': bot_id } },
{ value: 'skills', label: 'bots.tabs.skills', component: BotSkills, params: { 'bot-id': bot_id } },
{ value: 'access', label: 'bots.tabs.access', component: BotAccess, params: { 'bot-id': bot_id, 'bot-type': bot.value?.type } },
{ value: 'settings', label: 'bots.tabs.settings', component: BotSettings, params: { 'bot-id': bot_id, 'bot-type': bot.value?.type } }
]
})