@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,100 @@
1
+ import { z } from 'zod';
2
+
3
+ import { PluginModel } from '@/database/server/models/plugin';
4
+ import { authedProcedure, publicProcedure, router } from '@/libs/trpc';
5
+ import { LobeTool } from '@/types/tool';
6
+
7
+ const pluginProcedure = authedProcedure.use(async (opts) => {
8
+ const { ctx } = opts;
9
+
10
+ return opts.next({
11
+ ctx: { pluginModel: new PluginModel(ctx.userId) },
12
+ });
13
+ });
14
+
15
+ export const pluginRouter = router({
16
+ createOrInstallPlugin: pluginProcedure
17
+ .input(
18
+ z.object({
19
+ customParams: z.any(),
20
+ identifier: z.string(),
21
+ manifest: z.any(),
22
+ type: z.enum(['plugin', 'customPlugin']),
23
+ }),
24
+ )
25
+ .mutation(async ({ input, ctx }) => {
26
+ const result = await ctx.pluginModel.findById(input.identifier);
27
+
28
+ // if not exist, we should create the plugin
29
+ if (!result) {
30
+ const data = await ctx.pluginModel.create({
31
+ customParams: input.customParams,
32
+ identifier: input.identifier,
33
+ manifest: input.manifest,
34
+ type: input.type,
35
+ });
36
+
37
+ return data.identifier;
38
+ }
39
+
40
+ // or we can just update the plugin manifest
41
+ await ctx.pluginModel.update(input.identifier, { manifest: input.manifest });
42
+ }),
43
+
44
+ createPlugin: pluginProcedure
45
+ .input(
46
+ z.object({
47
+ customParams: z.any(),
48
+ identifier: z.string(),
49
+ manifest: z.any(),
50
+ type: z.enum(['plugin', 'customPlugin']),
51
+ }),
52
+ )
53
+ .mutation(async ({ input, ctx }) => {
54
+ const data = await ctx.pluginModel.create({
55
+ customParams: input.customParams,
56
+ identifier: input.identifier,
57
+ manifest: input.manifest,
58
+ type: input.type,
59
+ });
60
+
61
+ return data.identifier;
62
+ }),
63
+
64
+ getPlugins: publicProcedure.query(async ({ ctx }): Promise<LobeTool[]> => {
65
+ if (!ctx.userId) return [];
66
+
67
+ const pluginModel = new PluginModel(ctx.userId);
68
+
69
+ return pluginModel.query();
70
+ }),
71
+
72
+ removeAllPlugins: pluginProcedure.mutation(async ({ ctx }) => {
73
+ return ctx.pluginModel.deleteAll();
74
+ }),
75
+
76
+ removePlugin: pluginProcedure
77
+ .input(z.object({ id: z.string() }))
78
+ .mutation(async ({ input, ctx }) => {
79
+ return ctx.pluginModel.delete(input.id);
80
+ }),
81
+
82
+ updatePlugin: pluginProcedure
83
+ .input(
84
+ z.object({
85
+ customParams: z.any().optional(),
86
+ id: z.string(),
87
+ manifest: z.any().optional(),
88
+ settings: z.any().optional(),
89
+ }),
90
+ )
91
+ .mutation(async ({ input, ctx }) => {
92
+ return ctx.pluginModel.update(input.id, {
93
+ customParams: input.customParams,
94
+ manifest: input.manifest,
95
+ settings: input.settings,
96
+ });
97
+ }),
98
+ });
99
+
100
+ export type PluginRouter = typeof pluginRouter;
@@ -0,0 +1,194 @@
1
+ import { z } from 'zod';
2
+
3
+ import { INBOX_SESSION_ID } from '@/const/session';
4
+ import { SessionModel } from '@/database/server/models/session';
5
+ import { SessionGroupModel } from '@/database/server/models/sessionGroup';
6
+ import { insertAgentSchema, insertSessionSchema } from '@/database/server/schemas/lobechat';
7
+ import { pino } from '@/libs/logger';
8
+ import { authedProcedure, publicProcedure, router } from '@/libs/trpc';
9
+ import { AgentChatConfigSchema } from '@/types/agent';
10
+ import { LobeMetaDataSchema } from '@/types/meta';
11
+ import { BatchTaskResult } from '@/types/service';
12
+ import { ChatSessionList } from '@/types/session';
13
+ import { merge } from '@/utils/merge';
14
+
15
+ const sessionProcedure = authedProcedure.use(async (opts) => {
16
+ const { ctx } = opts;
17
+
18
+ return opts.next({
19
+ ctx: {
20
+ sessionGroupModel: new SessionGroupModel(ctx.userId),
21
+ sessionModel: new SessionModel(ctx.userId),
22
+ },
23
+ });
24
+ });
25
+
26
+ export const sessionRouter = router({
27
+ batchCreateSessions: sessionProcedure
28
+ .input(
29
+ z.array(
30
+ z
31
+ .object({
32
+ config: z.object({}).passthrough(),
33
+ group: z.string().optional(),
34
+ id: z.string(),
35
+ meta: LobeMetaDataSchema,
36
+ pinned: z.boolean().optional(),
37
+ type: z.string(),
38
+ })
39
+ .partial(),
40
+ ),
41
+ )
42
+ .mutation(async ({ input, ctx }): Promise<BatchTaskResult> => {
43
+ const data = await ctx.sessionModel.batchCreate(
44
+ input.map((item) => ({
45
+ ...item,
46
+ ...item.meta,
47
+ })) as any,
48
+ );
49
+
50
+ return { added: data.rowCount as number, ids: [], skips: [], success: true };
51
+ }),
52
+
53
+ cloneSession: sessionProcedure
54
+ .input(z.object({ id: z.string(), newTitle: z.string() }))
55
+ .mutation(async ({ input, ctx }) => {
56
+ const data = await ctx.sessionModel.duplicate(input.id, input.newTitle);
57
+
58
+ return data?.id;
59
+ }),
60
+
61
+ countSessions: sessionProcedure.query(async ({ ctx }) => {
62
+ return ctx.sessionModel.count();
63
+ }),
64
+
65
+ createSession: sessionProcedure
66
+ .input(
67
+ z.object({
68
+ config: insertAgentSchema
69
+ .omit({ chatConfig: true, plugins: true, tags: true, tts: true })
70
+ .passthrough()
71
+ .partial(),
72
+ session: insertSessionSchema.omit({ createdAt: true, updatedAt: true }).partial(),
73
+ type: z.enum(['agent', 'group']),
74
+ }),
75
+ )
76
+ .mutation(async ({ input, ctx }) => {
77
+ const data = await ctx.sessionModel.create(input);
78
+
79
+ return data.id;
80
+ }),
81
+
82
+ getGroupedSessions: publicProcedure.query(async ({ ctx }): Promise<ChatSessionList> => {
83
+ if (!ctx.userId)
84
+ return {
85
+ sessionGroups: [],
86
+ sessions: [],
87
+ };
88
+
89
+ const sessionModel = new SessionModel(ctx.userId);
90
+
91
+ return sessionModel.queryWithGroups();
92
+ }),
93
+
94
+ getSessionConfig: sessionProcedure
95
+ .input(
96
+ z.object({
97
+ id: z.string(),
98
+ }),
99
+ )
100
+ .query(async ({ input, ctx }) => {
101
+ if (input.id === INBOX_SESSION_ID) {
102
+ const item = await ctx.sessionModel.findByIdOrSlug(INBOX_SESSION_ID);
103
+ // if there is no session for user, create one
104
+ if (!item) {
105
+ const res = await ctx.sessionModel.createInbox();
106
+ pino.info('create inbox session', res);
107
+ }
108
+ }
109
+
110
+ const session = await ctx.sessionModel.findByIdOrSlug(input.id);
111
+
112
+ if (!session) throw new Error('Session not found');
113
+
114
+ return session.agent;
115
+ }),
116
+
117
+ getSessions: sessionProcedure
118
+ .input(
119
+ z.object({
120
+ current: z.number().optional(),
121
+ pageSize: z.number().optional(),
122
+ }),
123
+ )
124
+ .query(async ({ input, ctx }) => {
125
+ const { current, pageSize } = input;
126
+
127
+ return ctx.sessionModel.query({ current, pageSize });
128
+ }),
129
+
130
+ removeAllSessions: sessionProcedure.mutation(async ({ ctx }) => {
131
+ return ctx.sessionModel.deleteAll();
132
+ }),
133
+
134
+ removeSession: sessionProcedure
135
+ .input(z.object({ id: z.string() }))
136
+ .mutation(async ({ input, ctx }) => {
137
+ return ctx.sessionModel.delete(input.id);
138
+ }),
139
+
140
+ searchSessions: sessionProcedure
141
+ .input(z.object({ keywords: z.string() }))
142
+ .query(async ({ input, ctx }) => {
143
+ return ctx.sessionModel.queryByKeyword(input.keywords);
144
+ }),
145
+
146
+ updateSession: sessionProcedure
147
+ .input(
148
+ z.object({
149
+ id: z.string(),
150
+ value: insertSessionSchema.partial(),
151
+ }),
152
+ )
153
+ .mutation(async ({ input, ctx }) => {
154
+ return ctx.sessionModel.update(input.id, input.value);
155
+ }),
156
+ updateSessionChatConfig: sessionProcedure
157
+ .input(
158
+ z.object({
159
+ id: z.string(),
160
+ value: AgentChatConfigSchema.partial(),
161
+ }),
162
+ )
163
+ .mutation(async ({ input, ctx }) => {
164
+ const session = await ctx.sessionModel.findByIdOrSlug(input.id);
165
+
166
+ if (!session) return;
167
+
168
+ return ctx.sessionModel.updateConfig(session.agent.id, {
169
+ chatConfig: merge(session.agent.chatConfig, input.value),
170
+ });
171
+ }),
172
+ updateSessionConfig: sessionProcedure
173
+ .input(
174
+ z.object({
175
+ id: z.string(),
176
+ value: z.object({}).passthrough().partial(),
177
+ }),
178
+ )
179
+ .mutation(async ({ input, ctx }) => {
180
+ const session = await ctx.sessionModel.findByIdOrSlug(input.id);
181
+
182
+ if (!session || !input.value) return;
183
+
184
+ if (!session.agent) {
185
+ throw new Error(
186
+ 'this session is not assign with agent, please contact with admin to fix this issue.',
187
+ );
188
+ }
189
+
190
+ return ctx.sessionModel.updateConfig(session.agent.id, input.value);
191
+ }),
192
+ });
193
+
194
+ export type SessionRouter = typeof sessionRouter;
@@ -0,0 +1,77 @@
1
+ import { z } from 'zod';
2
+
3
+ import { SessionGroupModel } from '@/database/server/models/sessionGroup';
4
+ import { insertSessionGroupSchema } from '@/database/server/schemas/lobechat';
5
+ import { authedProcedure, router } from '@/libs/trpc';
6
+ import { SessionGroupItem } from '@/types/session';
7
+
8
+ const sessionProcedure = authedProcedure.use(async (opts) => {
9
+ const { ctx } = opts;
10
+
11
+ return opts.next({
12
+ ctx: {
13
+ sessionGroupModel: new SessionGroupModel(ctx.userId),
14
+ },
15
+ });
16
+ });
17
+
18
+ export const sessionGroupRouter = router({
19
+ createSessionGroup: sessionProcedure
20
+ .input(
21
+ z.object({
22
+ name: z.string(),
23
+ sort: z.number().optional(),
24
+ }),
25
+ )
26
+ .mutation(async ({ input, ctx }) => {
27
+ const data = await ctx.sessionGroupModel.create({
28
+ name: input.name,
29
+ sort: input.sort,
30
+ });
31
+
32
+ return data?.id;
33
+ }),
34
+
35
+ getSessionGroup: sessionProcedure.query(async ({ ctx }): Promise<SessionGroupItem[]> => {
36
+ return ctx.sessionGroupModel.query() as any;
37
+ }),
38
+
39
+ removeAllSessionGroups: sessionProcedure.mutation(async ({ ctx }) => {
40
+ return ctx.sessionGroupModel.deleteAll();
41
+ }),
42
+
43
+ removeSessionGroup: sessionProcedure
44
+ .input(z.object({ id: z.string(), removeChildren: z.boolean().optional() }))
45
+ .mutation(async ({ input, ctx }) => {
46
+ return ctx.sessionGroupModel.delete(input.id);
47
+ }),
48
+
49
+ updateSessionGroup: sessionProcedure
50
+ .input(
51
+ z.object({
52
+ id: z.string(),
53
+ value: insertSessionGroupSchema.partial(),
54
+ }),
55
+ )
56
+ .mutation(async ({ input, ctx }) => {
57
+ return ctx.sessionGroupModel.update(input.id, input.value);
58
+ }),
59
+ updateSessionGroupOrder: sessionProcedure
60
+ .input(
61
+ z.object({
62
+ sortMap: z.array(
63
+ z.object({
64
+ id: z.string(),
65
+ sort: z.number(),
66
+ }),
67
+ ),
68
+ }),
69
+ )
70
+ .mutation(async ({ input, ctx }) => {
71
+ console.log('sortMap:', input.sortMap);
72
+
73
+ return ctx.sessionGroupModel.updateOrder(input.sortMap);
74
+ }),
75
+ });
76
+
77
+ export type SessionGroupRouter = typeof sessionGroupRouter;
@@ -0,0 +1,134 @@
1
+ import { z } from 'zod';
2
+
3
+ import { TopicModel } from '@/database/server/models/topic';
4
+ import { authedProcedure, publicProcedure, router } from '@/libs/trpc';
5
+ import { BatchTaskResult } from '@/types/service';
6
+
7
+ const topicProcedure = authedProcedure.use(async (opts) => {
8
+ const { ctx } = opts;
9
+
10
+ return opts.next({
11
+ ctx: { topicModel: new TopicModel(ctx.userId) },
12
+ });
13
+ });
14
+
15
+ export const topicRouter = router({
16
+ batchCreateTopics: topicProcedure
17
+ .input(
18
+ z.array(
19
+ z.object({
20
+ favorite: z.boolean().optional(),
21
+ id: z.string().optional(),
22
+ messages: z.array(z.string()).optional(),
23
+ sessionId: z.string().optional(),
24
+ title: z.string(),
25
+ }),
26
+ ),
27
+ )
28
+ .mutation(async ({ input, ctx }): Promise<BatchTaskResult> => {
29
+ const data = await ctx.topicModel.batchCreate(
30
+ input.map((item) => ({
31
+ ...item,
32
+ })) as any,
33
+ );
34
+
35
+ return { added: data.length, ids: [], skips: [], success: true };
36
+ }),
37
+
38
+ batchDelete: topicProcedure
39
+ .input(z.object({ ids: z.array(z.string()) }))
40
+ .mutation(async ({ input, ctx }) => {
41
+ return ctx.topicModel.batchDelete(input.ids);
42
+ }),
43
+
44
+ batchDeleteBySessionId: topicProcedure
45
+ .input(z.object({ id: z.string().nullable().optional() }))
46
+ .mutation(async ({ input, ctx }) => {
47
+ return ctx.topicModel.batchDeleteBySessionId(input.id);
48
+ }),
49
+
50
+ cloneTopic: topicProcedure
51
+ .input(z.object({ id: z.string(), newTitle: z.string().optional() }))
52
+ .mutation(async ({ input, ctx }) => {
53
+ const data = await ctx.topicModel.duplicate(input.id, input.newTitle);
54
+
55
+ return data.topic.id;
56
+ }),
57
+
58
+ countTopics: topicProcedure.query(async ({ ctx }) => {
59
+ return ctx.topicModel.count();
60
+ }),
61
+
62
+ createTopic: topicProcedure
63
+ .input(
64
+ z.object({
65
+ favorite: z.boolean().optional(),
66
+ messages: z.array(z.string()).optional(),
67
+ sessionId: z.string().nullable().optional(),
68
+ title: z.string(),
69
+ }),
70
+ )
71
+ .mutation(async ({ input, ctx }) => {
72
+ const data = await ctx.topicModel.create(input);
73
+
74
+ return data.id;
75
+ }),
76
+
77
+ getAllTopics: topicProcedure.query(async ({ ctx }) => {
78
+ return ctx.topicModel.queryAll();
79
+ }),
80
+
81
+ getTopics: publicProcedure
82
+ .input(
83
+ z.object({
84
+ current: z.number().optional(),
85
+ pageSize: z.number().optional(),
86
+ sessionId: z.string().nullable().optional(),
87
+ }),
88
+ )
89
+ .query(async ({ input, ctx }) => {
90
+ if (!ctx.userId) return [];
91
+
92
+ const topicModel = new TopicModel(ctx.userId);
93
+
94
+ return topicModel.query(input);
95
+ }),
96
+
97
+ hasTopics: topicProcedure.query(async ({ ctx }) => {
98
+ return (await ctx.topicModel.count()) === 0;
99
+ }),
100
+
101
+ removeAllTopics: topicProcedure.mutation(async ({ ctx }) => {
102
+ return ctx.topicModel.deleteAll();
103
+ }),
104
+
105
+ removeTopic: topicProcedure
106
+ .input(z.object({ id: z.string() }))
107
+ .mutation(async ({ input, ctx }) => {
108
+ return ctx.topicModel.delete(input.id);
109
+ }),
110
+
111
+ searchTopics: topicProcedure
112
+ .input(z.object({ keywords: z.string(), sessionId: z.string().nullable().optional() }))
113
+ .query(async ({ input, ctx }) => {
114
+ return ctx.topicModel.queryByKeyword(input.keywords, input.sessionId);
115
+ }),
116
+
117
+ updateTopic: topicProcedure
118
+ .input(
119
+ z.object({
120
+ id: z.string(),
121
+ value: z.object({
122
+ favorite: z.boolean().optional(),
123
+ messages: z.array(z.string()).optional(),
124
+ sessionId: z.string().optional(),
125
+ title: z.string().optional(),
126
+ }),
127
+ }),
128
+ )
129
+ .mutation(async ({ input, ctx }) => {
130
+ return ctx.topicModel.update(input.id, input.value);
131
+ }),
132
+ });
133
+
134
+ export type TopicRouter = typeof topicRouter;
@@ -0,0 +1,57 @@
1
+ import { z } from 'zod';
2
+
3
+ import { MessageModel } from '@/database/server/models/message';
4
+ import { SessionModel } from '@/database/server/models/session';
5
+ import { UserModel } from '@/database/server/models/user';
6
+ import { authedProcedure, router } from '@/libs/trpc';
7
+ import { UserInitializationState, UserPreference } from '@/types/user';
8
+
9
+ const userProcedure = authedProcedure.use(async (opts) => {
10
+ return opts.next({
11
+ ctx: { userModel: new UserModel() },
12
+ });
13
+ });
14
+
15
+ export const userRouter = router({
16
+ getUserState: userProcedure.query(async ({ ctx }): Promise<UserInitializationState> => {
17
+ const state = await ctx.userModel.getUserState(ctx.userId);
18
+
19
+ const messageModel = new MessageModel(ctx.userId);
20
+ const messageCount = await messageModel.count();
21
+
22
+ const sessionModel = new SessionModel(ctx.userId);
23
+ const sessionCount = await sessionModel.count();
24
+
25
+ return {
26
+ canEnablePWAGuide: messageCount >= 2,
27
+ canEnableTrace: messageCount >= 4,
28
+ // 有消息,或者创建过助手,则认为有 conversation
29
+ hasConversation: messageCount > 0 || sessionCount > 1,
30
+
31
+ isOnboard: state.isOnboarded || false,
32
+ preference: state.preference as UserPreference,
33
+ settings: state.settings,
34
+ userId: ctx.userId,
35
+ };
36
+ }),
37
+
38
+ makeUserOnboarded: userProcedure.mutation(async ({ ctx }) => {
39
+ return ctx.userModel.updateUser(ctx.userId, { isOnboarded: true });
40
+ }),
41
+
42
+ resetSettings: userProcedure.mutation(async ({ ctx }) => {
43
+ return ctx.userModel.deleteSetting(ctx.userId);
44
+ }),
45
+
46
+ updatePreference: userProcedure.input(z.any()).mutation(async ({ ctx, input }) => {
47
+ return ctx.userModel.updatePreference(ctx.userId, input);
48
+ }),
49
+
50
+ updateSettings: userProcedure
51
+ .input(z.object({}).passthrough())
52
+ .mutation(async ({ ctx, input }) => {
53
+ return ctx.userModel.updateSetting(ctx.userId, input);
54
+ }),
55
+ });
56
+
57
+ export type UserRouter = typeof userRouter;
@@ -1,9 +1,6 @@
1
- // import { getClientConfig } from '@/config/client';
1
+ import { isServerMode } from '@/const/version';
2
+
2
3
  import { ClientService } from './client';
4
+ import { ServerService } from './server';
3
5
 
4
- // import { ServerService } from './server';
5
- //
6
- // const { ENABLED_SERVER_SERVICE } = getClientConfig();
7
- //
8
- // export const fileService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
9
- export const fileService = new ClientService();
6
+ export const fileService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,45 @@
1
+ import urlJoin from 'url-join';
2
+
3
+ import { fileEnv } from '@/config/file';
4
+ import { lambdaClient } from '@/libs/trpc/client';
5
+ import { FilePreview, UploadFileParams } from '@/types/files';
6
+
7
+ import { IFileService } from './type';
8
+
9
+ interface CreateFileParams extends Omit<UploadFileParams, 'url'> {
10
+ url: string;
11
+ }
12
+
13
+ export class ServerService implements IFileService {
14
+ async createFile(params: UploadFileParams) {
15
+ return lambdaClient.file.createFile.mutate(params as CreateFileParams);
16
+ }
17
+
18
+ async getFile(id: string): Promise<FilePreview> {
19
+ if (!fileEnv.NEXT_PUBLIC_S3_DOMAIN) {
20
+ throw new Error('fileEnv.NEXT_PUBLIC_S3_DOMAIN is not set while enable server upload');
21
+ }
22
+
23
+ const item = await lambdaClient.file.findById.query({ id });
24
+
25
+ if (!item) {
26
+ throw new Error('file not found');
27
+ }
28
+
29
+ return {
30
+ fileType: item.fileType,
31
+ id: item.id,
32
+ name: item.name,
33
+ saveMode: 'url',
34
+ url: urlJoin(fileEnv.NEXT_PUBLIC_S3_DOMAIN!, item.url!),
35
+ };
36
+ }
37
+
38
+ async removeFile(id: string) {
39
+ await lambdaClient.file.removeFile.mutate({ id });
40
+ }
41
+
42
+ async removeAllFiles() {
43
+ await lambdaClient.file.removeAllFiles.mutate();
44
+ }
45
+ }
@@ -1,3 +1,6 @@
1
+ import { isServerMode } from '@/const/version';
2
+
1
3
  import { ClientService } from './client';
4
+ import { ServerService } from './server';
2
5
 
3
- export const importService = new ClientService();
6
+ export const importService = isServerMode ? new ServerService() : new ClientService();