@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.
- package/.github/workflows/release.yml +21 -2
- package/.github/workflows/sync.yml +1 -1
- package/.github/workflows/test.yml +35 -4
- package/CHANGELOG.md +25 -0
- package/LICENSE +38 -21
- package/codecov.yml +11 -0
- package/drizzle.config.ts +29 -0
- package/next.config.mjs +3 -0
- package/package.json +24 -4
- package/scripts/migrateServerDB/index.ts +30 -0
- package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +2 -1
- package/src/app/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx +95 -88
- package/src/app/(main)/chat/settings/features/HeaderContent.tsx +37 -31
- package/src/app/api/webhooks/clerk/__tests__/fixtures/createUser.json +73 -0
- package/src/app/api/webhooks/clerk/route.ts +159 -0
- package/src/app/api/webhooks/clerk/validateRequest.ts +22 -0
- package/src/app/trpc/edge/[trpc]/route.ts +1 -1
- package/src/app/trpc/lambda/[trpc]/route.ts +26 -0
- package/src/config/auth.ts +2 -0
- package/src/config/db.ts +13 -1
- package/src/database/server/core/db.ts +44 -0
- package/src/database/server/core/dbForTest.ts +45 -0
- package/src/database/server/index.ts +1 -0
- package/src/database/server/migrations/0000_init.sql +439 -0
- package/src/database/server/migrations/0001_add_client_id.sql +9 -0
- package/src/database/server/migrations/0002_amusing_puma.sql +9 -0
- package/src/database/server/migrations/meta/0000_snapshot.json +1583 -0
- package/src/database/server/migrations/meta/0001_snapshot.json +1636 -0
- package/src/database/server/migrations/meta/0002_snapshot.json +1630 -0
- package/src/database/server/migrations/meta/_journal.json +27 -0
- package/src/database/server/models/__tests__/file.test.ts +140 -0
- package/src/database/server/models/__tests__/message.test.ts +847 -0
- package/src/database/server/models/__tests__/plugin.test.ts +172 -0
- package/src/database/server/models/__tests__/session.test.ts +595 -0
- package/src/database/server/models/__tests__/topic.test.ts +623 -0
- package/src/database/server/models/__tests__/user.test.ts +173 -0
- package/src/database/server/models/_template.ts +44 -0
- package/src/database/server/models/file.ts +51 -0
- package/src/database/server/models/message.ts +378 -0
- package/src/database/server/models/plugin.ts +63 -0
- package/src/database/server/models/session.ts +290 -0
- package/src/database/server/models/sessionGroup.ts +69 -0
- package/src/database/server/models/topic.ts +265 -0
- package/src/database/server/models/user.ts +138 -0
- package/src/database/server/modules/DataImporter/__tests__/fixtures/messages.json +1101 -0
- package/src/database/server/modules/DataImporter/__tests__/index.test.ts +954 -0
- package/src/database/server/modules/DataImporter/index.ts +333 -0
- package/src/database/server/schemas/_id.ts +15 -0
- package/src/database/server/schemas/lobechat.ts +601 -0
- package/src/database/server/utils/idGenerator.test.ts +39 -0
- package/src/database/server/utils/idGenerator.ts +26 -0
- package/src/features/User/UserPanel/useMenu.tsx +43 -37
- package/src/libs/trpc/client.ts +52 -3
- package/src/server/files/s3.ts +21 -1
- package/src/server/keyVaultsEncrypt/index.test.ts +62 -0
- package/src/server/keyVaultsEncrypt/index.ts +93 -0
- package/src/server/mock.ts +1 -1
- package/src/server/routers/{index.ts → edge/index.ts} +3 -3
- package/src/server/routers/lambda/file.ts +49 -0
- package/src/server/routers/lambda/importer.ts +54 -0
- package/src/server/routers/lambda/index.ts +28 -0
- package/src/server/routers/lambda/message.ts +165 -0
- package/src/server/routers/lambda/plugin.ts +100 -0
- package/src/server/routers/lambda/session.ts +194 -0
- package/src/server/routers/lambda/sessionGroup.ts +77 -0
- package/src/server/routers/lambda/topic.ts +134 -0
- package/src/server/routers/lambda/user.ts +57 -0
- package/src/services/file/index.ts +4 -7
- package/src/services/file/server.ts +45 -0
- package/src/services/import/index.ts +4 -1
- package/src/services/import/server.ts +115 -0
- package/src/services/message/index.ts +4 -8
- package/src/services/message/server.ts +93 -0
- package/src/services/plugin/index.ts +4 -9
- package/src/services/plugin/server.ts +46 -0
- package/src/services/session/index.ts +4 -8
- package/src/services/session/server.ts +148 -0
- package/src/services/topic/index.ts +4 -9
- package/src/services/topic/server.ts +68 -0
- package/src/services/user/index.ts +4 -9
- package/src/services/user/server.ts +28 -0
- package/tests/setup-db.ts +7 -0
- package/vitest.config.ts +2 -1
- 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
|
-
|
|
1
|
+
import { isServerMode } from '@/const/version';
|
|
2
|
+
|
|
2
3
|
import { ClientService } from './client';
|
|
4
|
+
import { ServerService } from './server';
|
|
3
5
|
|
|
4
|
-
|
|
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();
|