@lobehub/chat 0.162.25 → 0.163.0

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 (84) hide show
  1. package/.github/workflows/release.yml +21 -2
  2. package/.github/workflows/sync.yml +1 -1
  3. package/.github/workflows/test.yml +35 -4
  4. package/CHANGELOG.md +25 -0
  5. package/LICENSE +38 -21
  6. package/codecov.yml +11 -0
  7. package/drizzle.config.ts +29 -0
  8. package/next.config.mjs +3 -0
  9. package/package.json +24 -4
  10. package/scripts/migrateServerDB/index.ts +30 -0
  11. package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +2 -1
  12. package/src/app/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx +95 -88
  13. package/src/app/(main)/chat/settings/features/HeaderContent.tsx +37 -31
  14. package/src/app/api/webhooks/clerk/__tests__/fixtures/createUser.json +73 -0
  15. package/src/app/api/webhooks/clerk/route.ts +159 -0
  16. package/src/app/api/webhooks/clerk/validateRequest.ts +22 -0
  17. package/src/app/trpc/edge/[trpc]/route.ts +1 -1
  18. package/src/app/trpc/lambda/[trpc]/route.ts +26 -0
  19. package/src/config/auth.ts +2 -0
  20. package/src/config/db.ts +13 -1
  21. package/src/database/server/core/db.ts +44 -0
  22. package/src/database/server/core/dbForTest.ts +45 -0
  23. package/src/database/server/index.ts +1 -0
  24. package/src/database/server/migrations/0000_init.sql +439 -0
  25. package/src/database/server/migrations/0001_add_client_id.sql +9 -0
  26. package/src/database/server/migrations/0002_amusing_puma.sql +9 -0
  27. package/src/database/server/migrations/meta/0000_snapshot.json +1583 -0
  28. package/src/database/server/migrations/meta/0001_snapshot.json +1636 -0
  29. package/src/database/server/migrations/meta/0002_snapshot.json +1630 -0
  30. package/src/database/server/migrations/meta/_journal.json +27 -0
  31. package/src/database/server/models/__tests__/file.test.ts +140 -0
  32. package/src/database/server/models/__tests__/message.test.ts +847 -0
  33. package/src/database/server/models/__tests__/plugin.test.ts +172 -0
  34. package/src/database/server/models/__tests__/session.test.ts +595 -0
  35. package/src/database/server/models/__tests__/topic.test.ts +623 -0
  36. package/src/database/server/models/__tests__/user.test.ts +173 -0
  37. package/src/database/server/models/_template.ts +44 -0
  38. package/src/database/server/models/file.ts +51 -0
  39. package/src/database/server/models/message.ts +378 -0
  40. package/src/database/server/models/plugin.ts +63 -0
  41. package/src/database/server/models/session.ts +290 -0
  42. package/src/database/server/models/sessionGroup.ts +69 -0
  43. package/src/database/server/models/topic.ts +265 -0
  44. package/src/database/server/models/user.ts +138 -0
  45. package/src/database/server/modules/DataImporter/__tests__/fixtures/messages.json +1101 -0
  46. package/src/database/server/modules/DataImporter/__tests__/index.test.ts +954 -0
  47. package/src/database/server/modules/DataImporter/index.ts +333 -0
  48. package/src/database/server/schemas/_id.ts +15 -0
  49. package/src/database/server/schemas/lobechat.ts +601 -0
  50. package/src/database/server/utils/idGenerator.test.ts +39 -0
  51. package/src/database/server/utils/idGenerator.ts +26 -0
  52. package/src/features/User/UserPanel/useMenu.tsx +43 -37
  53. package/src/libs/trpc/client.ts +52 -3
  54. package/src/server/files/s3.ts +21 -1
  55. package/src/server/keyVaultsEncrypt/index.test.ts +62 -0
  56. package/src/server/keyVaultsEncrypt/index.ts +93 -0
  57. package/src/server/mock.ts +1 -1
  58. package/src/server/routers/{index.ts → edge/index.ts} +3 -3
  59. package/src/server/routers/lambda/file.ts +49 -0
  60. package/src/server/routers/lambda/importer.ts +54 -0
  61. package/src/server/routers/lambda/index.ts +28 -0
  62. package/src/server/routers/lambda/message.ts +165 -0
  63. package/src/server/routers/lambda/plugin.ts +100 -0
  64. package/src/server/routers/lambda/session.ts +194 -0
  65. package/src/server/routers/lambda/sessionGroup.ts +77 -0
  66. package/src/server/routers/lambda/topic.ts +134 -0
  67. package/src/server/routers/lambda/user.ts +57 -0
  68. package/src/services/file/index.ts +4 -7
  69. package/src/services/file/server.ts +45 -0
  70. package/src/services/import/index.ts +4 -1
  71. package/src/services/import/server.ts +115 -0
  72. package/src/services/message/index.ts +4 -8
  73. package/src/services/message/server.ts +93 -0
  74. package/src/services/plugin/index.ts +4 -9
  75. package/src/services/plugin/server.ts +46 -0
  76. package/src/services/session/index.ts +4 -8
  77. package/src/services/session/server.ts +148 -0
  78. package/src/services/topic/index.ts +4 -9
  79. package/src/services/topic/server.ts +68 -0
  80. package/src/services/user/index.ts +4 -9
  81. package/src/services/user/server.ts +28 -0
  82. package/tests/setup-db.ts +7 -0
  83. package/vitest.config.ts +2 -1
  84. package/vitest.server.config.ts +23 -0
@@ -0,0 +1,115 @@
1
+ import { DefaultErrorShape } from '@trpc/server/unstable-core-do-not-import';
2
+
3
+ import { edgeClient, lambdaClient } from '@/libs/trpc/client';
4
+ import { useUserStore } from '@/store/user';
5
+ import { ImportStage, ImporterEntryData, OnImportCallbacks } from '@/types/importer';
6
+ import { UserSettings } from '@/types/user/settings';
7
+ import { uuid } from '@/utils/uuid';
8
+
9
+ export class ServerService {
10
+ importSettings = async (settings: UserSettings) => {
11
+ await useUserStore.getState().importAppSettings(settings);
12
+ };
13
+
14
+ importData = async (data: ImporterEntryData, callbacks?: OnImportCallbacks): Promise<void> => {
15
+ const handleError = (e: unknown) => {
16
+ callbacks?.onStageChange?.(ImportStage.Error);
17
+ const error = e as DefaultErrorShape;
18
+
19
+ callbacks?.onError?.({
20
+ code: error.data.code,
21
+ httpStatus: error.data.httpStatus,
22
+ message: error.message,
23
+ path: error.data.path,
24
+ });
25
+ };
26
+
27
+ const totalLength =
28
+ (data.messages?.length || 0) +
29
+ (data.sessionGroups?.length || 0) +
30
+ (data.sessions?.length || 0) +
31
+ (data.topics?.length || 0);
32
+
33
+ if (totalLength < 500) {
34
+ callbacks?.onStageChange?.(ImportStage.Importing);
35
+ const time = Date.now();
36
+ try {
37
+ const result = await lambdaClient.importer.importByPost.mutate({ data });
38
+ const duration = Date.now() - time;
39
+
40
+ callbacks?.onStageChange?.(ImportStage.Success);
41
+ callbacks?.onSuccess?.(result, duration);
42
+ } catch (e) {
43
+ handleError(e);
44
+ }
45
+
46
+ return;
47
+ }
48
+
49
+ // if the data is too large, upload it to S3 and upload by file
50
+ const filename = `${uuid()}.json`;
51
+
52
+ const pathname = `import_config/${filename}`;
53
+
54
+ const url = await edgeClient.upload.createS3PreSignedUrl.mutate({ pathname });
55
+
56
+ try {
57
+ callbacks?.onStageChange?.(ImportStage.Uploading);
58
+ await this.uploadWithProgress(url, data, callbacks?.onFileUploading);
59
+ } catch {
60
+ throw new Error('Upload Error');
61
+ }
62
+
63
+ callbacks?.onStageChange?.(ImportStage.Importing);
64
+ const time = Date.now();
65
+ try {
66
+ const result = await lambdaClient.importer.importByFile.mutate({ pathname });
67
+ const duration = Date.now() - time;
68
+ callbacks?.onStageChange?.(ImportStage.Success);
69
+ callbacks?.onSuccess?.(result, duration);
70
+ } catch (e) {
71
+ handleError(e);
72
+ }
73
+ };
74
+
75
+ private uploadWithProgress = async (
76
+ url: string,
77
+ data: object,
78
+ onProgress: OnImportCallbacks['onFileUploading'],
79
+ ) => {
80
+ const xhr = new XMLHttpRequest();
81
+
82
+ let startTime = Date.now();
83
+ xhr.upload.addEventListener('progress', (event) => {
84
+ if (event.lengthComputable) {
85
+ const progress = Number(((event.loaded / event.total) * 100).toFixed(1));
86
+
87
+ const speedInByte = event.loaded / ((Date.now() - startTime) / 1000);
88
+
89
+ onProgress?.({
90
+ // if the progress is 100, it means the file is uploaded
91
+ // but the server is still processing it
92
+ // so make it as 99.5 and let users think it's still uploading
93
+ progress: progress === 100 ? 99.5 : progress,
94
+ restTime: (event.total - event.loaded) / speedInByte,
95
+ speed: speedInByte / 1024,
96
+ });
97
+ }
98
+ });
99
+
100
+ xhr.open('PUT', url);
101
+ xhr.setRequestHeader('Content-Type', 'application/json');
102
+
103
+ return new Promise((resolve, reject) => {
104
+ xhr.addEventListener('load', () => {
105
+ if (xhr.status >= 200 && xhr.status < 300) {
106
+ resolve(xhr.response);
107
+ } else {
108
+ reject(xhr.statusText);
109
+ }
110
+ });
111
+ xhr.addEventListener('error', () => reject(xhr.statusText));
112
+ xhr.send(JSON.stringify(data));
113
+ });
114
+ };
115
+ }
@@ -1,12 +1,8 @@
1
- // import { getClientConfig } from '@/config/client';
2
- // import { ServerService } from './server';
3
- // import { ClientService } from './client';
4
- //
5
- // const { ENABLED_SERVER_SERVICE } = getClientConfig();
6
- //
7
- // export const messageService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
1
+ import { isServerMode } from '@/const/version';
2
+
8
3
  import { ClientService } from './client';
4
+ import { ServerService } from './server';
9
5
 
10
6
  export type { CreateMessageParams } from './type';
11
7
 
12
- export const messageService = new ClientService();
8
+ export const messageService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,93 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { INBOX_SESSION_ID } from '@/const/session';
3
+ import { lambdaClient } from '@/libs/trpc/client';
4
+ import { ChatMessage, ChatMessageError, ChatTTS, ChatTranslate } from '@/types/message';
5
+
6
+ import { CreateMessageParams, IMessageService } from './type';
7
+
8
+ export class ServerService implements IMessageService {
9
+ createMessage({ sessionId, ...params }: CreateMessageParams): Promise<string> {
10
+ return lambdaClient.message.createMessage.mutate({
11
+ ...params,
12
+ sessionId: this.toDbSessionId(sessionId),
13
+ });
14
+ }
15
+
16
+ batchCreateMessages(messages: ChatMessage[]): Promise<any> {
17
+ return lambdaClient.message.batchCreateMessages.mutate(messages);
18
+ }
19
+
20
+ getMessages(sessionId?: string, topicId?: string | undefined): Promise<ChatMessage[]> {
21
+ return lambdaClient.message.getMessages.query({
22
+ sessionId: this.toDbSessionId(sessionId),
23
+ topicId,
24
+ });
25
+ }
26
+
27
+ getAllMessages(): Promise<ChatMessage[]> {
28
+ return lambdaClient.message.getAllMessages.query();
29
+ }
30
+ getAllMessagesInSession(sessionId: string): Promise<ChatMessage[]> {
31
+ return lambdaClient.message.getAllMessagesInSession.query({
32
+ sessionId: this.toDbSessionId(sessionId),
33
+ });
34
+ }
35
+
36
+ countMessages(): Promise<number> {
37
+ return lambdaClient.message.count.query();
38
+ }
39
+ countTodayMessages(): Promise<number> {
40
+ return lambdaClient.message.countToday.query();
41
+ }
42
+
43
+ updateMessageError(id: string, error: ChatMessageError): Promise<any> {
44
+ return lambdaClient.message.update.mutate({ id, value: { error } });
45
+ }
46
+
47
+ updateMessage(id: string, message: Partial<ChatMessage>): Promise<any> {
48
+ return lambdaClient.message.update.mutate({ id, value: message });
49
+ }
50
+
51
+ updateMessageTranslate(id: string, translate: Partial<ChatTranslate> | false): Promise<any> {
52
+ return lambdaClient.message.updateTranslate.mutate({ id, value: translate as ChatTranslate });
53
+ }
54
+
55
+ updateMessageTTS(id: string, tts: Partial<ChatTTS> | false): Promise<any> {
56
+ return lambdaClient.message.updateTTS.mutate({ id, value: tts });
57
+ }
58
+
59
+ updateMessagePluginState(id: string, value: any): Promise<any> {
60
+ return lambdaClient.message.updatePluginState.mutate({ id, value });
61
+ }
62
+
63
+ bindMessagesToTopic(topicId: string, messageIds: string[]): Promise<any> {
64
+ throw new Error('Method not implemented.');
65
+ }
66
+
67
+ removeMessage(id: string): Promise<any> {
68
+ return lambdaClient.message.removeMessage.mutate({ id });
69
+ }
70
+ removeMessages(sessionId: string, topicId?: string | undefined): Promise<any> {
71
+ return lambdaClient.message.removeMessages.mutate({
72
+ sessionId: this.toDbSessionId(sessionId),
73
+ topicId,
74
+ });
75
+ }
76
+ removeAllMessages(): Promise<any> {
77
+ return lambdaClient.message.removeAllMessages.mutate();
78
+ }
79
+
80
+ private toDbSessionId(sessionId: string | undefined) {
81
+ return sessionId === INBOX_SESSION_ID ? null : sessionId;
82
+ }
83
+
84
+ async hasMessages() {
85
+ const number = await this.countMessages();
86
+ return number > 0;
87
+ }
88
+
89
+ async messageCountToCheckTrace() {
90
+ const number = await this.countMessages();
91
+ return number >= 4;
92
+ }
93
+ }
@@ -1,11 +1,6 @@
1
- // import { getClientConfig } from '@/config/client';
2
- import { ClientService } from './client';
1
+ import { isServerMode } from '@/const/version';
3
2
 
4
- // import { ServerService } from './server';
5
- //
6
- // export type { InstallPluginParams } from './client';
7
- //
8
- // const { ENABLED_SERVER_SERVICE } = getClientConfig();
3
+ import { ClientService } from './client';
4
+ import { ServerService } from './server';
9
5
 
10
- // export const pluginService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
11
- export const pluginService = new ClientService();
6
+ export const pluginService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,46 @@
1
+ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
+
3
+ import { lambdaClient } from '@/libs/trpc/client';
4
+ import { LobeTool } from '@/types/tool';
5
+ import { LobeToolCustomPlugin } from '@/types/tool/plugin';
6
+
7
+ import { IPluginService, InstallPluginParams } from './type';
8
+
9
+ export class ServerService implements IPluginService {
10
+ installPlugin = async (plugin: InstallPluginParams) => {
11
+ await lambdaClient.plugin.createOrInstallPlugin.mutate(plugin);
12
+ };
13
+
14
+ getInstalledPlugins = (): Promise<LobeTool[]> => {
15
+ return lambdaClient.plugin.getPlugins.query();
16
+ };
17
+
18
+ async uninstallPlugin(identifier: string) {
19
+ await lambdaClient.plugin.removePlugin.mutate({ id: identifier });
20
+ }
21
+
22
+ async createCustomPlugin(customPlugin: LobeToolCustomPlugin) {
23
+ await lambdaClient.plugin.createPlugin.mutate({ ...customPlugin, type: 'customPlugin' });
24
+ }
25
+
26
+ async updatePlugin(id: string, value: LobeToolCustomPlugin) {
27
+ await lambdaClient.plugin.updatePlugin.mutate({
28
+ customParams: value.customParams,
29
+ id,
30
+ manifest: value.manifest,
31
+ settings: value.settings,
32
+ });
33
+ }
34
+
35
+ async updatePluginManifest(id: string, manifest: LobeChatPluginManifest) {
36
+ await lambdaClient.plugin.updatePlugin.mutate({ id, manifest });
37
+ }
38
+
39
+ async removeAllPlugins() {
40
+ await lambdaClient.plugin.removeAllPlugins.mutate();
41
+ }
42
+
43
+ async updatePluginSettings(id: string, settings: any, signal?: AbortSignal) {
44
+ await lambdaClient.plugin.updatePlugin.mutate({ id, settings }, { signal });
45
+ }
46
+ }
@@ -1,10 +1,6 @@
1
- // import { getClientConfig } from '@/config/client';
2
- //
3
- import { ClientService } from './client';
4
-
5
- // import { ServerService } from './server';
1
+ import { isServerMode } from '@/const/version';
6
2
 
7
- // const { ENABLED_SERVER_SERVICE } = getClientConfig();
3
+ import { ClientService } from './client';
4
+ import { ServerService } from './server';
8
5
 
9
- // export const sessionService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
10
- export const sessionService = new ClientService();
6
+ export const sessionService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,148 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { DeepPartial } from 'utility-types';
3
+
4
+ import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
5
+ import { lambdaClient } from '@/libs/trpc/client';
6
+ import { useUserStore } from '@/store/user';
7
+ import { authSelectors } from '@/store/user/selectors';
8
+ import { LobeAgentChatConfig, LobeAgentConfig } from '@/types/agent';
9
+ import { MetaData } from '@/types/meta';
10
+ import { BatchTaskResult } from '@/types/service';
11
+ import {
12
+ ChatSessionList,
13
+ LobeAgentSession,
14
+ LobeSessionType,
15
+ LobeSessions,
16
+ SessionGroupId,
17
+ SessionGroupItem,
18
+ SessionGroups,
19
+ } from '@/types/session';
20
+
21
+ import { ISessionService } from './type';
22
+
23
+ export class ServerService implements ISessionService {
24
+ async hasSessions() {
25
+ return (await this.countSessions()) === 0;
26
+ }
27
+
28
+ createSession(type: LobeSessionType, data: Partial<LobeAgentSession>): Promise<string> {
29
+ const { config, group, meta, ...session } = data;
30
+
31
+ return lambdaClient.session.createSession.mutate({
32
+ config: { ...config, ...meta } as any,
33
+ session: { ...session, groupId: group },
34
+ type,
35
+ });
36
+ }
37
+
38
+ async batchCreateSessions(importSessions: LobeSessions): Promise<BatchTaskResult> {
39
+ // TODO: remove any
40
+ const data = await lambdaClient.session.batchCreateSessions.mutate(importSessions as any);
41
+ console.log(data);
42
+ return data;
43
+ }
44
+
45
+ cloneSession(id: string, newTitle: string): Promise<string | undefined> {
46
+ return lambdaClient.session.cloneSession.mutate({ id, newTitle });
47
+ }
48
+
49
+ getGroupedSessions(): Promise<ChatSessionList> {
50
+ return lambdaClient.session.getGroupedSessions.query();
51
+ }
52
+
53
+ countSessions(): Promise<number> {
54
+ return lambdaClient.session.countSessions.query();
55
+ }
56
+
57
+ updateSession(
58
+ id: string,
59
+ data: Partial<{ group?: SessionGroupId; meta?: any; pinned?: boolean }>,
60
+ ): Promise<any> {
61
+ const { group, pinned, meta } = data;
62
+ return lambdaClient.session.updateSession.mutate({
63
+ id,
64
+ value: { groupId: group === 'default' ? null : group, pinned, ...meta },
65
+ });
66
+ }
67
+
68
+ async getSessionConfig(id: string): Promise<LobeAgentConfig> {
69
+ const isLogin = authSelectors.isLogin(useUserStore.getState());
70
+ if (!isLogin) return DEFAULT_AGENT_CONFIG;
71
+
72
+ // TODO: Need to be fixed
73
+ // @ts-ignore
74
+ return lambdaClient.session.getSessionConfig.query({ id });
75
+ }
76
+
77
+ updateSessionConfig(
78
+ id: string,
79
+ config: DeepPartial<LobeAgentConfig>,
80
+ signal?: AbortSignal,
81
+ ): Promise<any> {
82
+ return lambdaClient.session.updateSessionConfig.mutate({ id, value: config }, { signal });
83
+ }
84
+
85
+ updateSessionMeta(id: string, meta: Partial<MetaData>, signal?: AbortSignal): Promise<any> {
86
+ return lambdaClient.session.updateSessionConfig.mutate({ id, value: meta }, { signal });
87
+ }
88
+
89
+ updateSessionChatConfig(
90
+ id: string,
91
+ value: DeepPartial<LobeAgentChatConfig>,
92
+ signal?: AbortSignal,
93
+ ): Promise<any> {
94
+ return lambdaClient.session.updateSessionChatConfig.mutate({ id, value }, { signal });
95
+ }
96
+
97
+ getSessionsByType(type: 'agent' | 'group' | 'all' = 'all'): Promise<LobeSessions> {
98
+ // TODO: need be fixed
99
+ // @ts-ignore
100
+ return lambdaClient.session.getSessions.query({});
101
+ }
102
+
103
+ searchSessions(keywords: string): Promise<LobeSessions> {
104
+ return lambdaClient.session.searchSessions.query({ keywords });
105
+ }
106
+
107
+ removeSession(id: string): Promise<any> {
108
+ return lambdaClient.session.removeSession.mutate({ id });
109
+ }
110
+
111
+ removeAllSessions(): Promise<any> {
112
+ return lambdaClient.session.removeAllSessions.mutate();
113
+ }
114
+
115
+ // ************************************** //
116
+ // *********** SessionGroup *********** //
117
+ // ************************************** //
118
+
119
+ createSessionGroup(name: string, sort?: number): Promise<string> {
120
+ return lambdaClient.sessionGroup.createSessionGroup.mutate({ name, sort });
121
+ }
122
+
123
+ getSessionGroups(): Promise<SessionGroupItem[]> {
124
+ return lambdaClient.sessionGroup.getSessionGroup.query();
125
+ }
126
+
127
+ batchCreateSessionGroups(groups: SessionGroups): Promise<BatchTaskResult> {
128
+ return Promise.resolve({ added: 0, ids: [], skips: [], success: true });
129
+ }
130
+
131
+ removeSessionGroup(id: string, removeChildren?: boolean): Promise<any> {
132
+ return lambdaClient.sessionGroup.removeSessionGroup.mutate({ id, removeChildren });
133
+ }
134
+
135
+ removeSessionGroups(): Promise<any> {
136
+ return lambdaClient.sessionGroup.removeAllSessionGroups.mutate();
137
+ }
138
+
139
+ updateSessionGroup(id: string, value: Partial<SessionGroupItem>): Promise<any> {
140
+ // TODO: need be fixed
141
+ // @ts-ignore
142
+ return lambdaClient.sessionGroup.updateSessionGroup.mutate({ id, value });
143
+ }
144
+
145
+ updateSessionGroupOrder(sortMap: { id: string; sort: number }[]): Promise<any> {
146
+ return lambdaClient.sessionGroup.updateSessionGroupOrder.mutate({ sortMap });
147
+ }
148
+ }
@@ -1,11 +1,6 @@
1
- // import { getClientConfig } from '@/config/client';
2
- //
3
- // import { ClientService } from './client';
4
- // import { ServerService } from './server';
5
- //
6
- // const { ENABLED_SERVER_SERVICE } = getClientConfig();
7
- //
8
- // export const topicService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
1
+ import { isServerMode } from '@/const/version';
2
+
9
3
  import { ClientService } from './client';
4
+ import { ServerService } from './server';
10
5
 
11
- export const topicService = new ClientService();
6
+ export const topicService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,68 @@
1
+ import { INBOX_SESSION_ID } from '@/const/session';
2
+ import { lambdaClient } from '@/libs/trpc/client';
3
+ import { CreateTopicParams, ITopicService, QueryTopicParams } from '@/services/topic/type';
4
+ import { BatchTaskResult } from '@/types/service';
5
+ import { ChatTopic } from '@/types/topic';
6
+
7
+ export class ServerService implements ITopicService {
8
+ createTopic(params: CreateTopicParams): Promise<string> {
9
+ return lambdaClient.topic.createTopic.mutate({
10
+ ...params,
11
+ sessionId: this.toDbSessionId(params.sessionId),
12
+ });
13
+ }
14
+
15
+ batchCreateTopics(importTopics: ChatTopic[]): Promise<BatchTaskResult> {
16
+ return lambdaClient.topic.batchCreateTopics.mutate(importTopics);
17
+ }
18
+
19
+ cloneTopic(id: string, newTitle?: string | undefined): Promise<string> {
20
+ return lambdaClient.topic.cloneTopic.mutate({ id, newTitle });
21
+ }
22
+
23
+ getTopics(params: QueryTopicParams): Promise<ChatTopic[]> {
24
+ return lambdaClient.topic.getTopics.query({
25
+ ...params,
26
+ sessionId: this.toDbSessionId(params.sessionId),
27
+ }) as any;
28
+ }
29
+
30
+ getAllTopics(): Promise<ChatTopic[]> {
31
+ return lambdaClient.topic.getAllTopics.query() as any;
32
+ }
33
+
34
+ async countTopics() {
35
+ return lambdaClient.topic.countTopics.query();
36
+ }
37
+
38
+ searchTopics(keywords: string, sessionId?: string | undefined): Promise<ChatTopic[]> {
39
+ return lambdaClient.topic.searchTopics.query({
40
+ keywords,
41
+ sessionId: this.toDbSessionId(sessionId),
42
+ }) as any;
43
+ }
44
+
45
+ updateTopic(id: string, data: Partial<ChatTopic>): Promise<any> {
46
+ return lambdaClient.topic.updateTopic.mutate({ id, value: data });
47
+ }
48
+
49
+ removeTopic(id: string): Promise<any> {
50
+ return lambdaClient.topic.removeTopic.mutate({ id });
51
+ }
52
+
53
+ removeTopics(sessionId: string): Promise<any> {
54
+ return lambdaClient.topic.batchDeleteBySessionId.mutate({ id: this.toDbSessionId(sessionId) });
55
+ }
56
+
57
+ batchRemoveTopics(topics: string[]): Promise<any> {
58
+ return lambdaClient.topic.batchDelete.mutate({ ids: topics });
59
+ }
60
+
61
+ removeAllTopic(): Promise<any> {
62
+ return lambdaClient.topic.removeAllTopics.mutate();
63
+ }
64
+
65
+ private toDbSessionId(sessionId?: string | null) {
66
+ return sessionId === INBOX_SESSION_ID ? null : sessionId;
67
+ }
68
+ }
@@ -1,11 +1,6 @@
1
- // import { getClientConfig } from '@/config/client';
2
- //
3
- // import { ClientService } from './client';
4
- // import { ServerService } from './server';
5
- //
6
- // const { ENABLED_SERVER_SERVICE } = getClientConfig();
7
- //
8
- // export const userService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
1
+ import { isServerMode } from '@/const/version';
2
+
9
3
  import { ClientService } from './client';
4
+ import { ServerService } from './server';
10
5
 
11
- export const userService = new ClientService();
6
+ export const userService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,28 @@
1
+ import { DeepPartial } from 'utility-types';
2
+
3
+ import { lambdaClient } from '@/libs/trpc/client';
4
+ import { IUserService } from '@/services/user/type';
5
+ import { UserInitializationState, UserPreference } from '@/types/user';
6
+ import { UserSettings } from '@/types/user/settings';
7
+
8
+ export class ServerService implements IUserService {
9
+ getUserState = async (): Promise<UserInitializationState> => {
10
+ return lambdaClient.user.getUserState.query();
11
+ };
12
+
13
+ async makeUserOnboarded() {
14
+ return lambdaClient.user.makeUserOnboarded.mutate();
15
+ }
16
+
17
+ async updatePreference(preference: UserPreference) {
18
+ return lambdaClient.user.updatePreference.mutate(preference);
19
+ }
20
+
21
+ updateUserSettings = async (value: DeepPartial<UserSettings>, signal?: AbortSignal) => {
22
+ return lambdaClient.user.updateSettings.mutate(value, { signal });
23
+ };
24
+
25
+ resetUserSettings = async () => {
26
+ return lambdaClient.user.resetSettings.mutate();
27
+ };
28
+ }
@@ -0,0 +1,7 @@
1
+ // import env
2
+ import { Crypto } from '@peculiar/webcrypto';
3
+ import * as dotenv from 'dotenv';
4
+
5
+ dotenv.config();
6
+
7
+ global.crypto = new Crypto();
package/vitest.config.ts CHANGED
@@ -22,12 +22,13 @@ export default defineConfig({
22
22
  ],
23
23
  provider: 'v8',
24
24
  reporter: ['text', 'json', 'lcov', 'text-summary'],
25
+ reportsDirectory: './coverage/app',
25
26
  },
26
27
  deps: {
27
28
  inline: ['vitest-canvas-mock'],
28
29
  },
29
- // threads: false,
30
30
  environment: 'happy-dom',
31
+ exclude: ['**/node_modules/**', '**/dist/**', '**/build/**', 'src/database/server/**/**'],
31
32
  globals: true,
32
33
  setupFiles: './tests/setup.ts',
33
34
  },
@@ -0,0 +1,23 @@
1
+ import { resolve } from 'node:path';
2
+ import { defineConfig } from 'vitest/config';
3
+
4
+ export default defineConfig({
5
+ test: {
6
+ alias: {
7
+ '@': resolve(__dirname, './src'),
8
+ },
9
+ coverage: {
10
+ all: false,
11
+ exclude: ['src/database/server/core/dbForTest.ts'],
12
+ provider: 'v8',
13
+ reporter: ['text', 'json', 'lcov', 'text-summary'],
14
+ reportsDirectory: './coverage/server',
15
+ },
16
+ environment: 'node',
17
+ include: ['src/database/server/**/**/*.test.ts'],
18
+ poolOptions: {
19
+ threads: { singleThread: true },
20
+ },
21
+ setupFiles: './tests/setup-db.ts',
22
+ },
23
+ });