diff --git a/desktop/src/api/adapters.ts b/desktop/src/api/adapters.ts index 8855c48d..8bac289b 100644 --- a/desktop/src/api/adapters.ts +++ b/desktop/src/api/adapters.ts @@ -41,6 +41,10 @@ export const adaptersApi = { return api.post('/api/adapters/wechat/unbind', {}) }, + unbindDingtalk() { + return api.post('/api/adapters/dingtalk/unbind', {}) + }, + beginDingtalkRegistration() { return api.post('/api/adapters/dingtalk/registration/begin', {}) }, diff --git a/desktop/src/stores/adapterStore.test.ts b/desktop/src/stores/adapterStore.test.ts index 88cdf057..73ab270c 100644 --- a/desktop/src/stores/adapterStore.test.ts +++ b/desktop/src/stores/adapterStore.test.ts @@ -7,6 +7,7 @@ describe('adapterStore IM pairing behavior', () => { startWechatLogin: vi.fn(), pollWechatLogin: vi.fn(), unbindWechat: vi.fn(), + unbindDingtalk: vi.fn(), beginDingtalkRegistration: vi.fn(), pollDingtalkRegistration: vi.fn(), } @@ -59,4 +60,17 @@ describe('adapterStore IM pairing behavior', () => { expect(adaptersApi.unbindWechat).toHaveBeenCalledTimes(1) expect(useAdapterStore.getState().config).toBe(nextConfig) }) + + it('unbinds the DingTalk bot through the explicit bot action', async () => { + const nextConfig = { dingtalk: { pairedUsers: [], allowedUsers: [] } } + adaptersApi.unbindDingtalk.mockResolvedValue(nextConfig) + + const { useAdapterStore } = await import('./adapterStore') + + await useAdapterStore.getState().unbindDingtalkBot() + + expect(adaptersApi.updateConfig).not.toHaveBeenCalled() + expect(adaptersApi.unbindDingtalk).toHaveBeenCalledTimes(1) + expect(useAdapterStore.getState().config).toBe(nextConfig) + }) }) diff --git a/desktop/src/stores/adapterStore.ts b/desktop/src/stores/adapterStore.ts index 47ff82b5..69dbd799 100644 --- a/desktop/src/stores/adapterStore.ts +++ b/desktop/src/stores/adapterStore.ts @@ -132,13 +132,9 @@ export const useAdapterStore = create((set, get) => ({ }, unbindDingtalkBot: async () => { - await get().updateConfig({ - dingtalk: { - clientId: undefined, - clientSecret: undefined, - pairedUsers: [], - }, - }) + const config = await adaptersApi.unbindDingtalk() + set({ config }) + void notifyTauriRestartAdapters() }, removePairedUser: async (platform, userId) => { diff --git a/src/server/__tests__/adapters.test.ts b/src/server/__tests__/adapters.test.ts index 7229a7bb..fd3c7d11 100644 --- a/src/server/__tests__/adapters.test.ts +++ b/src/server/__tests__/adapters.test.ts @@ -125,4 +125,27 @@ describe('Adapters API', () => { expect(json.wechat.allowedUsers).toEqual([]) expect(json.wechat.pairedUsers).toEqual([]) }) + + it('clears DingTalk credentials on unbind', async () => { + const put = makeRequest('PUT', '/api/adapters', { + dingtalk: { + clientId: 'ding-client-1', + clientSecret: 'dingtalk-client-secret', + allowedUsers: ['ding-allowed-user'], + permissionCardTemplateId: 'permission-template', + pairedUsers: [{ userId: 'ding-user', displayName: 'DingTalk User', pairedAt: 1 }], + }, + }) + await handleAdaptersApi(put.req, put.url, put.segments) + + const unbind = makeRequest('POST', '/api/adapters/dingtalk/unbind') + const res = await handleAdaptersApi(unbind.req, unbind.url, unbind.segments) + expect(res.status).toBe(200) + const json = await res.json() as any + expect(json.dingtalk.clientId).toBeUndefined() + expect(json.dingtalk.clientSecret).toBeUndefined() + expect(json.dingtalk.allowedUsers).toEqual([]) + expect(json.dingtalk.permissionCardTemplateId).toBeUndefined() + expect(json.dingtalk.pairedUsers).toEqual([]) + }) }) diff --git a/src/server/api/adapters.ts b/src/server/api/adapters.ts index 5e3a6096..c4e67cae 100644 --- a/src/server/api/adapters.ts +++ b/src/server/api/adapters.ts @@ -143,6 +143,18 @@ export async function handleAdaptersApi( if (tail[0] === 'wechat') { return handleWechatAdaptersApi(req, tail.slice(1)) } + if (tail[0] === 'dingtalk' && req.method === 'POST' && tail[1] === 'unbind') { + await adapterService.updateConfig({ + dingtalk: { + clientId: undefined, + clientSecret: undefined, + allowedUsers: [], + pairedUsers: [], + permissionCardTemplateId: undefined, + }, + }) + return Response.json(await adapterService.getConfig()) + } if (tail[0] === 'dingtalk' && tail[1] === 'registration') { if (req.method === 'POST' && tail[2] === 'begin') { return Response.json(await beginDingtalkRegistration())