From 18acc823c3976bbcdfda9ac0bb546f9869e8fb23 Mon Sep 17 00:00:00 2001 From: ChickenPige0n <2336983354@qq.com> Date: Mon, 13 Jul 2026 23:48:58 +0800 Subject: [PATCH] feat(admin-web): add Feishu Application Connection admin page ADR-0021 pins the organization<->Feishu application binding to 1:1 and the backend already exposes GET /api/org/:orgSlug/feishu-application-connection PUT /api/org/:orgSlug/feishu-application-connection (rotate/create) DELETE /api/org/:orgSlug/feishu-application-connection (disable) backed by FeishuApplicationConnectionService with versioned envelopes (ADR-0024). The org-admin SPA had no surface for it, so the only connection type the spec requires was unmanageable from the admin UI. Add a Feishu page that reads the current connection (status, redacted app fingerprint, active version, updatedAt), rotates credentials with appId/appSecret/botOpenId (+ optional verificationToken/encryptKey) as the backend requires, and disables with confirmation. Add the matching api client (feishuApplication / rotateFeishuApplication / disableFeishuApplication), a feishu nav icon and a nav entry. --- hub/admin-web/src/lib/api.ts | 29 ++- hub/admin-web/src/lib/components/Icon.svelte | 14 +- hub/admin-web/src/routes/+layout.svelte | 3 +- .../admin/org/[slug]/feishu/+page.svelte | 181 ++++++++++++++++++ 4 files changed, 223 insertions(+), 4 deletions(-) create mode 100644 hub/admin-web/src/routes/admin/org/[slug]/feishu/+page.svelte diff --git a/hub/admin-web/src/lib/api.ts b/hub/admin-web/src/lib/api.ts index c70e863..1888089 100644 --- a/hub/admin-web/src/lib/api.ts +++ b/hub/admin-web/src/lib/api.ts @@ -156,6 +156,16 @@ export interface ProviderConnectionRow { updatedAt: string; } +export interface FeishuApplicationConnection { + id: string; + appFingerprint: string; + status: 'DRAFT' | 'ACTIVE' | 'DISABLED'; + activeVersion: number | null; + keyId: string | null; + createdAt: string; + updatedAt: string; +} + export interface UsageTotals { runCount: number; runsWithCost: number; @@ -259,5 +269,22 @@ export const api = { put( `${orgBase(slug)}/provider-connections/${encodeURIComponent(providerId)}`, body, - ) as Promise + ) as Promise, + + feishuApplication: (slug: string) => + get(`${orgBase(slug)}/feishu-application-connection`) as Promise<{ + connection: FeishuApplicationConnection | null; + }>, + rotateFeishuApplication: ( + slug: string, + body: { + appId: string; + appSecret: string; + botOpenId: string; + verificationToken?: string; + encryptKey?: string; + }, + ) => put(`${orgBase(slug)}/feishu-application-connection`, body) as Promise, + disableFeishuApplication: (slug: string) => + del(`${orgBase(slug)}/feishu-application-connection`) as Promise }; diff --git a/hub/admin-web/src/lib/components/Icon.svelte b/hub/admin-web/src/lib/components/Icon.svelte index a9a6ac3..63c23db 100644 --- a/hub/admin-web/src/lib/components/Icon.svelte +++ b/hub/admin-web/src/lib/components/Icon.svelte @@ -9,8 +9,9 @@ | 'members' | 'teams' | 'projects' - | 'provider' - | 'menu' + | 'provider' + | 'feishu' + | 'menu' | 'logout' | 'org' | 'chevron' @@ -54,6 +55,15 @@ +{:else if name === 'feishu'} + + + + {:else if name === 'menu'} diff --git a/hub/admin-web/src/routes/+layout.svelte b/hub/admin-web/src/routes/+layout.svelte index 7f1b5f4..22f711b 100644 --- a/hub/admin-web/src/routes/+layout.svelte +++ b/hub/admin-web/src/routes/+layout.svelte @@ -23,7 +23,8 @@ { key: 'members', label: '成员', icon: 'members' as const }, { key: 'teams', label: '团队', icon: 'teams' as const }, { key: 'projects', label: '项目', icon: 'projects' as const }, - { key: 'provider', label: '供应方', icon: 'provider' as const } + { key: 'provider', label: '供应方', icon: 'provider' as const }, + { key: 'feishu', label: '飞书', icon: 'feishu' as const } ]; function isAdmin(org: OrgMembership): boolean { diff --git a/hub/admin-web/src/routes/admin/org/[slug]/feishu/+page.svelte b/hub/admin-web/src/routes/admin/org/[slug]/feishu/+page.svelte new file mode 100644 index 0000000..8d60f46 --- /dev/null +++ b/hub/admin-web/src/routes/admin/org/[slug]/feishu/+page.svelte @@ -0,0 +1,181 @@ + + + + +{#if loading} + +{:else if error} + +{:else} + {#if connection} +
+

当前连接

+
+
+
状态
+
{connection.status}
+
+
+
App 指纹
+
{connection.appFingerprint}
+
+
+
版本
+
{connection.activeVersion ?? '—'}
+
+
+
更新于
+
{fmtDate(connection.updatedAt)}
+
+
+ {#if connection.status !== 'DISABLED'} +
+ +
+ {/if} +
+ {:else} +
+

本组织尚未绑定飞书应用。填写下方凭据以创建连接。

+
+ {/if} + +
+

{connection ? '轮换凭据' : '创建连接'}

+

+ 密钥仅写入新版本,旧版本归档。{#if connection}App ID 不可变更,须与现有应用一致。{/if} +

+
+
+ App ID + +
+
+ App Secret + +
+
+ Bot Open ID + +
+
+ Verification Token(可选) + +
+
+ Encrypt Key(可选) + +
+
+
+
+ + +
+
+{/if}