@lobehub/chat 1.37.0 → 1.37.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/en-US/common.json +2 -2
  4. package/package.json +1 -1
  5. package/src/services/file/_deprecated.test.ts +119 -0
  6. package/src/services/file/{pglite.ts → _deprecated.ts} +28 -32
  7. package/src/services/file/client.test.ts +153 -74
  8. package/src/services/file/client.ts +32 -28
  9. package/src/services/file/index.ts +2 -2
  10. package/src/services/import/_deprecated.ts +74 -0
  11. package/src/services/import/{pglite.test.ts → client.test.ts} +1 -1
  12. package/src/services/import/client.ts +21 -61
  13. package/src/services/import/index.ts +2 -2
  14. package/src/services/message/_deprecated.test.ts +398 -0
  15. package/src/services/message/_deprecated.ts +121 -0
  16. package/src/services/message/client.test.ts +191 -159
  17. package/src/services/message/client.ts +47 -50
  18. package/src/services/message/index.ts +2 -2
  19. package/src/services/plugin/_deprecated.test.ts +162 -0
  20. package/src/services/plugin/_deprecated.ts +42 -0
  21. package/src/services/plugin/client.test.ts +68 -55
  22. package/src/services/plugin/client.ts +20 -11
  23. package/src/services/plugin/index.ts +2 -2
  24. package/src/services/session/_deprecated.test.ts +440 -0
  25. package/src/services/session/_deprecated.ts +183 -0
  26. package/src/services/session/client.test.ts +212 -241
  27. package/src/services/session/client.ts +61 -60
  28. package/src/services/session/index.ts +2 -2
  29. package/src/services/topic/{client.test.ts → _deprecated.test.ts} +1 -1
  30. package/src/services/topic/_deprecated.ts +70 -0
  31. package/src/services/topic/client.ts +40 -25
  32. package/src/services/topic/index.ts +2 -2
  33. package/src/services/topic/pglite.test.ts +1 -1
  34. package/src/services/user/{pglite.test.ts → _deprecated.test.ts} +32 -29
  35. package/src/services/user/_deprecated.ts +57 -0
  36. package/src/services/user/client.test.ts +28 -31
  37. package/src/services/user/client.ts +51 -16
  38. package/src/services/user/index.ts +2 -2
  39. package/src/store/chat/slices/builtinTool/action.test.ts +1 -1
  40. package/src/store/user/slices/common/action.test.ts +1 -1
  41. package/src/services/file/pglite.test.ts +0 -198
  42. package/src/services/import/pglite.ts +0 -34
  43. package/src/services/message/pglite.test.ts +0 -430
  44. package/src/services/message/pglite.ts +0 -118
  45. package/src/services/plugin/pglite.test.ts +0 -175
  46. package/src/services/plugin/pglite.ts +0 -51
  47. package/src/services/session/pglite.test.ts +0 -411
  48. package/src/services/session/pglite.ts +0 -184
  49. package/src/services/topic/pglite.ts +0 -85
  50. package/src/services/user/pglite.ts +0 -92
@@ -1,10 +1,11 @@
1
1
  import dayjs from 'dayjs';
2
2
 
3
- import { FileModel } from '@/database/_deprecated/models/file';
4
- import { MessageModel } from '@/database/_deprecated/models/message';
5
- import { DB_Message } from '@/database/_deprecated/schemas/message';
3
+ import { INBOX_SESSION_ID } from '@/const/session';
4
+ import { clientDB } from '@/database/client/db';
5
+ import { MessageItem } from '@/database/schemas';
6
+ import { MessageModel } from '@/database/server/models/message';
7
+ import { BaseClientService } from '@/services/baseClientService';
6
8
  import {
7
- ChatFileItem,
8
9
  ChatMessage,
9
10
  ChatMessageError,
10
11
  ChatTTS,
@@ -14,108 +15,104 @@ import {
14
15
 
15
16
  import { IMessageService } from './type';
16
17
 
17
- export class ClientService implements IMessageService {
18
- async createMessage(data: CreateMessageParams) {
19
- const { id } = await MessageModel.create(data);
18
+ export class ClientService extends BaseClientService implements IMessageService {
19
+ private get messageModel(): MessageModel {
20
+ return new MessageModel(clientDB as any, this.userId);
21
+ }
22
+
23
+ async createMessage({ sessionId, ...params }: CreateMessageParams) {
24
+ const { id } = await this.messageModel.create({
25
+ ...params,
26
+ sessionId: this.toDbSessionId(sessionId) as string,
27
+ });
20
28
 
21
29
  return id;
22
30
  }
23
31
 
24
- // @ts-ignore
25
- async batchCreateMessages(messages: ChatMessage[]) {
26
- return MessageModel.batchCreate(messages);
32
+ async batchCreateMessages(messages: MessageItem[]) {
33
+ return this.messageModel.batchCreate(messages);
27
34
  }
28
35
 
29
- async getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]> {
30
- const messages = await MessageModel.query({ sessionId, topicId });
31
-
32
- const fileList = (await Promise.all(
33
- messages
34
- .flatMap((item) => item.files)
35
- .filter(Boolean)
36
- .map(async (id) => FileModel.findById(id!)),
37
- )) as ChatFileItem[];
36
+ async getMessages(sessionId: string, topicId?: string) {
37
+ const data = await this.messageModel.query({
38
+ sessionId: this.toDbSessionId(sessionId),
39
+ topicId,
40
+ });
38
41
 
39
- return messages.map((item) => ({
40
- ...item,
41
- imageList: fileList
42
- .filter((file) => item.files?.includes(file.id) && file.fileType.startsWith('image'))
43
- .map((file) => ({
44
- alt: file.name,
45
- id: file.id,
46
- url: file.url,
47
- })),
48
- }));
42
+ return data as unknown as ChatMessage[];
49
43
  }
50
44
 
51
45
  async getAllMessages() {
52
- return MessageModel.queryAll();
46
+ const data = await this.messageModel.queryAll();
47
+
48
+ return data as unknown as ChatMessage[];
53
49
  }
54
50
 
55
51
  async countMessages() {
56
- return MessageModel.count();
52
+ return this.messageModel.count();
57
53
  }
58
54
 
59
55
  async countTodayMessages() {
60
- const topics = await MessageModel.queryAll();
56
+ const topics = await this.messageModel.queryAll();
61
57
  return topics.filter(
62
58
  (item) => dayjs(item.createdAt).format('YYYY-MM-DD') === dayjs().format('YYYY-MM-DD'),
63
59
  ).length;
64
60
  }
65
61
 
66
62
  async getAllMessagesInSession(sessionId: string) {
67
- return MessageModel.queryBySessionId(sessionId);
63
+ const data = this.messageModel.queryBySessionId(this.toDbSessionId(sessionId));
64
+
65
+ return data as unknown as ChatMessage[];
68
66
  }
69
67
 
70
68
  async updateMessageError(id: string, error: ChatMessageError) {
71
- return MessageModel.update(id, { error });
69
+ return this.messageModel.update(id, { error });
72
70
  }
73
71
 
74
- // @ts-ignore
75
- async updateMessage(id: string, message: Partial<DB_Message>) {
76
- return MessageModel.update(id, message);
72
+ async updateMessage(id: string, message: Partial<MessageItem>) {
73
+ return this.messageModel.update(id, message);
77
74
  }
78
75
 
79
76
  async updateMessageTTS(id: string, tts: Partial<ChatTTS> | false) {
80
- return MessageModel.update(id, { tts });
77
+ return this.messageModel.updateTTS(id, tts as any);
81
78
  }
82
79
 
83
80
  async updateMessageTranslate(id: string, translate: Partial<ChatTranslate> | false) {
84
- return MessageModel.update(id, { translate });
81
+ return this.messageModel.updateTranslate(id, translate as any);
85
82
  }
86
83
 
87
84
  async updateMessagePluginState(id: string, value: Record<string, any>) {
88
- return MessageModel.updatePluginState(id, value);
85
+ return this.messageModel.updatePluginState(id, value);
89
86
  }
90
87
 
91
88
  async updateMessagePluginArguments(id: string, value: string | Record<string, any>) {
92
89
  const args = typeof value === 'string' ? value : JSON.stringify(value);
93
90
 
94
- return MessageModel.updatePlugin(id, { arguments: args });
95
- }
96
-
97
- async bindMessagesToTopic(topicId: string, messageIds: string[]) {
98
- return MessageModel.batchUpdate(messageIds, { topicId });
91
+ return this.messageModel.updateMessagePlugin(id, { arguments: args });
99
92
  }
100
93
 
101
94
  async removeMessage(id: string) {
102
- return MessageModel.delete(id);
95
+ return this.messageModel.deleteMessage(id);
103
96
  }
104
97
 
105
98
  async removeMessages(ids: string[]) {
106
- return MessageModel.bulkDelete(ids);
99
+ return this.messageModel.deleteMessages(ids);
107
100
  }
108
101
 
109
- async removeMessagesByAssistant(assistantId: string, topicId?: string) {
110
- return MessageModel.batchDelete(assistantId, topicId);
102
+ async removeMessagesByAssistant(sessionId: string, topicId?: string) {
103
+ return this.messageModel.deleteMessagesBySession(this.toDbSessionId(sessionId), topicId);
111
104
  }
112
105
 
113
106
  async removeAllMessages() {
114
- return MessageModel.clearTable();
107
+ return this.messageModel.deleteAllMessages();
115
108
  }
116
109
 
117
110
  async hasMessages() {
118
111
  const number = await this.countMessages();
119
112
  return number > 0;
120
113
  }
114
+
115
+ private toDbSessionId(sessionId: string | undefined) {
116
+ return sessionId === INBOX_SESSION_ID ? undefined : sessionId;
117
+ }
121
118
  }
@@ -1,5 +1,5 @@
1
- import { ClientService as DeprecatedService } from './client';
2
- import { ClientService } from './pglite';
1
+ import { ClientService as DeprecatedService } from './_deprecated';
2
+ import { ClientService } from './client';
3
3
  import { ServerService } from './server';
4
4
 
5
5
  const clientService =
@@ -0,0 +1,162 @@
1
+ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { PluginModel } from '@/database/_deprecated/models/plugin';
5
+ import { DB_Plugin } from '@/database/_deprecated/schemas/plugin';
6
+ import { LobeTool } from '@/types/tool';
7
+ import { LobeToolCustomPlugin } from '@/types/tool/plugin';
8
+
9
+ import { ClientService } from './_deprecated';
10
+ import { InstallPluginParams } from './type';
11
+
12
+ const pluginService = new ClientService();
13
+
14
+ // Mocking modules and functions
15
+
16
+ vi.mock('@/database/_deprecated/models/plugin', () => ({
17
+ PluginModel: {
18
+ getList: vi.fn(),
19
+ create: vi.fn(),
20
+ delete: vi.fn(),
21
+ update: vi.fn(),
22
+ clear: vi.fn(),
23
+ },
24
+ }));
25
+
26
+ beforeEach(() => {
27
+ vi.resetAllMocks();
28
+ });
29
+
30
+ describe('PluginService', () => {
31
+ describe('installPlugin', () => {
32
+ it('should install a plugin', async () => {
33
+ // Arrange
34
+ const fakePlugin = {
35
+ identifier: 'test-plugin',
36
+ manifest: { name: 'TestPlugin', version: '1.0.0' } as unknown as LobeChatPluginManifest,
37
+ type: 'plugin',
38
+ } as InstallPluginParams;
39
+ vi.mocked(PluginModel.create).mockResolvedValue(fakePlugin);
40
+
41
+ // Act
42
+ const installedPlugin = await pluginService.installPlugin(fakePlugin);
43
+
44
+ // Assert
45
+ expect(PluginModel.create).toHaveBeenCalledWith(fakePlugin);
46
+ expect(installedPlugin).toEqual(fakePlugin);
47
+ });
48
+ });
49
+
50
+ describe('getInstalledPlugins', () => {
51
+ it('should return a list of installed plugins', async () => {
52
+ // Arrange
53
+ const fakePlugins = [{ identifier: 'test-plugin', type: 'plugin' }] as LobeTool[];
54
+ vi.mocked(PluginModel.getList).mockResolvedValue(fakePlugins as DB_Plugin[]);
55
+
56
+ // Act
57
+ const installedPlugins = await pluginService.getInstalledPlugins();
58
+
59
+ // Assert
60
+ expect(PluginModel.getList).toHaveBeenCalled();
61
+ expect(installedPlugins).toEqual(fakePlugins);
62
+ });
63
+ });
64
+
65
+ describe('uninstallPlugin', () => {
66
+ it('should uninstall a plugin', async () => {
67
+ // Arrange
68
+ const identifier = 'test-plugin';
69
+ vi.mocked(PluginModel.delete).mockResolvedValue();
70
+
71
+ // Act
72
+ const result = await pluginService.uninstallPlugin(identifier);
73
+
74
+ // Assert
75
+ expect(PluginModel.delete).toHaveBeenCalledWith(identifier);
76
+ expect(result).toBe(undefined);
77
+ });
78
+ });
79
+
80
+ describe('createCustomPlugin', () => {
81
+ it('should create a custom plugin', async () => {
82
+ // Arrange
83
+ const customPlugin = {
84
+ identifier: 'custom-plugin',
85
+ manifest: {},
86
+ type: 'customPlugin',
87
+ } as LobeToolCustomPlugin;
88
+ vi.mocked(PluginModel.create).mockResolvedValue(customPlugin);
89
+
90
+ // Act
91
+ const result = await pluginService.createCustomPlugin(customPlugin);
92
+
93
+ // Assert
94
+ expect(PluginModel.create).toHaveBeenCalledWith({
95
+ ...customPlugin,
96
+ type: 'customPlugin',
97
+ });
98
+ expect(result).toEqual(customPlugin);
99
+ });
100
+ });
101
+
102
+ describe('updatePlugin', () => {
103
+ it('should update a plugin', async () => {
104
+ // Arrange
105
+ const id = 'plugin-id';
106
+ const value = { settings: { ab: '1' } } as unknown as LobeToolCustomPlugin;
107
+ vi.mocked(PluginModel.update).mockResolvedValue(1);
108
+
109
+ // Act
110
+ const result = await pluginService.updatePlugin(id, value);
111
+
112
+ // Assert
113
+ expect(PluginModel.update).toHaveBeenCalledWith(id, value);
114
+ expect(result).toEqual(undefined);
115
+ });
116
+ });
117
+
118
+ describe('updatePluginManifest', () => {
119
+ it('should update a plugin manifest', async () => {
120
+ // Arrange
121
+ const id = 'plugin-id';
122
+ const manifest = { name: 'NewPluginManifest' } as unknown as LobeChatPluginManifest;
123
+ vi.mocked(PluginModel.update).mockResolvedValue(1);
124
+
125
+ // Act
126
+ const result = await pluginService.updatePluginManifest(id, manifest);
127
+
128
+ // Assert
129
+ expect(PluginModel.update).toHaveBeenCalledWith(id, { manifest });
130
+ expect(result).toEqual(undefined);
131
+ });
132
+ });
133
+
134
+ describe('removeAllPlugins', () => {
135
+ it('should remove all plugins', async () => {
136
+ // Arrange
137
+ vi.mocked(PluginModel.clear).mockResolvedValue(undefined);
138
+
139
+ // Act
140
+ const result = await pluginService.removeAllPlugins();
141
+
142
+ // Assert
143
+ expect(PluginModel.clear).toHaveBeenCalled();
144
+ expect(result).toBe(undefined);
145
+ });
146
+ });
147
+
148
+ describe('updatePluginSettings', () => {
149
+ it('should update plugin settings', async () => {
150
+ // Arrange
151
+ const id = 'plugin-id';
152
+ const settings = { color: 'blue' };
153
+
154
+ // Act
155
+ const result = await pluginService.updatePluginSettings(id, settings);
156
+
157
+ // Assert
158
+ expect(PluginModel.update).toHaveBeenCalledWith(id, { settings });
159
+ expect(result).toEqual(undefined);
160
+ });
161
+ });
162
+ });
@@ -0,0 +1,42 @@
1
+ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
+
3
+ import { PluginModel } from '@/database/_deprecated/models/plugin';
4
+ import { LobeTool } from '@/types/tool';
5
+ import { LobeToolCustomPlugin } from '@/types/tool/plugin';
6
+
7
+ import { IPluginService, InstallPluginParams } from './type';
8
+
9
+ export class ClientService implements IPluginService {
10
+ installPlugin = async (plugin: InstallPluginParams) => {
11
+ return PluginModel.create(plugin);
12
+ };
13
+
14
+ getInstalledPlugins = () => {
15
+ return PluginModel.getList() as Promise<LobeTool[]>;
16
+ };
17
+
18
+ uninstallPlugin(identifier: string) {
19
+ return PluginModel.delete(identifier);
20
+ }
21
+
22
+ async createCustomPlugin(customPlugin: LobeToolCustomPlugin) {
23
+ return PluginModel.create({ ...customPlugin, type: 'customPlugin' });
24
+ }
25
+
26
+ async updatePlugin(id: string, value: LobeToolCustomPlugin) {
27
+ await PluginModel.update(id, value);
28
+ return;
29
+ }
30
+ async updatePluginManifest(id: string, manifest: LobeChatPluginManifest) {
31
+ await PluginModel.update(id, { manifest });
32
+ }
33
+
34
+ async removeAllPlugins() {
35
+ return PluginModel.clear();
36
+ }
37
+
38
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
39
+ async updatePluginSettings(id: string, settings: any, _?: AbortSignal) {
40
+ await PluginModel.update(id, { settings });
41
+ }
42
+ }
@@ -1,30 +1,29 @@
1
1
  import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { eq } from 'drizzle-orm';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { PluginModel } from '@/database/_deprecated/models/plugin';
5
- import { DB_Plugin } from '@/database/_deprecated/schemas/plugin';
5
+ import { clientDB, initializeDB } from '@/database/client/db';
6
+ import { installedPlugins, users } from '@/database/schemas';
6
7
  import { LobeTool } from '@/types/tool';
7
8
  import { LobeToolCustomPlugin } from '@/types/tool/plugin';
8
9
 
9
10
  import { ClientService } from './client';
10
11
  import { InstallPluginParams } from './type';
11
12
 
12
- const pluginService = new ClientService();
13
-
14
13
  // Mocking modules and functions
15
14
 
16
- vi.mock('@/database/_deprecated/models/plugin', () => ({
17
- PluginModel: {
18
- getList: vi.fn(),
19
- create: vi.fn(),
20
- delete: vi.fn(),
21
- update: vi.fn(),
22
- clear: vi.fn(),
23
- },
24
- }));
25
-
26
- beforeEach(() => {
27
- vi.resetAllMocks();
15
+ const userId = 'message-db';
16
+ const pluginService = new ClientService(userId);
17
+
18
+ // Mock data
19
+ beforeEach(async () => {
20
+ await initializeDB();
21
+
22
+ // 在每个测试用例之前,重置表数据
23
+ await clientDB.transaction(async (trx) => {
24
+ await trx.delete(users);
25
+ await trx.insert(users).values([{ id: userId }, { id: '456' }]);
26
+ });
28
27
  });
29
28
 
30
29
  describe('PluginService', () => {
@@ -32,18 +31,19 @@ describe('PluginService', () => {
32
31
  it('should install a plugin', async () => {
33
32
  // Arrange
34
33
  const fakePlugin = {
35
- identifier: 'test-plugin',
34
+ identifier: 'test-plugin-d',
36
35
  manifest: { name: 'TestPlugin', version: '1.0.0' } as unknown as LobeChatPluginManifest,
37
36
  type: 'plugin',
38
37
  } as InstallPluginParams;
39
- vi.mocked(PluginModel.create).mockResolvedValue(fakePlugin);
40
38
 
41
39
  // Act
42
- const installedPlugin = await pluginService.installPlugin(fakePlugin);
40
+ await pluginService.installPlugin(fakePlugin);
43
41
 
44
42
  // Assert
45
- expect(PluginModel.create).toHaveBeenCalledWith(fakePlugin);
46
- expect(installedPlugin).toEqual(fakePlugin);
43
+ const result = await clientDB.query.installedPlugins.findFirst({
44
+ where: eq(installedPlugins.identifier, fakePlugin.identifier),
45
+ });
46
+ expect(result).toMatchObject(fakePlugin);
47
47
  });
48
48
  });
49
49
 
@@ -51,14 +51,14 @@ describe('PluginService', () => {
51
51
  it('should return a list of installed plugins', async () => {
52
52
  // Arrange
53
53
  const fakePlugins = [{ identifier: 'test-plugin', type: 'plugin' }] as LobeTool[];
54
- vi.mocked(PluginModel.getList).mockResolvedValue(fakePlugins as DB_Plugin[]);
55
-
54
+ await clientDB
55
+ .insert(installedPlugins)
56
+ .values([{ identifier: 'test-plugin', type: 'plugin', userId }]);
56
57
  // Act
57
- const installedPlugins = await pluginService.getInstalledPlugins();
58
+ const data = await pluginService.getInstalledPlugins();
58
59
 
59
60
  // Assert
60
- expect(PluginModel.getList).toHaveBeenCalled();
61
- expect(installedPlugins).toEqual(fakePlugins);
61
+ expect(data).toMatchObject(fakePlugins);
62
62
  });
63
63
  });
64
64
 
@@ -66,13 +66,15 @@ describe('PluginService', () => {
66
66
  it('should uninstall a plugin', async () => {
67
67
  // Arrange
68
68
  const identifier = 'test-plugin';
69
- vi.mocked(PluginModel.delete).mockResolvedValue();
69
+ await clientDB.insert(installedPlugins).values([{ identifier, type: 'plugin', userId }]);
70
70
 
71
71
  // Act
72
- const result = await pluginService.uninstallPlugin(identifier);
72
+ await pluginService.uninstallPlugin(identifier);
73
73
 
74
74
  // Assert
75
- expect(PluginModel.delete).toHaveBeenCalledWith(identifier);
75
+ const result = await clientDB.query.installedPlugins.findFirst({
76
+ where: eq(installedPlugins.identifier, identifier),
77
+ });
76
78
  expect(result).toBe(undefined);
77
79
  });
78
80
  });
@@ -81,67 +83,74 @@ describe('PluginService', () => {
81
83
  it('should create a custom plugin', async () => {
82
84
  // Arrange
83
85
  const customPlugin = {
84
- identifier: 'custom-plugin',
86
+ identifier: 'custom-plugin-x',
85
87
  manifest: {},
86
88
  type: 'customPlugin',
87
89
  } as LobeToolCustomPlugin;
88
- vi.mocked(PluginModel.create).mockResolvedValue(customPlugin);
89
90
 
90
91
  // Act
91
- const result = await pluginService.createCustomPlugin(customPlugin);
92
+ await pluginService.createCustomPlugin(customPlugin);
92
93
 
93
94
  // Assert
94
- expect(PluginModel.create).toHaveBeenCalledWith({
95
- ...customPlugin,
96
- type: 'customPlugin',
95
+ const result = await clientDB.query.installedPlugins.findFirst({
96
+ where: eq(installedPlugins.identifier, customPlugin.identifier),
97
97
  });
98
- expect(result).toEqual(customPlugin);
98
+ expect(result).toMatchObject(customPlugin);
99
99
  });
100
100
  });
101
101
 
102
102
  describe('updatePlugin', () => {
103
103
  it('should update a plugin', async () => {
104
104
  // Arrange
105
- const id = 'plugin-id';
106
- const value = { settings: { ab: '1' } } as unknown as LobeToolCustomPlugin;
107
- vi.mocked(PluginModel.update).mockResolvedValue(1);
105
+ const identifier = 'plugin-id';
106
+ const value = { customParams: { ab: '1' } } as unknown as LobeToolCustomPlugin;
107
+ await clientDB.insert(installedPlugins).values([{ identifier, type: 'plugin', userId }]);
108
108
 
109
109
  // Act
110
- const result = await pluginService.updatePlugin(id, value);
110
+ await pluginService.updatePlugin(identifier, value);
111
111
 
112
112
  // Assert
113
- expect(PluginModel.update).toHaveBeenCalledWith(id, value);
114
- expect(result).toEqual(undefined);
113
+ const result = await clientDB.query.installedPlugins.findFirst({
114
+ where: eq(installedPlugins.identifier, identifier),
115
+ });
116
+ expect(result).toMatchObject(value);
115
117
  });
116
118
  });
117
119
 
118
120
  describe('updatePluginManifest', () => {
119
121
  it('should update a plugin manifest', async () => {
120
122
  // Arrange
121
- const id = 'plugin-id';
123
+ const identifier = 'plugin-id';
122
124
  const manifest = { name: 'NewPluginManifest' } as unknown as LobeChatPluginManifest;
123
- vi.mocked(PluginModel.update).mockResolvedValue(1);
125
+ await clientDB.insert(installedPlugins).values([{ identifier, type: 'plugin', userId }]);
124
126
 
125
127
  // Act
126
- const result = await pluginService.updatePluginManifest(id, manifest);
128
+ await pluginService.updatePluginManifest(identifier, manifest);
127
129
 
128
130
  // Assert
129
- expect(PluginModel.update).toHaveBeenCalledWith(id, { manifest });
130
- expect(result).toEqual(undefined);
131
+ const result = await clientDB.query.installedPlugins.findFirst({
132
+ where: eq(installedPlugins.identifier, identifier),
133
+ });
134
+ expect(result).toMatchObject({ manifest });
131
135
  });
132
136
  });
133
137
 
134
138
  describe('removeAllPlugins', () => {
135
139
  it('should remove all plugins', async () => {
136
140
  // Arrange
137
- vi.mocked(PluginModel.clear).mockResolvedValue(undefined);
141
+ await clientDB.insert(installedPlugins).values([
142
+ { identifier: '123', type: 'plugin', userId },
143
+ { identifier: '234', type: 'plugin', userId },
144
+ ]);
138
145
 
139
146
  // Act
140
- const result = await pluginService.removeAllPlugins();
147
+ await pluginService.removeAllPlugins();
141
148
 
142
149
  // Assert
143
- expect(PluginModel.clear).toHaveBeenCalled();
144
- expect(result).toBe(undefined);
150
+ const result = await clientDB.query.installedPlugins.findMany({
151
+ where: eq(installedPlugins.userId, userId),
152
+ });
153
+ expect(result.length).toEqual(0);
145
154
  });
146
155
  });
147
156
 
@@ -150,13 +159,17 @@ describe('PluginService', () => {
150
159
  // Arrange
151
160
  const id = 'plugin-id';
152
161
  const settings = { color: 'blue' };
162
+ await clientDB.insert(installedPlugins).values([{ identifier: id, type: 'plugin', userId }]);
153
163
 
154
164
  // Act
155
- const result = await pluginService.updatePluginSettings(id, settings);
165
+ await pluginService.updatePluginSettings(id, settings);
156
166
 
157
167
  // Assert
158
- expect(PluginModel.update).toHaveBeenCalledWith(id, { settings });
159
- expect(result).toEqual(undefined);
168
+ const result = await clientDB.query.installedPlugins.findFirst({
169
+ where: eq(installedPlugins.identifier, id),
170
+ });
171
+
172
+ expect(result).toMatchObject({ settings });
160
173
  });
161
174
  });
162
175
  });
@@ -1,42 +1,51 @@
1
1
  import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
2
 
3
- import { PluginModel } from '@/database/_deprecated/models/plugin';
3
+ import { clientDB } from '@/database/client/db';
4
+ import { PluginModel } from '@/database/server/models/plugin';
5
+ import { BaseClientService } from '@/services/baseClientService';
4
6
  import { LobeTool } from '@/types/tool';
5
7
  import { LobeToolCustomPlugin } from '@/types/tool/plugin';
6
8
 
7
9
  import { IPluginService, InstallPluginParams } from './type';
8
10
 
9
- export class ClientService implements IPluginService {
11
+ export class ClientService extends BaseClientService implements IPluginService {
12
+ private get pluginModel(): PluginModel {
13
+ return new PluginModel(clientDB as any, this.userId);
14
+ }
15
+
10
16
  installPlugin = async (plugin: InstallPluginParams) => {
11
- return PluginModel.create(plugin);
17
+ await this.pluginModel.create(plugin);
18
+ return;
12
19
  };
13
20
 
14
21
  getInstalledPlugins = () => {
15
- return PluginModel.getList() as Promise<LobeTool[]>;
22
+ return this.pluginModel.query() as Promise<LobeTool[]>;
16
23
  };
17
24
 
18
- uninstallPlugin(identifier: string) {
19
- return PluginModel.delete(identifier);
25
+ async uninstallPlugin(identifier: string) {
26
+ await this.pluginModel.delete(identifier);
27
+ return;
20
28
  }
21
29
 
22
30
  async createCustomPlugin(customPlugin: LobeToolCustomPlugin) {
23
- return PluginModel.create({ ...customPlugin, type: 'customPlugin' });
31
+ await this.pluginModel.create({ ...customPlugin, type: 'customPlugin' });
32
+ return;
24
33
  }
25
34
 
26
35
  async updatePlugin(id: string, value: LobeToolCustomPlugin) {
27
- await PluginModel.update(id, value);
36
+ await this.pluginModel.update(id, value);
28
37
  return;
29
38
  }
30
39
  async updatePluginManifest(id: string, manifest: LobeChatPluginManifest) {
31
- await PluginModel.update(id, { manifest });
40
+ await this.pluginModel.update(id, { manifest });
32
41
  }
33
42
 
34
43
  async removeAllPlugins() {
35
- return PluginModel.clear();
44
+ await this.pluginModel.deleteAll();
36
45
  }
37
46
 
38
47
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
39
48
  async updatePluginSettings(id: string, settings: any, _?: AbortSignal) {
40
- await PluginModel.update(id, { settings });
49
+ await this.pluginModel.update(id, { settings });
41
50
  }
42
51
  }