@lobehub/chat 1.42.6 → 1.43.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 (140) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/changelog/v1.json +12 -0
  3. package/docs/.cdn.cache.json +1 -0
  4. package/docs/changelog/2025-01-03-user-profile.mdx +27 -0
  5. package/docs/changelog/2025-01-03-user-profile.zh-CN.mdx +26 -0
  6. package/docs/self-hosting/advanced/auth/next-auth/wechat.mdx +3 -1
  7. package/docs/self-hosting/advanced/auth/next-auth/wechat.zh-CN.mdx +2 -2
  8. package/locales/ar/auth.json +76 -4
  9. package/locales/bg-BG/auth.json +75 -3
  10. package/locales/de-DE/auth.json +78 -6
  11. package/locales/en-US/auth.json +78 -6
  12. package/locales/es-ES/auth.json +75 -3
  13. package/locales/fa-IR/auth.json +77 -5
  14. package/locales/fr-FR/auth.json +78 -6
  15. package/locales/it-IT/auth.json +76 -4
  16. package/locales/ja-JP/auth.json +76 -4
  17. package/locales/ko-KR/auth.json +75 -3
  18. package/locales/nl-NL/auth.json +76 -4
  19. package/locales/pl-PL/auth.json +76 -4
  20. package/locales/pt-BR/auth.json +76 -4
  21. package/locales/ru-RU/auth.json +75 -3
  22. package/locales/tr-TR/auth.json +74 -3
  23. package/locales/vi-VN/auth.json +75 -3
  24. package/locales/zh-CN/auth.json +75 -3
  25. package/locales/zh-TW/auth.json +75 -3
  26. package/package.json +4 -3
  27. package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +4 -0
  28. package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -46
  29. package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +11 -14
  30. package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +6 -21
  31. package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +38 -21
  32. package/src/app/(main)/(mobile)/me/profile/layout.tsx +0 -3
  33. package/src/app/(main)/(mobile)/me/profile/page.tsx +3 -3
  34. package/src/app/(main)/chat/loading.tsx +2 -2
  35. package/src/app/(main)/discover/loading.tsx +2 -8
  36. package/src/app/(main)/files/loading.tsx +2 -2
  37. package/src/app/(main)/profile/(home)/Client.tsx +53 -0
  38. package/src/app/(main)/profile/(home)/[[...slugs]]/page.tsx +38 -0
  39. package/src/app/(main)/profile/@category/default.tsx +9 -0
  40. package/src/app/(main)/profile/@category/features/CategoryContent.tsx +38 -0
  41. package/src/app/(main)/profile/_layout/Desktop/Header.tsx +85 -0
  42. package/src/app/(main)/profile/_layout/Desktop/SideBar.tsx +42 -0
  43. package/src/app/(main)/profile/_layout/Desktop/index.tsx +48 -0
  44. package/src/app/(main)/profile/_layout/Mobile/Header.tsx +23 -5
  45. package/src/app/(main)/profile/_layout/Mobile/index.tsx +12 -5
  46. package/src/app/(main)/profile/_layout/type.ts +6 -0
  47. package/src/app/(main)/profile/error.tsx +5 -0
  48. package/src/app/(main)/profile/features/ClerkProfile.tsx +72 -0
  49. package/src/app/(main)/profile/hooks/useCategory.tsx +51 -0
  50. package/src/app/(main)/profile/layout.tsx +7 -17
  51. package/src/app/(main)/profile/loading.tsx +2 -22
  52. package/src/app/(main)/profile/not-found.tsx +3 -0
  53. package/src/app/(main)/profile/security/page.tsx +34 -0
  54. package/src/app/(main)/profile/stats/Client.tsx +52 -0
  55. package/src/app/(main)/profile/stats/features/AiHeatmaps.tsx +130 -0
  56. package/src/app/(main)/profile/stats/features/AssistantsRank.tsx +115 -0
  57. package/src/app/(main)/profile/stats/features/ModelsRank.tsx +84 -0
  58. package/src/app/(main)/profile/stats/features/ShareButton/Preview.tsx +159 -0
  59. package/src/app/(main)/profile/stats/features/ShareButton/ShareModal.tsx +87 -0
  60. package/src/app/(main)/profile/stats/features/ShareButton/TotalCard.tsx +39 -0
  61. package/src/app/(main)/profile/stats/features/ShareButton/index.tsx +26 -0
  62. package/src/app/(main)/profile/stats/features/TimeLabel.tsx +30 -0
  63. package/src/app/(main)/profile/stats/features/TopicsRank.tsx +103 -0
  64. package/src/app/(main)/profile/stats/features/TotalAssistants.tsx +56 -0
  65. package/src/app/(main)/profile/stats/features/TotalMessages.tsx +56 -0
  66. package/src/app/(main)/profile/stats/features/TotalTopics.tsx +53 -0
  67. package/src/app/(main)/profile/stats/features/TotalWords.tsx +54 -0
  68. package/src/app/(main)/profile/stats/features/Welcome.tsx +86 -0
  69. package/src/app/(main)/profile/{[[...slugs]] → stats}/page.tsx +4 -5
  70. package/src/app/(main)/repos/[id]/evals/dataset/page.tsx +2 -2
  71. package/src/app/(main)/repos/[id]/evals/evaluation/page.tsx +2 -2
  72. package/src/app/(main)/settings/@category/features/CategoryContent.tsx +1 -1
  73. package/src/app/(main)/settings/_layout/Desktop/index.tsx +1 -1
  74. package/src/app/(main)/settings/_layout/Mobile/Header.tsx +1 -1
  75. package/src/app/(main)/settings/_layout/Mobile/index.tsx +2 -0
  76. package/src/app/(main)/settings/common/features/Theme/index.tsx +2 -17
  77. package/src/app/(main)/settings/loading.tsx +2 -2
  78. package/src/components/Loading/BrandTextLoading/index.tsx +2 -2
  79. package/src/components/Statistic/index.tsx +15 -0
  80. package/src/components/StatisticCard/TitleWithPercentage.tsx +80 -0
  81. package/src/components/StatisticCard/growthPercentage.tsx +8 -0
  82. package/src/components/StatisticCard/index.tsx +209 -0
  83. package/src/const/url.ts +3 -3
  84. package/src/database/server/models/__tests__/message.test.ts +346 -35
  85. package/src/database/server/models/__tests__/session.test.ts +185 -2
  86. package/src/database/server/models/__tests__/topic.test.ts +136 -0
  87. package/src/database/server/models/__tests__/user.test.ts +140 -1
  88. package/src/database/server/models/message.ts +109 -14
  89. package/src/database/server/models/session.ts +75 -4
  90. package/src/database/server/models/topic.ts +43 -3
  91. package/src/database/server/models/user.ts +22 -0
  92. package/src/database/utils/genWhere.ts +39 -0
  93. package/src/features/ShareModal/ShareImage/index.tsx +11 -24
  94. package/src/features/ShareModal/ShareImage/type.ts +1 -6
  95. package/src/features/User/DataStatistics.tsx +21 -14
  96. package/src/features/User/UserPanel/PanelContent.tsx +12 -16
  97. package/src/features/User/UserPanel/useMenu.tsx +4 -6
  98. package/src/features/User/__tests__/PanelContent.test.tsx +4 -0
  99. package/src/features/User/__tests__/useMenu.test.tsx +1 -21
  100. package/src/hooks/useActiveTabKey.ts +34 -1
  101. package/src/{features/ShareModal/ShareImage → hooks}/useScreenshot.ts +51 -6
  102. package/src/locales/default/auth.ts +74 -2
  103. package/src/server/ld.test.ts +1 -1
  104. package/src/server/modules/AssistantStore/index.ts +3 -2
  105. package/src/server/routers/lambda/message.ts +35 -6
  106. package/src/server/routers/lambda/session.ts +17 -3
  107. package/src/server/routers/lambda/topic.ts +17 -3
  108. package/src/server/routers/lambda/user.ts +4 -0
  109. package/src/server/services/changelog/index.ts +1 -1
  110. package/src/services/message/_deprecated.ts +16 -0
  111. package/src/services/message/client.test.ts +0 -18
  112. package/src/services/message/client.ts +12 -9
  113. package/src/services/message/server.ts +12 -4
  114. package/src/services/message/type.ts +15 -3
  115. package/src/services/session/_deprecated.ts +5 -0
  116. package/src/services/session/client.ts +6 -2
  117. package/src/services/session/server.ts +6 -2
  118. package/src/services/session/type.ts +7 -1
  119. package/src/services/topic/_deprecated.ts +5 -0
  120. package/src/services/topic/client.ts +6 -2
  121. package/src/services/topic/server.ts +7 -1
  122. package/src/services/topic/type.ts +7 -2
  123. package/src/services/user/_deprecated.ts +4 -0
  124. package/src/services/user/client.ts +4 -0
  125. package/src/services/user/server.ts +4 -0
  126. package/src/services/user/type.ts +5 -0
  127. package/src/store/global/initialState.ts +6 -0
  128. package/src/store/user/slices/auth/action.test.ts +1 -33
  129. package/src/store/user/slices/auth/action.ts +0 -9
  130. package/src/store/user/slices/common/action.test.ts +2 -2
  131. package/src/types/message/index.ts +5 -0
  132. package/src/types/session/index.ts +8 -0
  133. package/src/types/topic/topic.ts +7 -0
  134. package/src/utils/format.ts +1 -1
  135. package/src/utils/time.ts +23 -0
  136. package/src/app/(main)/profile/[[...slugs]]/Client.tsx +0 -76
  137. package/src/components/Loading/BrandTextLoading/LobeChatText/SVG.tsx +0 -44
  138. package/src/components/Loading/BrandTextLoading/LobeChatText/index.tsx +0 -6
  139. package/src/components/Loading/BrandTextLoading/LobeChatText/style.css +0 -32
  140. package/src/hooks/useActiveSettingsKey.ts +0 -20
@@ -1,8 +1,80 @@
1
1
  export default {
2
+ date: {
3
+ prevMonth: '上个月',
4
+ recent30Days: '最近30天',
5
+ },
6
+ header: {
7
+ desc: '管理您的账户信息。',
8
+ title: '账户',
9
+ },
10
+ heatmaps: {
11
+ legend: {
12
+ less: '不活跃',
13
+ more: '活跃',
14
+ },
15
+ months: {
16
+ apr: '四月',
17
+ aug: '八月',
18
+ dec: '十二月',
19
+ feb: '二月',
20
+ jan: '一月',
21
+ jul: '七月',
22
+ jun: '六月',
23
+ mar: '三月',
24
+ may: '五月',
25
+ nov: '十一月',
26
+ oct: '十月',
27
+ sep: '九月',
28
+ },
29
+ tooltip: '{{date}} 当日发送 {{count}} 条消息',
30
+ totalCount: '过去一年共发送 {{count}} 条消息',
31
+ },
2
32
  login: '登录',
3
33
  loginOrSignup: '登录 / 注册',
4
- profile: '个人资料',
5
- security: '安全',
34
+ profile: {
35
+ avatar: '头像',
36
+ email: '电子邮件地址',
37
+ username: '用户名',
38
+ },
6
39
  signout: '退出登录',
7
40
  signup: '注册',
41
+ stats: {
42
+ aiheatmaps: 'AI 指数',
43
+ assistants: '助手数',
44
+ assistantsRank: {
45
+ left: '助手名称',
46
+ right: '话题数',
47
+ title: '助手使用率',
48
+ },
49
+ createdAt: '用户创建于',
50
+ days: '天',
51
+ empty: {
52
+ desc: '请积累更多聊天数据后查看',
53
+ title: '暂无数据',
54
+ },
55
+ lastYearActivity: '过去一年活跃度',
56
+ messages: '消息数',
57
+ modelsRank: {
58
+ left: '模型名称',
59
+ right: '消息数',
60
+ title: '模型使用率',
61
+ },
62
+ share: {
63
+ title: '我的 AI 活跃指数',
64
+ },
65
+ topics: '话题数',
66
+ topicsRank: {
67
+ left: '话题名称',
68
+ right: '消息数',
69
+ title: '话题内容量',
70
+ },
71
+ updatedAt: '数据更新至',
72
+ welcome: '{{username}}, 这是你和 {{appName}} 相伴的第 <span>{{days}}</span> 天',
73
+ words: '累计字数',
74
+ },
75
+ tab: {
76
+ profile: '个人资料',
77
+ security: '安全',
78
+ stats: '数据统计',
79
+ },
8
80
  };
@@ -29,7 +29,7 @@ describe('Ld', () => {
29
29
 
30
30
  expect(org['@type']).toBe('Organization');
31
31
  expect(org.name).toBe('LobeHub');
32
- expect(org.url).toBe('https://lobehub.com/');
32
+ expect(org.url).toBe('https://lobehub.com');
33
33
  });
34
34
  });
35
35
 
@@ -36,7 +36,7 @@ export class AssistantStore {
36
36
  }
37
37
 
38
38
  if (!res.ok) {
39
- console.error('fetch agent index error:', await res.text());
39
+ console.warn('fetch agent index error:', await res.text());
40
40
  return [];
41
41
  }
42
42
 
@@ -55,7 +55,8 @@ export class AssistantStore {
55
55
 
56
56
  return data;
57
57
  } catch (e) {
58
- console.error('fetch agent index error:', e);
58
+ console.error('[AgentIndexFetchError] failed to fetch agent index, error detail:');
59
+ console.error(e);
59
60
 
60
61
  throw e;
61
62
  }
@@ -27,12 +27,33 @@ export const messageRouter = router({
27
27
  return { added: data.rowCount as number, ids: [], skips: [], success: true };
28
28
  }),
29
29
 
30
- count: messageProcedure.query(async ({ ctx }) => {
31
- return ctx.messageModel.count();
32
- }),
33
- countToday: messageProcedure.query(async ({ ctx }) => {
34
- return ctx.messageModel.countToday();
35
- }),
30
+ count: messageProcedure
31
+ .input(
32
+ z
33
+ .object({
34
+ endDate: z.string().optional(),
35
+ range: z.tuple([z.string(), z.string()]).optional(),
36
+ startDate: z.string().optional(),
37
+ })
38
+ .optional(),
39
+ )
40
+ .query(async ({ ctx, input }) => {
41
+ return ctx.messageModel.count(input);
42
+ }),
43
+
44
+ countWords: messageProcedure
45
+ .input(
46
+ z
47
+ .object({
48
+ endDate: z.string().optional(),
49
+ range: z.tuple([z.string(), z.string()]).optional(),
50
+ startDate: z.string().optional(),
51
+ })
52
+ .optional(),
53
+ )
54
+ .query(async ({ ctx, input }) => {
55
+ return ctx.messageModel.countWords(input);
56
+ }),
36
57
 
37
58
  createMessage: messageProcedure
38
59
  .input(z.object({}).passthrough().partial())
@@ -56,6 +77,10 @@ export const messageRouter = router({
56
77
  return ctx.messageModel.queryBySessionId(input.sessionId);
57
78
  }),
58
79
 
80
+ getHeatmaps: messageProcedure.query(async ({ ctx }) => {
81
+ return ctx.messageModel.getHeatmaps();
82
+ }),
83
+
59
84
  // TODO: 未来这部分方法也需要使用 authedProcedure
60
85
  getMessages: publicProcedure
61
86
  .input(
@@ -74,6 +99,10 @@ export const messageRouter = router({
74
99
  return messageModel.query(input, { postProcessUrl: (path) => getFullFileUrl(path) });
75
100
  }),
76
101
 
102
+ rankModels: messageProcedure.query(async ({ ctx }) => {
103
+ return ctx.messageModel.rankModels();
104
+ }),
105
+
77
106
  removeAllMessages: messageProcedure.mutation(async ({ ctx }) => {
78
107
  return ctx.messageModel.deleteAllMessages();
79
108
  }),
@@ -57,9 +57,19 @@ export const sessionRouter = router({
57
57
  return data?.id;
58
58
  }),
59
59
 
60
- countSessions: sessionProcedure.query(async ({ ctx }) => {
61
- return ctx.sessionModel.count();
62
- }),
60
+ countSessions: sessionProcedure
61
+ .input(
62
+ z
63
+ .object({
64
+ endDate: z.string().optional(),
65
+ range: z.tuple([z.string(), z.string()]).optional(),
66
+ startDate: z.string().optional(),
67
+ })
68
+ .optional(),
69
+ )
70
+ .query(async ({ ctx, input }) => {
71
+ return ctx.sessionModel.count(input);
72
+ }),
63
73
 
64
74
  createSession: sessionProcedure
65
75
  .input(
@@ -103,6 +113,10 @@ export const sessionRouter = router({
103
113
  return ctx.sessionModel.query({ current, pageSize });
104
114
  }),
105
115
 
116
+ rankSessions: sessionProcedure.input(z.number().optional()).query(async ({ ctx, input }) => {
117
+ return ctx.sessionModel.rank(input);
118
+ }),
119
+
106
120
  removeAllSessions: sessionProcedure.mutation(async ({ ctx }) => {
107
121
  return ctx.sessionModel.deleteAll();
108
122
  }),
@@ -56,9 +56,19 @@ export const topicRouter = router({
56
56
  return data.topic.id;
57
57
  }),
58
58
 
59
- countTopics: topicProcedure.query(async ({ ctx }) => {
60
- return ctx.topicModel.count();
61
- }),
59
+ countTopics: topicProcedure
60
+ .input(
61
+ z
62
+ .object({
63
+ endDate: z.string().optional(),
64
+ range: z.tuple([z.string(), z.string()]).optional(),
65
+ startDate: z.string().optional(),
66
+ })
67
+ .optional(),
68
+ )
69
+ .query(async ({ ctx, input }) => {
70
+ return ctx.topicModel.count(input);
71
+ }),
62
72
 
63
73
  createTopic: topicProcedure
64
74
  .input(
@@ -100,6 +110,10 @@ export const topicRouter = router({
100
110
  return (await ctx.topicModel.count()) === 0;
101
111
  }),
102
112
 
113
+ rankTopics: topicProcedure.input(z.number().optional()).query(async ({ ctx, input }) => {
114
+ return ctx.topicModel.rank(input);
115
+ }),
116
+
103
117
  removeAllTopics: topicProcedure.mutation(async ({ ctx }) => {
104
118
  return ctx.topicModel.deleteAll();
105
119
  }),
@@ -20,6 +20,10 @@ const userProcedure = authedProcedure.use(async (opts) => {
20
20
  });
21
21
 
22
22
  export const userRouter = router({
23
+ getUserRegistrationDuration: userProcedure.query(async ({ ctx }) => {
24
+ return ctx.userModel.getUserRegistrationDuration();
25
+ }),
26
+
23
27
  getUserState: userProcedure.query(async ({ ctx }): Promise<UserInitializationState> => {
24
28
  let state: Awaited<ReturnType<UserModel['getUserState']>> | undefined;
25
29
 
@@ -54,7 +54,7 @@ export class ChangelogService {
54
54
  return this.mergeChangelogs(data.cloud, data.community).slice(0, 5);
55
55
  } catch (e) {
56
56
  const cause = (e as Error).cause as { code: string };
57
- if (cause.code.includes('ETIMEDOUT')) {
57
+ if (cause?.code.includes('ETIMEDOUT')) {
58
58
  console.warn(
59
59
  '[ChangelogFetchTimeout] fail to fetch changelog lists due to network timeout. Please check your network connection.',
60
60
  );
@@ -10,6 +10,7 @@ import {
10
10
  ChatTTS,
11
11
  ChatTranslate,
12
12
  CreateMessageParams,
13
+ ModelRankItem,
13
14
  } from '@/types/message';
14
15
 
15
16
  import { IMessageService } from './type';
@@ -56,6 +57,21 @@ export class ClientService implements IMessageService {
56
57
  return MessageModel.count();
57
58
  }
58
59
 
60
+ // @ts-ignore
61
+ async rankModels(): Promise<ModelRankItem[]> {
62
+ throw new Error('Method not implemented.');
63
+ }
64
+
65
+ // @ts-ignore
66
+ async countWords(): Promise<number> {
67
+ throw new Error('Method not implemented.');
68
+ }
69
+
70
+ // @ts-ignore
71
+ async getHeatmaps() {
72
+ throw new Error('Method not implemented.');
73
+ }
74
+
59
75
  async countTodayMessages() {
60
76
  const topics = await MessageModel.queryAll();
61
77
  return topics.filter(
@@ -350,24 +350,6 @@ describe('MessageClientService', () => {
350
350
  });
351
351
  });
352
352
 
353
- describe('countTodayMessages', () => {
354
- it('should count the number of messages created today', async () => {
355
- // Setup
356
- const mockMessages = [
357
- { ...mockMessage, id: undefined, createdAt: new Date(), userId },
358
- { ...mockMessage, id: undefined, createdAt: new Date(), userId },
359
- { ...mockMessage, id: undefined, createdAt: new Date('2023-01-01'), userId },
360
- ];
361
- await clientDB.insert(messages).values(mockMessages);
362
-
363
- // Execute
364
- const count = await messageService.countTodayMessages();
365
-
366
- // Assert
367
- expect(count).toBe(2);
368
- });
369
- });
370
-
371
353
  describe('updateMessageTTS', () => {
372
354
  it('should update the TTS field of a message', async () => {
373
355
  // Setup
@@ -1,5 +1,3 @@
1
- import dayjs from 'dayjs';
2
-
3
1
  import { INBOX_SESSION_ID } from '@/const/session';
4
2
  import { clientDB } from '@/database/client/db';
5
3
  import { MessageModel } from '@/database/server/models/message';
@@ -52,15 +50,20 @@ export class ClientService extends BaseClientService implements IMessageService
52
50
  return data as unknown as ChatMessage[];
53
51
  };
54
52
 
55
- countMessages: IMessageService['countMessages'] = async () => {
56
- return this.messageModel.count();
53
+ countMessages: IMessageService['countMessages'] = async (params) => {
54
+ return this.messageModel.count(params);
55
+ };
56
+
57
+ countWords: IMessageService['countWords'] = async (params) => {
58
+ return this.messageModel.countWords(params);
59
+ };
60
+
61
+ rankModels: IMessageService['rankModels'] = async () => {
62
+ return this.messageModel.rankModels();
57
63
  };
58
64
 
59
- countTodayMessages: IMessageService['countTodayMessages'] = async () => {
60
- const topics = await this.messageModel.queryAll();
61
- return topics.filter(
62
- (item) => dayjs(item.createdAt).format('YYYY-MM-DD') === dayjs().format('YYYY-MM-DD'),
63
- ).length;
65
+ getHeatmaps: IMessageService['getHeatmaps'] = async () => {
66
+ return this.messageModel.getHeatmaps();
64
67
  };
65
68
 
66
69
  getAllMessagesInSession: IMessageService['getAllMessagesInSession'] = async (sessionId) => {
@@ -36,12 +36,20 @@ export class ServerService implements IMessageService {
36
36
  });
37
37
  };
38
38
 
39
- countMessages: IMessageService['countMessages'] = async () => {
40
- return lambdaClient.message.count.query();
39
+ countMessages: IMessageService['countMessages'] = async (params) => {
40
+ return lambdaClient.message.count.query(params);
41
41
  };
42
42
 
43
- countTodayMessages: IMessageService['countTodayMessages'] = async () => {
44
- return lambdaClient.message.countToday.query();
43
+ countWords: IMessageService['countWords'] = async (params) => {
44
+ return lambdaClient.message.countWords.query(params);
45
+ };
46
+
47
+ rankModels: IMessageService['rankModels'] = async () => {
48
+ return lambdaClient.message.rankModels.query();
49
+ };
50
+
51
+ getHeatmaps: IMessageService['getHeatmaps'] = async () => {
52
+ return lambdaClient.message.getHeatmaps.query();
45
53
  };
46
54
 
47
55
  updateMessageError: IMessageService['updateMessageError'] = async (id, error) => {
@@ -1,3 +1,5 @@
1
+ import type { HeatmapsProps } from '@lobehub/charts';
2
+
1
3
  import { MessageItem } from '@/database/schemas';
2
4
  import {
3
5
  ChatMessage,
@@ -5,6 +7,7 @@ import {
5
7
  ChatTTS,
6
8
  ChatTranslate,
7
9
  CreateMessageParams,
10
+ ModelRankItem,
8
11
  } from '@/types/message';
9
12
 
10
13
  /* eslint-disable typescript-sort-keys/interface */
@@ -16,9 +19,18 @@ export interface IMessageService {
16
19
  getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]>;
17
20
  getAllMessages(): Promise<ChatMessage[]>;
18
21
  getAllMessagesInSession(sessionId: string): Promise<ChatMessage[]>;
19
- countMessages(): Promise<number>;
20
- countTodayMessages(): Promise<number>;
21
-
22
+ countMessages(params?: {
23
+ endDate?: string;
24
+ range?: [string, string];
25
+ startDate?: string;
26
+ }): Promise<number>;
27
+ countWords(params?: {
28
+ endDate?: string;
29
+ range?: [string, string];
30
+ startDate?: string;
31
+ }): Promise<number>;
32
+ rankModels(): Promise<ModelRankItem[]>;
33
+ getHeatmaps(): Promise<HeatmapsProps['data']>;
22
34
  updateMessageError(id: string, error: ChatMessageError): Promise<any>;
23
35
  updateMessage(id: string, message: Partial<MessageItem>): Promise<any>;
24
36
  updateMessageTTS(id: string, tts: Partial<ChatTTS> | false): Promise<any>;
@@ -82,6 +82,11 @@ export class ClientService implements ISessionService {
82
82
  return SessionModel.count();
83
83
  }
84
84
 
85
+ // @ts-ignore
86
+ async rankSessions() {
87
+ throw new Error('Method not implemented.');
88
+ }
89
+
85
90
  async hasSessions() {
86
91
  return (await this.countSessions()) !== 0;
87
92
  }
@@ -74,8 +74,12 @@ export class ClientService extends BaseClientService implements ISessionService
74
74
  }
75
75
  };
76
76
 
77
- countSessions: ISessionService['countSessions'] = async () => {
78
- return this.sessionModel.count();
77
+ countSessions: ISessionService['countSessions'] = async (params) => {
78
+ return this.sessionModel.count(params);
79
+ };
80
+
81
+ rankSessions: ISessionService['rankSessions'] = async (limit) => {
82
+ return this.sessionModel.rank(limit);
79
83
  };
80
84
 
81
85
  searchSessions: ISessionService['searchSessions'] = async (keyword) => {
@@ -36,8 +36,12 @@ export class ServerService implements ISessionService {
36
36
  return lambdaClient.session.getGroupedSessions.query();
37
37
  };
38
38
 
39
- countSessions: ISessionService['countSessions'] = () => {
40
- return lambdaClient.session.countSessions.query();
39
+ countSessions: ISessionService['countSessions'] = async (params) => {
40
+ return lambdaClient.session.countSessions.query(params);
41
+ };
42
+
43
+ rankSessions: ISessionService['rankSessions'] = async (limit) => {
44
+ return lambdaClient.session.rankSessions.query(limit);
41
45
  };
42
46
 
43
47
  updateSession: ISessionService['updateSession'] = (id, data) => {
@@ -11,6 +11,7 @@ import {
11
11
  LobeSessions,
12
12
  SessionGroupItem,
13
13
  SessionGroups,
14
+ SessionRankItem,
14
15
  UpdateSessionParams,
15
16
  } from '@/types/session';
16
17
 
@@ -31,7 +32,12 @@ export interface ISessionService {
31
32
  * @deprecated
32
33
  */
33
34
  getSessionsByType(type?: 'agent' | 'group' | 'all'): Promise<LobeSessions>;
34
- countSessions(): Promise<number>;
35
+ countSessions(params?: {
36
+ endDate?: string;
37
+ range?: [string, string];
38
+ startDate?: string;
39
+ }): Promise<number>;
40
+ rankSessions(limit?: number): Promise<SessionRankItem[]>;
35
41
  searchSessions(keyword: string): Promise<LobeSessions>;
36
42
 
37
43
  updateSession(id: string, data: Partial<UpdateSessionParams>): Promise<any>;
@@ -38,6 +38,11 @@ export class ClientService implements ITopicService {
38
38
  return TopicModel.count();
39
39
  }
40
40
 
41
+ // @ts-ignore
42
+ async rankTopics() {
43
+ throw new Error('Method not implemented.');
44
+ }
45
+
41
46
  async updateTopicFavorite(id: string, favorite?: boolean) {
42
47
  return this.updateTopic(id, { favorite });
43
48
  }
@@ -55,8 +55,12 @@ export class ClientService extends BaseClientService implements ITopicService {
55
55
  return data as unknown as Promise<ChatTopic[]>;
56
56
  };
57
57
 
58
- countTopics: ITopicService['countTopics'] = async () => {
59
- return this.topicModel.count();
58
+ countTopics: ITopicService['countTopics'] = async (params) => {
59
+ return this.topicModel.count(params);
60
+ };
61
+
62
+ rankTopics: ITopicService['rankTopics'] = async (limit) => {
63
+ return this.topicModel.rank(limit);
60
64
  };
61
65
 
62
66
  updateTopic: ITopicService['updateTopic'] = async (id, data) => {
@@ -24,7 +24,13 @@ export class ServerService implements ITopicService {
24
24
  getAllTopics: ITopicService['getAllTopics'] = () =>
25
25
  lambdaClient.topic.getAllTopics.query() as any;
26
26
 
27
- countTopics: ITopicService['countTopics'] = async () => lambdaClient.topic.countTopics.query();
27
+ countTopics: ITopicService['countTopics'] = async (params) => {
28
+ return lambdaClient.topic.countTopics.query(params);
29
+ };
30
+
31
+ rankTopics: ITopicService['rankTopics'] = async (limit) => {
32
+ return lambdaClient.topic.rankTopics.query(limit);
33
+ };
28
34
 
29
35
  searchTopics: ITopicService['searchTopics'] = (keywords, sessionId) =>
30
36
  lambdaClient.topic.searchTopics.query({
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable typescript-sort-keys/interface */
2
2
  import { BatchTaskResult } from '@/types/service';
3
- import { ChatTopic } from '@/types/topic';
3
+ import { ChatTopic, TopicRankItem } from '@/types/topic';
4
4
 
5
5
  export interface CreateTopicParams {
6
6
  favorite?: boolean;
@@ -22,7 +22,12 @@ export interface ITopicService {
22
22
 
23
23
  getTopics(params: QueryTopicParams): Promise<ChatTopic[]>;
24
24
  getAllTopics(): Promise<ChatTopic[]>;
25
- countTopics(): Promise<number>;
25
+ countTopics(params?: {
26
+ endDate?: string;
27
+ range?: [string, string];
28
+ startDate?: string;
29
+ }): Promise<number>;
30
+ rankTopics(limit?: number): Promise<TopicRankItem[]>;
26
31
  searchTopics(keyword: string, sessionId?: string): Promise<ChatTopic[]>;
27
32
 
28
33
  updateTopic(id: string, data: Partial<ChatTopic>): Promise<any>;
@@ -16,6 +16,10 @@ export class ClientService implements IUserService {
16
16
  this.preferenceStorage = new AsyncLocalStorage('LOBE_PREFERENCE');
17
17
  }
18
18
 
19
+ getUserRegistrationDuration = async () => {
20
+ throw new Error('Method not implemented.');
21
+ };
22
+
19
23
  async getUserState(): Promise<UserInitializationState> {
20
24
  const user = await UserModel.getUser();
21
25
  const messageCount = await MessageModel.count();
@@ -27,6 +27,10 @@ export class ClientService extends BaseClientService implements IUserService {
27
27
  this.preferenceStorage = new AsyncLocalStorage('LOBE_PREFERENCE');
28
28
  }
29
29
 
30
+ getUserRegistrationDuration: IUserService['getUserRegistrationDuration'] = async () => {
31
+ return this.userModel.getUserRegistrationDuration();
32
+ };
33
+
30
34
  getUserState: IUserService['getUserState'] = async () => {
31
35
  // if user not exist in the db, create one to make sure the user exist
32
36
  await this.makeSureUserExist();
@@ -2,6 +2,10 @@ import { lambdaClient } from '@/libs/trpc/client';
2
2
  import { IUserService } from '@/services/user/type';
3
3
 
4
4
  export class ServerService implements IUserService {
5
+ getUserRegistrationDuration: IUserService['getUserRegistrationDuration'] = async () => {
6
+ return lambdaClient.user.getUserRegistrationDuration.query();
7
+ };
8
+
5
9
  getUserState: IUserService['getUserState'] = async () => {
6
10
  return lambdaClient.user.getUserState.query();
7
11
  };
@@ -4,6 +4,11 @@ import { UserGuide, UserInitializationState, UserPreference } from '@/types/user
4
4
  import { UserSettings } from '@/types/user/settings';
5
5
 
6
6
  export interface IUserService {
7
+ getUserRegistrationDuration: () => Promise<{
8
+ createdAt: string;
9
+ duration: number;
10
+ updatedAt: string;
11
+ }>;
7
12
  getUserState: () => Promise<UserInitializationState>;
8
13
  resetUserSettings: () => Promise<any>;
9
14
  updateGuide: (guide: Partial<UserGuide>) => Promise<any>;
@@ -31,6 +31,12 @@ export enum SettingsTabs {
31
31
  TTS = 'tts',
32
32
  }
33
33
 
34
+ export enum ProfileTabs {
35
+ Profile = 'profile',
36
+ Security = 'security',
37
+ Stats = 'stats',
38
+ }
39
+
34
40
  export interface SystemStatus {
35
41
  // which sessionGroup should expand
36
42
  expandSessionGroupKeys: string[];
@@ -1,11 +1,8 @@
1
- import { act, renderHook, waitFor } from '@testing-library/react';
1
+ import { act, renderHook } from '@testing-library/react';
2
2
  import { mutate } from 'swr';
3
3
  import { afterEach, describe, expect, it, vi } from 'vitest';
4
- import { withSWR } from '~test-utils';
5
4
 
6
- import { userService } from '@/services/user';
7
5
  import { useUserStore } from '@/store/user';
8
- import { switchLang } from '@/utils/client/switchLang';
9
6
 
10
7
  vi.mock('zustand/traditional');
11
8
 
@@ -170,33 +167,4 @@ describe('createAuthSlice', () => {
170
167
  expect(signIn).not.toHaveBeenCalled();
171
168
  });
172
169
  });
173
-
174
- describe('openUserProfile', () => {
175
- it('should call clerkOpenUserProfile when Clerk is enabled', async () => {
176
- enableClerk = true;
177
-
178
- const clerkOpenUserProfileMock = vi.fn();
179
- useUserStore.setState({ clerkOpenUserProfile: clerkOpenUserProfileMock });
180
-
181
- const { result } = renderHook(() => useUserStore());
182
-
183
- await act(async () => {
184
- await result.current.openUserProfile();
185
- });
186
-
187
- expect(clerkOpenUserProfileMock).toHaveBeenCalled();
188
- });
189
- it('should not call clerkOpenUserProfile when Clerk is disabled', async () => {
190
- const clerkOpenUserProfileMock = vi.fn();
191
- useUserStore.setState({ clerkOpenUserProfile: clerkOpenUserProfileMock });
192
-
193
- const { result } = renderHook(() => useUserStore());
194
-
195
- await act(async () => {
196
- await result.current.openUserProfile();
197
- });
198
-
199
- expect(clerkOpenUserProfileMock).not.toHaveBeenCalled();
200
- });
201
- });
202
170
  });