@lobehub/chat 1.77.5 → 1.77.7

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 (73) hide show
  1. package/.github/scripts/pr-comment.js +80 -0
  2. package/.github/scripts/pr-release-body.js +59 -0
  3. package/.github/workflows/release-desktop.yml +331 -0
  4. package/.github/workflows/test.yml +1 -1
  5. package/CHANGELOG.md +50 -0
  6. package/changelog/v1.json +18 -0
  7. package/next.config.ts +16 -11
  8. package/package.json +92 -89
  9. package/packages/electron-client-ipc/README.md +48 -0
  10. package/packages/electron-client-ipc/package.json +7 -0
  11. package/packages/electron-client-ipc/src/events/devtools.ts +6 -0
  12. package/packages/electron-client-ipc/src/events/index.ts +13 -0
  13. package/packages/electron-client-ipc/src/index.ts +2 -0
  14. package/packages/electron-client-ipc/src/types/dispatch.ts +10 -0
  15. package/packages/electron-client-ipc/src/types/index.ts +1 -0
  16. package/packages/electron-server-ipc/README.md +1 -1
  17. package/pnpm-workspace.yaml +1 -0
  18. package/scripts/setup-test-postgres-db.sh +21 -0
  19. package/src/app/desktop/devtools/page.tsx +89 -0
  20. package/src/app/desktop/layout.tsx +31 -0
  21. package/src/app/layout.tsx +11 -0
  22. package/src/app/not-found.tsx +1 -0
  23. package/src/const/desktop.ts +1 -0
  24. package/src/const/version.ts +2 -0
  25. package/src/database/client/db.ts +3 -10
  26. package/src/database/core/db-adaptor.ts +20 -2
  27. package/src/database/models/__tests__/aiProvider.test.ts +2 -0
  28. package/src/database/models/__tests__/message.test.ts +97 -26
  29. package/src/database/models/__tests__/session.test.ts +2 -0
  30. package/src/database/models/drizzleMigration.ts +15 -0
  31. package/src/database/models/message.ts +10 -5
  32. package/src/database/models/session.ts +8 -0
  33. package/src/database/models/user.ts +10 -1
  34. package/src/database/server/index.ts +1 -1
  35. package/src/features/DevPanel/features/FloatPanel.tsx +23 -6
  36. package/src/features/User/UserPanel/index.tsx +10 -6
  37. package/src/libs/trpc/async/index.ts +11 -1
  38. package/src/libs/trpc/lambda/index.ts +1 -0
  39. package/src/libs/trpc/lambda/serverDatabase.ts +10 -0
  40. package/src/libs/trpc/middleware/userAuth.ts +10 -0
  41. package/src/server/routers/async/file.ts +4 -5
  42. package/src/server/routers/async/ragEval.ts +3 -4
  43. package/src/server/routers/lambda/_template.ts +3 -5
  44. package/src/server/routers/lambda/agent.ts +8 -8
  45. package/src/server/routers/lambda/aiModel.ts +5 -5
  46. package/src/server/routers/lambda/aiProvider.test.ts +0 -2
  47. package/src/server/routers/lambda/aiProvider.ts +5 -5
  48. package/src/server/routers/lambda/chunk.ts +18 -15
  49. package/src/server/routers/lambda/exporter.ts +4 -4
  50. package/src/server/routers/lambda/file.ts +5 -5
  51. package/src/server/routers/lambda/importer.ts +3 -3
  52. package/src/server/routers/lambda/knowledgeBase.ts +3 -3
  53. package/src/server/routers/lambda/message.ts +5 -3
  54. package/src/server/routers/lambda/plugin.ts +5 -3
  55. package/src/server/routers/lambda/ragEval.ts +17 -14
  56. package/src/server/routers/lambda/session.ts +6 -4
  57. package/src/server/routers/lambda/sessionGroup.ts +3 -3
  58. package/src/server/routers/lambda/thread.ts +4 -4
  59. package/src/server/routers/lambda/topic.ts +5 -3
  60. package/src/server/routers/lambda/user.ts +7 -7
  61. package/src/server/routers/tools/__tests__/search.test.ts +1 -0
  62. package/src/server/services/nextAuthUser/index.test.ts +1 -2
  63. package/src/server/translation.test.ts +72 -52
  64. package/src/server/translation.ts +2 -11
  65. package/src/services/electron/devtools.ts +9 -0
  66. package/src/styles/electron.ts +14 -0
  67. package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/index.tsx +3 -8
  68. package/src/tools/web-browsing/Render/Search/SearchResult/ShowMore.tsx +2 -4
  69. package/src/types/electron.ts +11 -0
  70. package/src/utils/electron/dispatch.ts +10 -0
  71. package/tsconfig.json +6 -6
  72. package/vitest.config.ts +3 -1
  73. package/vitest.server.config.ts +7 -3
@@ -1,6 +1,24 @@
1
- // import { isDesktop } from '@/const/version';
2
1
  import { getDBInstance } from '@/database/core/web-server';
2
+ import { LobeChatDatabase } from '@/database/type';
3
3
 
4
- // import { getPgliteInstance } from './electron';
4
+ /**
5
+ * 懒加载数据库实例
6
+ * 避免每次模块导入时都初始化数据库
7
+ */
8
+ let cachedDB: LobeChatDatabase | null = null;
9
+
10
+ export const getServerDB = async (): Promise<LobeChatDatabase> => {
11
+ // 如果已经有缓存的实例,直接返回
12
+ if (cachedDB) return cachedDB;
13
+
14
+ try {
15
+ // 根据环境选择合适的数据库实例
16
+ cachedDB = getDBInstance();
17
+ return cachedDB;
18
+ } catch (error) {
19
+ console.error('❌ Failed to initialize database:', error);
20
+ throw error;
21
+ }
22
+ };
5
23
 
6
24
  export const serverDB = getDBInstance();
@@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
5
5
  import { LobeChatDatabase } from '@/database/type';
6
6
  import { ModelProvider } from '@/libs/agent-runtime';
7
+ import { sleep } from '@/utils/sleep';
7
8
 
8
9
  import { aiProviders, users } from '../../schemas';
9
10
  import { AiProviderModel } from '../aiProvider';
@@ -96,6 +97,7 @@ describe('AiProviderModel', () => {
96
97
  describe('query', () => {
97
98
  it('should query ai providers for the user', async () => {
98
99
  await aiProviderModel.create({ name: 'AiHubMix', source: 'custom', id: 'aihubmix' });
100
+ await sleep(10);
99
101
  await aiProviderModel.create({ name: 'AiHubMix', source: 'custom', id: 'aihubmix-2' });
100
102
 
101
103
  const userGroups = await aiProviderModel.query();
@@ -1660,7 +1660,15 @@ describe('MessageModel', () => {
1660
1660
 
1661
1661
  describe('getHeatmaps', () => {
1662
1662
  it('should return heatmap data for the last year', async () => {
1663
- const today = dayjs();
1663
+ // 使用固定日期进行测试
1664
+ vi.useFakeTimers();
1665
+ const fixedDate = new Date('2023-04-07T13:00:00Z');
1666
+ vi.setSystemTime(fixedDate);
1667
+
1668
+ const today = dayjs(fixedDate);
1669
+ const twoDaysAgoDate = today.subtract(2, 'day').format('YYYY-MM-DD');
1670
+ const oneDayAgoDate = today.subtract(1, 'day').format('YYYY-MM-DD');
1671
+ const todayDate = today.format('YYYY-MM-DD');
1664
1672
 
1665
1673
  // 创建测试数据
1666
1674
  await serverDB.insert(messages).values([
@@ -1695,31 +1703,39 @@ describe('MessageModel', () => {
1695
1703
  expect(result.length).toBeLessThan(368);
1696
1704
 
1697
1705
  // 检查两天前的数据
1698
- const twoDaysAgo = result.find(
1699
- (item) => item.date === today.subtract(2, 'day').format('YYYY-MM-DD'),
1700
- );
1706
+ const twoDaysAgo = result.find((item) => item.date === twoDaysAgoDate);
1701
1707
  expect(twoDaysAgo?.count).toBe(2);
1702
1708
  expect(twoDaysAgo?.level).toBe(1);
1703
1709
 
1704
1710
  // 检查一天前的数据
1705
- const oneDayAgo = result.find(
1706
- (item) => item.date === today.subtract(1, 'day').format('YYYY-MM-DD'),
1707
- );
1711
+ const oneDayAgo = result.find((item) => item.date === oneDayAgoDate);
1708
1712
  expect(oneDayAgo?.count).toBe(1);
1709
1713
  expect(oneDayAgo?.level).toBe(1);
1710
1714
 
1711
1715
  // 检查今天的数据
1712
- const todayData = result.find((item) => item.date === today.format('YYYY-MM-DD'));
1716
+ const todayData = result.find((item) => item.date === todayDate);
1713
1717
  expect(todayData?.count).toBe(0);
1714
1718
  expect(todayData?.level).toBe(0);
1719
+
1720
+ vi.useRealTimers();
1715
1721
  });
1716
1722
 
1717
1723
  it('should calculate correct levels based on message count', async () => {
1718
- const today = dayjs();
1724
+ // 使用固定日期进行测试
1725
+ vi.useFakeTimers();
1726
+ const fixedDate = new Date('2023-05-15T12:00:00Z');
1727
+ vi.setSystemTime(fixedDate);
1728
+
1729
+ const today = dayjs(fixedDate);
1730
+ const fourDaysAgoDate = today.subtract(4, 'day').format('YYYY-MM-DD');
1731
+ const threeDaysAgoDate = today.subtract(3, 'day').format('YYYY-MM-DD');
1732
+ const twoDaysAgoDate = today.subtract(2, 'day').format('YYYY-MM-DD');
1733
+ const oneDayAgoDate = today.subtract(1, 'day').format('YYYY-MM-DD');
1734
+ const todayDate = today.format('YYYY-MM-DD');
1719
1735
 
1720
1736
  // 创建测试数据 - 不同数量的消息以测试不同的等级
1721
1737
  await serverDB.insert(messages).values([
1722
- // 1 message - level 0
1738
+ // 1 message - level 1
1723
1739
  {
1724
1740
  id: '1',
1725
1741
  userId,
@@ -1727,7 +1743,7 @@ describe('MessageModel', () => {
1727
1743
  content: 'message 1',
1728
1744
  createdAt: today.subtract(4, 'day').toDate(),
1729
1745
  },
1730
- // 6 messages - level 1
1746
+ // 6 messages - level 2
1731
1747
  ...Array(6)
1732
1748
  .fill(0)
1733
1749
  .map((_, i) => ({
@@ -1737,7 +1753,7 @@ describe('MessageModel', () => {
1737
1753
  content: `message 2-${i}`,
1738
1754
  createdAt: today.subtract(3, 'day').toDate(),
1739
1755
  })),
1740
- // 11 messages - level 2
1756
+ // 11 messages - level 3
1741
1757
  ...Array(11)
1742
1758
  .fill(0)
1743
1759
  .map((_, i) => ({
@@ -1747,7 +1763,7 @@ describe('MessageModel', () => {
1747
1763
  content: `message 3-${i}`,
1748
1764
  createdAt: today.subtract(2, 'day').toDate(),
1749
1765
  })),
1750
- // 16 messages - level 3
1766
+ // 16 messages - level 4
1751
1767
  ...Array(16)
1752
1768
  .fill(0)
1753
1769
  .map((_, i) => ({
@@ -1773,33 +1789,88 @@ describe('MessageModel', () => {
1773
1789
  const result = await messageModel.getHeatmaps();
1774
1790
 
1775
1791
  // 检查不同天数的等级
1776
- const fourDaysAgo = result.find(
1777
- (item) => item.date === today.subtract(4, 'day').format('YYYY-MM-DD'),
1778
- );
1792
+ const fourDaysAgo = result.find((item) => item.date === fourDaysAgoDate);
1779
1793
  expect(fourDaysAgo?.count).toBe(1);
1780
1794
  expect(fourDaysAgo?.level).toBe(1);
1781
1795
 
1782
- const threeDaysAgo = result.find(
1783
- (item) => item.date === today.subtract(3, 'day').format('YYYY-MM-DD'),
1784
- );
1796
+ const threeDaysAgo = result.find((item) => item.date === threeDaysAgoDate);
1785
1797
  expect(threeDaysAgo?.count).toBe(6);
1786
1798
  expect(threeDaysAgo?.level).toBe(2);
1787
1799
 
1788
- const twoDaysAgo = result.find(
1789
- (item) => item.date === today.subtract(2, 'day').format('YYYY-MM-DD'),
1790
- );
1800
+ const twoDaysAgo = result.find((item) => item.date === twoDaysAgoDate);
1791
1801
  expect(twoDaysAgo?.count).toBe(11);
1792
1802
  expect(twoDaysAgo?.level).toBe(3);
1793
1803
 
1794
- const oneDayAgo = result.find(
1795
- (item) => item.date === today.subtract(1, 'day').format('YYYY-MM-DD'),
1796
- );
1804
+ const oneDayAgo = result.find((item) => item.date === oneDayAgoDate);
1797
1805
  expect(oneDayAgo?.count).toBe(16);
1798
1806
  expect(oneDayAgo?.level).toBe(4);
1799
1807
 
1800
- const todayData = result.find((item) => item.date === today.format('YYYY-MM-DD'));
1808
+ const todayData = result.find((item) => item.date === todayDate);
1801
1809
  expect(todayData?.count).toBe(21);
1802
1810
  expect(todayData?.level).toBe(4);
1811
+
1812
+ vi.useRealTimers();
1813
+ });
1814
+
1815
+ it.skip('should return time count correctly when 19:00 time', async () => {
1816
+ // 使用固定日期进行测试
1817
+ vi.useFakeTimers();
1818
+ const fixedDate = new Date('2025-04-02T19:00:00Z');
1819
+ vi.setSystemTime(fixedDate);
1820
+
1821
+ const today = dayjs(fixedDate);
1822
+ const twoDaysAgoDate = today.subtract(2, 'day').format('YYYY-MM-DD');
1823
+ const oneDayAgoDate = today.subtract(1, 'day').format('YYYY-MM-DD');
1824
+ const todayDate = today.format('YYYY-MM-DD');
1825
+
1826
+ // 创建测试数据
1827
+ await serverDB.insert(messages).values([
1828
+ {
1829
+ id: '1',
1830
+ userId,
1831
+ role: 'user',
1832
+ content: 'message 1',
1833
+ createdAt: today.subtract(2, 'day').toDate(),
1834
+ },
1835
+ {
1836
+ id: '2',
1837
+ userId,
1838
+ role: 'user',
1839
+ content: 'message 2',
1840
+ createdAt: today.subtract(2, 'day').toDate(),
1841
+ },
1842
+ {
1843
+ id: '3',
1844
+ userId,
1845
+ role: 'user',
1846
+ content: 'message 3',
1847
+ createdAt: today.subtract(1, 'day').toDate(),
1848
+ },
1849
+ ]);
1850
+
1851
+ // 调用 getHeatmaps 方法
1852
+ const result = await messageModel.getHeatmaps();
1853
+
1854
+ // 断言结果
1855
+ expect(result.length).toBeGreaterThanOrEqual(366);
1856
+ expect(result.length).toBeLessThan(368);
1857
+
1858
+ // 检查两天前的数据
1859
+ const twoDaysAgo = result.find((item) => item.date === twoDaysAgoDate);
1860
+ expect(twoDaysAgo?.count).toBe(2);
1861
+ expect(twoDaysAgo?.level).toBe(1);
1862
+
1863
+ // 检查一天前的数据
1864
+ const oneDayAgo = result.find((item) => item.date === oneDayAgoDate);
1865
+ expect(oneDayAgo?.count).toBe(1);
1866
+ expect(oneDayAgo?.level).toBe(1);
1867
+
1868
+ // 检查今天的数据
1869
+ const todayData = result.find((item) => item.date === todayDate);
1870
+ expect(todayData?.count).toBe(0);
1871
+ expect(todayData?.level).toBe(0);
1872
+
1873
+ vi.useRealTimers();
1803
1874
  });
1804
1875
 
1805
1876
  it('should handle empty data', async () => {
@@ -894,7 +894,9 @@ describe('SessionModel', () => {
894
894
  await trx.insert(topics).values([
895
895
  { id: 't1', sessionId: '1', userId },
896
896
  { id: 't2', sessionId: '1', userId },
897
+ { id: 't6', sessionId: '1', userId },
897
898
  { id: 't3', sessionId: '2', userId },
899
+ { id: 't8', sessionId: '2', userId },
898
900
  { id: 't4', sessionId: '3', userId },
899
901
  ]);
900
902
  });
@@ -1,3 +1,5 @@
1
+ import { sql } from 'drizzle-orm';
2
+
1
3
  import { LobeChatDatabase } from '@/database/type';
2
4
  import { MigrationTableItem } from '@/types/clientDB';
3
5
 
@@ -8,6 +10,19 @@ export class DrizzleMigrationModel {
8
10
  this.db = db;
9
11
  }
10
12
 
13
+ getTableCounts = async () => {
14
+ // 这里使用 pg_tables 系统表查询用户表数量
15
+ const result = await this.db.execute(
16
+ sql`
17
+ SELECT COUNT(*) as table_count
18
+ FROM information_schema.tables
19
+ WHERE table_schema = 'public'
20
+ `,
21
+ );
22
+
23
+ return parseInt((result.rows[0] as any).table_count || '0');
24
+ };
25
+
11
26
  getMigrationList = async () => {
12
27
  const res = await this.db.execute(
13
28
  'SELECT * FROM "drizzle"."__drizzle_migrations" ORDER BY "created_at" DESC;',
@@ -382,15 +382,20 @@ export class MessageModel {
382
382
  .orderBy(desc(sql`heatmaps_date`));
383
383
 
384
384
  const heatmapData: HeatmapsProps['data'] = [];
385
- let currentDate = startDate;
385
+ let currentDate = startDate.clone();
386
+
387
+ const dateCountMap = new Map<string, number>();
388
+ for (const item of result) {
389
+ if (item?.date) {
390
+ const dateStr = dayjs(item.date as string).format('YYYY-MM-DD');
391
+ dateCountMap.set(dateStr, Number(item.count) || 0);
392
+ }
393
+ }
386
394
 
387
395
  while (currentDate.isBefore(endDate) || currentDate.isSame(endDate, 'day')) {
388
396
  const formattedDate = currentDate.format('YYYY-MM-DD');
389
- const matchingResult = result.find(
390
- (r) => r?.date && dayjs(r.date as string).format('YYYY-MM-DD') === formattedDate,
391
- );
397
+ const count = dateCountMap.get(formattedDate) || 0;
392
398
 
393
- const count = matchingResult ? matchingResult.count : 0;
394
399
  const levelCount = count > 0 ? Math.ceil(count / 5) : 0;
395
400
  const level = levelCount > 4 ? 4 : levelCount;
396
401
 
@@ -193,6 +193,14 @@ export class SessionModel {
193
193
  type: 'agent' | 'group';
194
194
  }): Promise<SessionItem> => {
195
195
  return this.db.transaction(async (trx) => {
196
+ if (slug) {
197
+ const existResult = await trx.query.sessions.findFirst({
198
+ where: and(eq(sessions.slug, slug), eq(sessions.userId, this.userId)),
199
+ });
200
+
201
+ if (existResult) return existResult;
202
+ }
203
+
196
204
  const newAgents = await trx
197
205
  .insert(agents)
198
206
  .values({
@@ -85,7 +85,13 @@ export class UserModel {
85
85
  const state = result[0];
86
86
 
87
87
  // Decrypt keyVaults
88
- const decryptKeyVaults = await decryptor(state.settingsKeyVaults, this.userId);
88
+ let decryptKeyVaults = {};
89
+
90
+ try {
91
+ decryptKeyVaults = await decryptor(state.settingsKeyVaults, this.userId);
92
+ } catch {
93
+ /* empty */
94
+ }
89
95
 
90
96
  const settings: DeepPartial<UserSettings> = {
91
97
  defaultAgent: state.settingsDefaultAgent || {},
@@ -171,6 +177,9 @@ export class UserModel {
171
177
  };
172
178
 
173
179
  // Static method
180
+ static makeSureUserExist = async (db: LobeChatDatabase, userId: string) => {
181
+ await db.insert(users).values({ id: userId }).onConflictDoNothing();
182
+ };
174
183
 
175
184
  static createUser = async (db: LobeChatDatabase, params: NewUser) => {
176
185
  // if user already exists, skip creation
@@ -1 +1 @@
1
- export { serverDB } from '../core/db-adaptor';
1
+ export { getServerDB, serverDB } from '../core/db-adaptor';
@@ -4,14 +4,16 @@ import { ActionIcon, FluentEmoji, Icon, SideNav } from '@lobehub/ui';
4
4
  import { FloatButton } from 'antd';
5
5
  import { createStyles } from 'antd-style';
6
6
  import { BugIcon, BugOff, XIcon } from 'lucide-react';
7
+ import { usePathname } from 'next/navigation';
7
8
  import { ReactNode, memo, useEffect, useState } from 'react';
8
9
  import { Flexbox } from 'react-layout-kit';
9
10
  import { Rnd } from 'react-rnd';
10
11
 
11
12
  import { BRANDING_NAME } from '@/const/branding';
13
+ import { isDesktop } from '@/const/version';
12
14
 
13
15
  // 定义样式
14
- const useStyles = createStyles(({ token, css, prefixCls }) => {
16
+ export const useStyles = createStyles(({ token, css, prefixCls }) => {
15
17
  return {
16
18
  collapsed: css`
17
19
  pointer-events: none;
@@ -86,6 +88,7 @@ const CollapsibleFloatPanel = memo<CollapsibleFloatPanelProps>(({ items }) => {
86
88
  const [position, setPosition] = useState({ x: 100, y: 100 });
87
89
  const [size, setSize] = useState({ height: minHeight, width: minWidth });
88
90
 
91
+ const pathname = usePathname();
89
92
  useEffect(() => {
90
93
  try {
91
94
  const localStoragePosition = localStorage.getItem('debug-panel-position');
@@ -108,11 +111,25 @@ const CollapsibleFloatPanel = memo<CollapsibleFloatPanelProps>(({ items }) => {
108
111
 
109
112
  return (
110
113
  <>
111
- <FloatButton
112
- className={styles.floatButton}
113
- icon={<Icon icon={isExpanded ? BugOff : BugIcon} />}
114
- onClick={() => setIsExpanded(!isExpanded)}
115
- />
114
+ {
115
+ // desktop devtools 下隐藏
116
+ pathname !== '/desktop/devtools' && (
117
+ <FloatButton
118
+ className={styles.floatButton}
119
+ icon={<Icon icon={isExpanded ? BugOff : BugIcon} />}
120
+ onClick={async () => {
121
+ if (isDesktop) {
122
+ const { electronDevtoolsService } = await import('@/services/electron/devtools');
123
+
124
+ await electronDevtoolsService.openDevtools();
125
+
126
+ return;
127
+ }
128
+ setIsExpanded(!isExpanded);
129
+ }}
130
+ />
131
+ )
132
+ }
116
133
  {isExpanded && (
117
134
  <Rnd
118
135
  bounds="window"
@@ -4,16 +4,20 @@ import { Popover } from 'antd';
4
4
  import { createStyles } from 'antd-style';
5
5
  import { PropsWithChildren, memo, useState } from 'react';
6
6
 
7
+ import { isDesktop } from '@/const/version';
8
+
7
9
  import PanelContent from './PanelContent';
8
10
  import UpgradeBadge from './UpgradeBadge';
9
11
  import { useNewVersion } from './useNewVersion';
10
12
 
11
- const useStyles = createStyles(({ css }) => ({
12
- popover: css`
13
- inset-block-start: 8px !important;
14
- inset-inline-start: 8px !important;
15
- `,
16
- }));
13
+ const useStyles = createStyles(({ css }) => {
14
+ return {
15
+ popover: css`
16
+ inset-block-start: ${isDesktop ? 24 : 8}px !important;
17
+ inset-inline-start: 8px !important;
18
+ `,
19
+ };
20
+ });
17
21
 
18
22
  const UserPanel = memo<PropsWithChildren>(({ children }) => {
19
23
  const hasNewVersion = useNewVersion();
@@ -1,3 +1,5 @@
1
+ import { getServerDB } from '@/database/core/db-adaptor';
2
+
1
3
  import { asyncAuth } from './asyncAuth';
2
4
  import { asyncTrpc } from './init';
3
5
 
@@ -5,6 +7,14 @@ export const publicProcedure = asyncTrpc.procedure;
5
7
 
6
8
  export const asyncRouter = asyncTrpc.router;
7
9
 
8
- export const asyncAuthedProcedure = asyncTrpc.procedure.use(asyncAuth);
10
+ export const asyncAuthedProcedure = asyncTrpc.procedure.use(asyncAuth).use(
11
+ asyncTrpc.middleware(async (opts) => {
12
+ const serverDB = await getServerDB();
13
+
14
+ return opts.next({
15
+ ctx: { serverDB },
16
+ });
17
+ }),
18
+ );
9
19
 
10
20
  export const createAsyncCallerFactory = asyncTrpc.createCallerFactory;
@@ -0,0 +1 @@
1
+ export * from './serverDatabase';
@@ -0,0 +1,10 @@
1
+ import { getServerDB } from '@/database/core/db-adaptor';
2
+ import { trpc } from '@/libs/trpc/init';
3
+
4
+ export const serverDatabase = trpc.middleware(async (opts) => {
5
+ const serverDB = await getServerDB();
6
+
7
+ return opts.next({
8
+ ctx: { serverDB },
9
+ });
10
+ });
@@ -1,11 +1,21 @@
1
1
  import { TRPCError } from '@trpc/server';
2
2
 
3
3
  import { enableClerk } from '@/const/auth';
4
+ import { DESKTOP_USER_ID } from '@/const/desktop';
5
+ import { isDesktop } from '@/const/version';
4
6
 
5
7
  import { trpc } from '../init';
6
8
 
7
9
  export const userAuth = trpc.middleware(async (opts) => {
8
10
  const { ctx } = opts;
11
+
12
+ if (isDesktop) {
13
+ return opts.next({
14
+ ctx: {
15
+ userId: DESKTOP_USER_ID,
16
+ },
17
+ });
18
+ }
9
19
  // `ctx.user` is nullable
10
20
  if (!ctx.userId) {
11
21
  if (enableClerk) {
@@ -11,7 +11,6 @@ import { ChunkModel } from '@/database/models/chunk';
11
11
  import { EmbeddingModel } from '@/database/models/embedding';
12
12
  import { FileModel } from '@/database/models/file';
13
13
  import { NewChunkItem, NewEmbeddingsItem } from '@/database/schemas';
14
- import { serverDB } from '@/database/server';
15
14
  import { asyncAuthedProcedure, asyncRouter as router } from '@/libs/trpc/async';
16
15
  import { getServerDefaultFilesConfig } from '@/server/globalConfig';
17
16
  import { initAgentRuntimeWithUserPayload } from '@/server/modules/AgentRuntime';
@@ -31,11 +30,11 @@ const fileProcedure = asyncAuthedProcedure.use(async (opts) => {
31
30
 
32
31
  return opts.next({
33
32
  ctx: {
34
- asyncTaskModel: new AsyncTaskModel(serverDB, ctx.userId),
35
- chunkModel: new ChunkModel(serverDB, ctx.userId),
33
+ asyncTaskModel: new AsyncTaskModel(ctx.serverDB, ctx.userId),
34
+ chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
36
35
  chunkService: new ChunkService(ctx.userId),
37
- embeddingModel: new EmbeddingModel(serverDB, ctx.userId),
38
- fileModel: new FileModel(serverDB, ctx.userId),
36
+ embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
37
+ fileModel: new FileModel(ctx.serverDB, ctx.userId),
39
38
  },
40
39
  });
41
40
  });
@@ -7,7 +7,6 @@ import { DEFAULT_EMBEDDING_MODEL, DEFAULT_MODEL } from '@/const/settings';
7
7
  import { ChunkModel } from '@/database/models/chunk';
8
8
  import { EmbeddingModel } from '@/database/models/embedding';
9
9
  import { FileModel } from '@/database/models/file';
10
- import { serverDB } from '@/database/server';
11
10
  import {
12
11
  EvalDatasetRecordModel,
13
12
  EvalEvaluationModel,
@@ -25,13 +24,13 @@ const ragEvalProcedure = asyncAuthedProcedure.use(async (opts) => {
25
24
 
26
25
  return opts.next({
27
26
  ctx: {
28
- chunkModel: new ChunkModel(serverDB, ctx.userId),
27
+ chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
29
28
  chunkService: new ChunkService(ctx.userId),
30
29
  datasetRecordModel: new EvalDatasetRecordModel(ctx.userId),
31
- embeddingModel: new EmbeddingModel(serverDB, ctx.userId),
30
+ embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
32
31
  evalRecordModel: new EvaluationRecordModel(ctx.userId),
33
32
  evaluationModel: new EvalEvaluationModel(ctx.userId),
34
- fileModel: new FileModel(serverDB, ctx.userId),
33
+ fileModel: new FileModel(ctx.serverDB, ctx.userId),
35
34
  },
36
35
  });
37
36
  });
@@ -2,16 +2,16 @@ import { z } from 'zod';
2
2
 
3
3
  import { SessionGroupModel } from '@/database/models/sessionGroup';
4
4
  import { insertSessionGroupSchema } from '@/database/schemas';
5
- import { serverDB } from '@/database/server';
6
5
  import { authedProcedure, router } from '@/libs/trpc';
6
+ import { serverDatabase } from '@/libs/trpc/lambda';
7
7
  import { SessionGroupItem } from '@/types/session';
8
8
 
9
- const sessionProcedure = authedProcedure.use(async (opts) => {
9
+ const sessionProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
10
10
  const { ctx } = opts;
11
11
 
12
12
  return opts.next({
13
13
  ctx: {
14
- sessionGroupModel: new SessionGroupModel(serverDB, ctx.userId),
14
+ sessionGroupModel: new SessionGroupModel(ctx.serverDB, ctx.userId),
15
15
  },
16
16
  });
17
17
  });
@@ -69,8 +69,6 @@ export const sessionGroupRouter = router({
69
69
  }),
70
70
  )
71
71
  .mutation(async ({ input, ctx }) => {
72
- console.log('sortMap:', input.sortMap);
73
-
74
72
  return ctx.sessionGroupModel.updateOrder(input.sortMap);
75
73
  }),
76
74
  });
@@ -7,22 +7,22 @@ import { FileModel } from '@/database/models/file';
7
7
  import { KnowledgeBaseModel } from '@/database/models/knowledgeBase';
8
8
  import { SessionModel } from '@/database/models/session';
9
9
  import { UserModel } from '@/database/models/user';
10
- import { serverDB } from '@/database/server';
11
10
  import { pino } from '@/libs/logger';
12
11
  import { authedProcedure, router } from '@/libs/trpc';
12
+ import { serverDatabase } from '@/libs/trpc/lambda';
13
13
  import { AgentService } from '@/server/services/agent';
14
14
  import { KnowledgeItem, KnowledgeType } from '@/types/knowledgeBase';
15
15
 
16
- const agentProcedure = authedProcedure.use(async (opts) => {
16
+ const agentProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
17
17
  const { ctx } = opts;
18
18
 
19
19
  return opts.next({
20
20
  ctx: {
21
- agentModel: new AgentModel(serverDB, ctx.userId),
22
- agentService: new AgentService(serverDB, ctx.userId),
23
- fileModel: new FileModel(serverDB, ctx.userId),
24
- knowledgeBaseModel: new KnowledgeBaseModel(serverDB, ctx.userId),
25
- sessionModel: new SessionModel(serverDB, ctx.userId),
21
+ agentModel: new AgentModel(ctx.serverDB, ctx.userId),
22
+ agentService: new AgentService(ctx.serverDB, ctx.userId),
23
+ fileModel: new FileModel(ctx.serverDB, ctx.userId),
24
+ knowledgeBaseModel: new KnowledgeBaseModel(ctx.serverDB, ctx.userId),
25
+ sessionModel: new SessionModel(ctx.serverDB, ctx.userId),
26
26
  },
27
27
  });
28
28
  });
@@ -90,7 +90,7 @@ export const agentRouter = router({
90
90
  // if there is no session for user, create one
91
91
  if (!item) {
92
92
  // if there is no user, return default config
93
- const user = await UserModel.findById(serverDB, ctx.userId);
93
+ const user = await UserModel.findById(ctx.serverDB, ctx.userId);
94
94
  if (!user) return DEFAULT_AGENT_CONFIG;
95
95
 
96
96
  const res = await ctx.agentService.createInbox();
@@ -3,8 +3,8 @@ import { z } from 'zod';
3
3
  import { AiModelModel } from '@/database/models/aiModel';
4
4
  import { UserModel } from '@/database/models/user';
5
5
  import { AiInfraRepos } from '@/database/repositories/aiInfra';
6
- import { serverDB } from '@/database/server';
7
6
  import { authedProcedure, router } from '@/libs/trpc';
7
+ import { serverDatabase } from '@/libs/trpc/lambda';
8
8
  import { getServerGlobalConfig } from '@/server/globalConfig';
9
9
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
10
10
  import {
@@ -15,7 +15,7 @@ import {
15
15
  } from '@/types/aiModel';
16
16
  import { ProviderConfig } from '@/types/user/settings';
17
17
 
18
- const aiModelProcedure = authedProcedure.use(async (opts) => {
18
+ const aiModelProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
19
19
  const { ctx } = opts;
20
20
 
21
21
  const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
@@ -24,13 +24,13 @@ const aiModelProcedure = authedProcedure.use(async (opts) => {
24
24
  return opts.next({
25
25
  ctx: {
26
26
  aiInfraRepos: new AiInfraRepos(
27
- serverDB,
27
+ ctx.serverDB,
28
28
  ctx.userId,
29
29
  aiProvider as Record<string, ProviderConfig>,
30
30
  ),
31
- aiModelModel: new AiModelModel(serverDB, ctx.userId),
31
+ aiModelModel: new AiModelModel(ctx.serverDB, ctx.userId),
32
32
  gateKeeper,
33
- userModel: new UserModel(serverDB, ctx.userId),
33
+ userModel: new UserModel(ctx.serverDB, ctx.userId),
34
34
  },
35
35
  });
36
36
  });
@@ -1,9 +1,7 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  import { AiProviderModel } from '@/database/models/aiProvider';
4
- import { UserModel } from '@/database/models/user';
5
4
  import { AiInfraRepos } from '@/database/repositories/aiInfra';
6
- import { serverDB } from '@/database/server';
7
5
  import { getServerGlobalConfig } from '@/server/globalConfig';
8
6
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
9
7
  import { AiProviderDetailItem, AiProviderRuntimeState } from '@/types/aiProvider';