fix: align feishu webhook verification flow with sdk behavior (#250)

This commit is contained in:
Ringo.Typowriter
2026-03-15 19:39:13 +08:00
committed by GitHub
parent 020c74c2bc
commit ca598bb0a5
8 changed files with 341 additions and 131 deletions
+2
View File
@@ -847,6 +847,8 @@
"webhookCallback": "WebHook Callback URL",
"webhookCallbackHint": "Use this URL as the event subscription request URL in Feishu/Lark.",
"webhookCallbackPending": "Save this platform configuration to generate the callback URL.",
"feishuWebhookSecurityHint": "For security, webhook mode requires either an Encrypt Key or a Verification Token; an unprotected public callback URL should not be exposed.",
"feishuWebhookSecretRequired": "For security, configure at least one of Encrypt Key or Verification Token.",
"noAvailableTypes": "All platform types have been configured",
"types": {
"feishu": "Feishu",
+2
View File
@@ -843,6 +843,8 @@
"webhookCallback": "WebHook 回调地址",
"webhookCallbackHint": "将该地址配置到飞书/Lark 事件订阅的请求 URL。",
"webhookCallbackPending": "保存平台配置后会生成回调地址。",
"feishuWebhookSecurityHint": "出于安全考虑,Webhook 模式必须配置 Encrypt Key 或 Verification Token 之一;未受保护的回调地址不应直接暴露在公网上。",
"feishuWebhookSecretRequired": "出于安全考虑,请至少配置 Encrypt Key 或 Verification Token 之一。",
"noAvailableTypes": "所有平台类型均已配置",
"types": {
"feishu": "飞书",
@@ -88,6 +88,12 @@
<h4 class="text-sm font-medium">
{{ $t('bots.channels.credentials') }}
</h4>
<p
v-if="showWebhookCallback"
class="text-xs text-muted-foreground"
>
{{ $t('bots.channels.feishuWebhookSecurityHint') }}
</p>
<div
v-for="(field, key) in orderedFields"
@@ -364,6 +370,19 @@ function validateRequired(): boolean {
return true
}
function validateFeishuWebhookSecrets(): boolean {
if (props.channelItem.meta.type !== 'feishu' || currentInboundMode.value !== 'webhook') {
return true
}
const encryptKey = String(form.credentials.encryptKey ?? form.credentials.encrypt_key ?? '').trim()
const verificationToken = String(form.credentials.verificationToken ?? form.credentials.verification_token ?? '').trim()
if (encryptKey !== '' || verificationToken !== '') {
return true
}
toast.error(t('bots.channels.feishuWebhookSecretRequired'))
return false
}
function buildCredentials(): Record<string, unknown> {
const credentials: Record<string, unknown> = {}
for (const [key, val] of Object.entries(form.credentials)) {
@@ -376,6 +395,7 @@ function buildCredentials(): Record<string, unknown> {
async function saveChannel(disabled: boolean, nextAction: 'save' | 'toggle') {
if (!validateRequired()) return
if (!validateFeishuWebhookSecrets()) return
action.value = nextAction
try {
const result = await upsertChannel({
@@ -413,9 +433,10 @@ async function handleEditSave() {
}
async function handleToggleDisabled() {
action.value = 'toggle'
try {
const nextDisabled = !form.disabled
if (!nextDisabled && !validateFeishuWebhookSecrets()) return
action.value = 'toggle'
const result = await updateChannelStatus({
platform: props.channelItem.meta.type,
disabled: nextDisabled,