@lobehub/lobehub 2.0.0-next.221 → 2.0.0-next.222

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 (114) hide show
  1. package/.github/workflows/claude-auto-testing.yml +6 -3
  2. package/.github/workflows/claude-dedupe-issues.yml +8 -1
  3. package/.github/workflows/claude-issue-triage.yml +8 -14
  4. package/.github/workflows/claude-translate-comments.yml +6 -3
  5. package/.github/workflows/claude-translator.yml +12 -14
  6. package/.github/workflows/claude.yml +10 -20
  7. package/.github/workflows/test.yml +26 -0
  8. package/CHANGELOG.md +33 -0
  9. package/changelog/v1.json +9 -0
  10. package/locales/zh-CN/components.json +1 -0
  11. package/package.json +3 -3
  12. package/packages/const/src/index.ts +0 -1
  13. package/packages/memory-user-memory/package.json +1 -0
  14. package/packages/memory-user-memory/src/extractors/context.test.ts +3 -2
  15. package/packages/memory-user-memory/src/extractors/experience.test.ts +3 -2
  16. package/packages/memory-user-memory/src/extractors/identity.test.ts +23 -6
  17. package/packages/memory-user-memory/src/extractors/preference.test.ts +3 -2
  18. package/packages/memory-user-memory/vitest.config.ts +4 -0
  19. package/packages/model-runtime/src/providers/replicate/index.ts +1 -1
  20. package/packages/ssrf-safe-fetch/index.test.ts +2 -2
  21. package/packages/ssrf-safe-fetch/package.json +3 -2
  22. package/packages/types/src/serverConfig.ts +2 -0
  23. package/packages/utils/package.json +1 -1
  24. package/packages/utils/src/client/xor-obfuscation.test.ts +32 -32
  25. package/packages/utils/src/client/xor-obfuscation.ts +3 -4
  26. package/packages/utils/src/imageToBase64.ts +1 -1
  27. package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
  28. package/packages/utils/src/server/auth.ts +1 -1
  29. package/packages/utils/src/server/xor.test.ts +9 -7
  30. package/packages/utils/src/server/xor.ts +1 -1
  31. package/packages/web-crawler/package.json +1 -1
  32. package/packages/web-crawler/src/crawImpl/__tests__/naive.test.ts +1 -1
  33. package/packages/web-crawler/src/crawImpl/naive.ts +1 -1
  34. package/scripts/prebuild.mts +58 -1
  35. package/src/app/(backend)/api/auth/[...all]/route.ts +2 -1
  36. package/src/app/(backend)/middleware/auth/index.ts +3 -3
  37. package/src/app/(backend)/middleware/auth/utils.test.ts +1 -1
  38. package/src/app/(backend)/middleware/auth/utils.ts +1 -1
  39. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +2 -2
  40. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +1 -1
  41. package/src/app/(backend)/webapi/plugin/gateway/route.ts +1 -1
  42. package/src/app/(backend)/webapi/proxy/route.ts +1 -1
  43. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
  44. package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
  45. package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
  46. package/src/app/[variants]/(auth)/signin/useSignIn.ts +2 -2
  47. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
  48. package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +12 -6
  49. package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
  50. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -1
  51. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
  52. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +1 -1
  53. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +1 -1
  54. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -1
  55. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +1 -1
  56. package/src/components/ModelSelect/index.tsx +103 -72
  57. package/src/envs/auth.ts +30 -9
  58. package/src/features/Conversation/Messages/AssistantGroup/components/EditState.tsx +15 -32
  59. package/src/features/Conversation/Messages/AssistantGroup/index.tsx +9 -7
  60. package/src/features/EditorModal/EditorCanvas.tsx +31 -50
  61. package/src/features/EditorModal/TextareCanvas.tsx +3 -1
  62. package/src/features/EditorModal/index.tsx +14 -4
  63. package/src/features/ModelSwitchPanel/components/Footer.tsx +42 -0
  64. package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +103 -0
  65. package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +24 -0
  66. package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +180 -0
  67. package/src/features/ModelSwitchPanel/components/List/index.tsx +99 -0
  68. package/src/features/ModelSwitchPanel/components/PanelContent.tsx +77 -0
  69. package/src/features/ModelSwitchPanel/components/Toolbar.tsx +54 -0
  70. package/src/features/ModelSwitchPanel/const.ts +29 -0
  71. package/src/features/ModelSwitchPanel/hooks/useBuildVirtualItems.ts +122 -0
  72. package/src/features/ModelSwitchPanel/hooks/useCurrentModelName.ts +18 -0
  73. package/src/features/ModelSwitchPanel/hooks/useDelayedRender.ts +18 -0
  74. package/src/features/ModelSwitchPanel/hooks/useModelAndProvider.ts +14 -0
  75. package/src/features/ModelSwitchPanel/hooks/usePanelHandlers.ts +33 -0
  76. package/src/features/ModelSwitchPanel/hooks/usePanelSize.ts +33 -0
  77. package/src/features/ModelSwitchPanel/hooks/usePanelState.ts +20 -0
  78. package/src/features/ModelSwitchPanel/index.tsx +25 -706
  79. package/src/features/ModelSwitchPanel/styles.ts +58 -0
  80. package/src/features/ModelSwitchPanel/types.ts +73 -0
  81. package/src/features/ModelSwitchPanel/utils.ts +24 -0
  82. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  83. package/src/features/User/__tests__/PanelContent.test.tsx +1 -1
  84. package/src/features/User/__tests__/UserAvatar.test.tsx +1 -1
  85. package/src/features/User/__tests__/useMenu.test.tsx +1 -1
  86. package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -1
  87. package/src/libs/better-auth/auth-client.ts +7 -3
  88. package/src/libs/better-auth/define-config.ts +2 -2
  89. package/src/libs/next/proxy/define-config.ts +1 -2
  90. package/src/libs/oidc-provider/provider.test.ts +1 -1
  91. package/src/libs/trpc/async/context.ts +1 -1
  92. package/src/libs/trpc/lambda/context.ts +7 -8
  93. package/src/libs/trpc/middleware/userAuth.ts +1 -1
  94. package/src/libs/trusted-client/getSessionUser.ts +1 -1
  95. package/src/locales/default/components.ts +1 -0
  96. package/src/server/globalConfig/index.ts +2 -0
  97. package/src/server/routers/async/caller.ts +1 -1
  98. package/src/server/routers/lambda/__tests__/user.test.ts +2 -2
  99. package/src/server/routers/lambda/user.ts +2 -1
  100. package/src/services/_auth.ts +3 -3
  101. package/src/services/chat/index.ts +1 -1
  102. package/src/services/chat/mecha/contextEngineering.ts +1 -1
  103. package/src/store/global/initialState.ts +10 -0
  104. package/src/store/global/selectors/systemStatus.ts +5 -0
  105. package/src/store/serverConfig/selectors.ts +5 -1
  106. package/src/store/tool/slices/mcpStore/action.ts +74 -75
  107. package/src/store/user/slices/auth/action.test.ts +1 -1
  108. package/src/store/user/slices/auth/action.ts +1 -1
  109. package/src/store/user/slices/auth/initialState.ts +1 -1
  110. package/src/store/user/slices/auth/selectors.test.ts +1 -1
  111. package/src/store/user/slices/auth/selectors.ts +1 -1
  112. package/src/store/user/slices/common/action.ts +1 -1
  113. package/src/store/userMemory/slices/context/action.ts +6 -6
  114. package/packages/const/src/auth.ts +0 -14
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
- import { SECRET_XOR_KEY } from '@/const/auth';
3
+ import { SECRET_XOR_KEY } from '@/envs/auth';
4
4
 
5
5
  import { obfuscatePayloadWithXOR } from './xor-obfuscation';
6
6
 
@@ -8,7 +8,7 @@ describe('xor-obfuscation', () => {
8
8
  describe('obfuscatePayloadWithXOR', () => {
9
9
  it('应该对简单字符串进行混淆并返回Base64字符串', () => {
10
10
  const payload = 'hello world';
11
- const result = obfuscatePayloadWithXOR(payload);
11
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
12
12
 
13
13
  // 验证返回值是字符串
14
14
  expect(typeof result).toBe('string');
@@ -22,7 +22,7 @@ describe('xor-obfuscation', () => {
22
22
 
23
23
  it('应该对JSON对象进行混淆', () => {
24
24
  const payload = { name: 'test', value: 123, active: true };
25
- const result = obfuscatePayloadWithXOR(payload);
25
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
26
26
 
27
27
  // 验证返回值是字符串
28
28
  expect(typeof result).toBe('string');
@@ -33,7 +33,7 @@ describe('xor-obfuscation', () => {
33
33
 
34
34
  it('应该对数组进行混淆', () => {
35
35
  const payload = [1, 2, 3, 'test', { nested: true }];
36
- const result = obfuscatePayloadWithXOR(payload);
36
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
37
37
 
38
38
  // 验证返回值是字符串
39
39
  expect(typeof result).toBe('string');
@@ -58,7 +58,7 @@ describe('xor-obfuscation', () => {
58
58
  tokens: ['abc123', 'def456'],
59
59
  metadata: null,
60
60
  };
61
- const result = obfuscatePayloadWithXOR(payload);
61
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
62
62
 
63
63
  // 验证返回值是字符串
64
64
  expect(typeof result).toBe('string');
@@ -69,8 +69,8 @@ describe('xor-obfuscation', () => {
69
69
 
70
70
  it('相同的输入应该产生相同的输出', () => {
71
71
  const payload = { test: 'consistent' };
72
- const result1 = obfuscatePayloadWithXOR(payload);
73
- const result2 = obfuscatePayloadWithXOR(payload);
72
+ const result1 = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
73
+ const result2 = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
74
74
 
75
75
  expect(result1).toBe(result2);
76
76
  });
@@ -79,15 +79,15 @@ describe('xor-obfuscation', () => {
79
79
  const payload1 = { test: 'value1' };
80
80
  const payload2 = { test: 'value2' };
81
81
 
82
- const result1 = obfuscatePayloadWithXOR(payload1);
83
- const result2 = obfuscatePayloadWithXOR(payload2);
82
+ const result1 = obfuscatePayloadWithXOR(payload1, SECRET_XOR_KEY);
83
+ const result2 = obfuscatePayloadWithXOR(payload2, SECRET_XOR_KEY);
84
84
 
85
85
  expect(result1).not.toBe(result2);
86
86
  });
87
87
 
88
88
  it('应该处理包含特殊字符的字符串', () => {
89
89
  const payload = 'Hello! @#$%^&*()_+-=[]{}|;:,.<>?/~`"\'\\';
90
- const result = obfuscatePayloadWithXOR(payload);
90
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
91
91
 
92
92
  // 验证返回值是字符串
93
93
  expect(typeof result).toBe('string');
@@ -98,7 +98,7 @@ describe('xor-obfuscation', () => {
98
98
 
99
99
  it('应该处理包含Unicode字符的字符串', () => {
100
100
  const payload = '你好世界 🌍 émojis 日本語 한국어';
101
- const result = obfuscatePayloadWithXOR(payload);
101
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
102
102
 
103
103
  // 验证返回值是字符串
104
104
  expect(typeof result).toBe('string');
@@ -109,7 +109,7 @@ describe('xor-obfuscation', () => {
109
109
 
110
110
  it('应该处理空字符串', () => {
111
111
  const payload = '';
112
- const result = obfuscatePayloadWithXOR(payload);
112
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
113
113
 
114
114
  // 验证返回值是字符串
115
115
  expect(typeof result).toBe('string');
@@ -120,7 +120,7 @@ describe('xor-obfuscation', () => {
120
120
 
121
121
  it('应该处理空对象', () => {
122
122
  const payload = {};
123
- const result = obfuscatePayloadWithXOR(payload);
123
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
124
124
 
125
125
  // 验证返回值是字符串
126
126
  expect(typeof result).toBe('string');
@@ -130,7 +130,7 @@ describe('xor-obfuscation', () => {
130
130
  });
131
131
 
132
132
  it('应该处理空数组', () => {
133
- const result = obfuscatePayloadWithXOR([]);
133
+ const result = obfuscatePayloadWithXOR([], SECRET_XOR_KEY);
134
134
 
135
135
  // 验证返回值是字符串
136
136
  expect(typeof result).toBe('string');
@@ -141,7 +141,7 @@ describe('xor-obfuscation', () => {
141
141
 
142
142
  it('应该处理null值', () => {
143
143
  const payload = null;
144
- const result = obfuscatePayloadWithXOR(payload);
144
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
145
145
 
146
146
  // 验证返回值是字符串
147
147
  expect(typeof result).toBe('string');
@@ -152,7 +152,7 @@ describe('xor-obfuscation', () => {
152
152
 
153
153
  it('应该处理数字', () => {
154
154
  const payload = 42;
155
- const result = obfuscatePayloadWithXOR(payload);
155
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
156
156
 
157
157
  // 验证返回值是字符串
158
158
  expect(typeof result).toBe('string');
@@ -165,8 +165,8 @@ describe('xor-obfuscation', () => {
165
165
  const payloadTrue = true;
166
166
  const payloadFalse = false;
167
167
 
168
- const resultTrue = obfuscatePayloadWithXOR(payloadTrue);
169
- const resultFalse = obfuscatePayloadWithXOR(payloadFalse);
168
+ const resultTrue = obfuscatePayloadWithXOR(payloadTrue, SECRET_XOR_KEY);
169
+ const resultFalse = obfuscatePayloadWithXOR(payloadFalse, SECRET_XOR_KEY);
170
170
 
171
171
  // 验证返回值是字符串
172
172
  expect(typeof resultTrue).toBe('string');
@@ -189,7 +189,7 @@ describe('xor-obfuscation', () => {
189
189
  tab: 'col1\tcol2',
190
190
  unicode: '\u0041\u0042\u0043',
191
191
  };
192
- const result = obfuscatePayloadWithXOR(payload);
192
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
193
193
 
194
194
  // 验证返回值是字符串
195
195
  expect(typeof result).toBe('string');
@@ -200,7 +200,7 @@ describe('xor-obfuscation', () => {
200
200
 
201
201
  it('应该处理很长的字符串', () => {
202
202
  const payload = 'a'.repeat(10000);
203
- const result = obfuscatePayloadWithXOR(payload);
203
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
204
204
 
205
205
  // 验证返回值是字符串
206
206
  expect(typeof result).toBe('string');
@@ -216,8 +216,8 @@ describe('xor-obfuscation', () => {
216
216
  const shortPayload = 'short';
217
217
  const longPayload = 'this is a much longer string that should produce different output';
218
218
 
219
- const shortResult = obfuscatePayloadWithXOR(shortPayload);
220
- const longResult = obfuscatePayloadWithXOR(longPayload);
219
+ const shortResult = obfuscatePayloadWithXOR(shortPayload, SECRET_XOR_KEY);
220
+ const longResult = obfuscatePayloadWithXOR(longPayload, SECRET_XOR_KEY);
221
221
 
222
222
  // 较长的输入应该产生较长的输出
223
223
  expect(longResult.length).toBeGreaterThan(shortResult.length);
@@ -225,7 +225,7 @@ describe('xor-obfuscation', () => {
225
225
 
226
226
  it('应该验证输出是有效的Base64格式', () => {
227
227
  const payload = { test: 'base64 validation' };
228
- const result = obfuscatePayloadWithXOR(payload);
228
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
229
229
 
230
230
  // 验证Base64格式的正则表达式
231
231
  const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
@@ -242,14 +242,14 @@ describe('xor-obfuscation', () => {
242
242
  },
243
243
  };
244
244
 
245
- const result = obfuscatePayloadWithXOR(payload);
245
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
246
246
  expect(typeof result).toBe('string');
247
247
  expect(() => atob(result)).not.toThrow();
248
248
  });
249
249
 
250
250
  it('应该对undefined值进行处理', () => {
251
251
  const payload = undefined;
252
- const result = obfuscatePayloadWithXOR(payload);
252
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
253
253
 
254
254
  // 验证返回值是字符串
255
255
  expect(typeof result).toBe('string');
@@ -268,7 +268,7 @@ describe('xor-obfuscation', () => {
268
268
  value: 123,
269
269
  };
270
270
 
271
- const result = obfuscatePayloadWithXOR(payload);
271
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
272
272
  expect(typeof result).toBe('string');
273
273
  expect(() => atob(result)).not.toThrow();
274
274
  });
@@ -279,7 +279,7 @@ describe('xor-obfuscation', () => {
279
279
 
280
280
  // 多次运行相同输入
281
281
  for (let i = 0; i < 10; i++) {
282
- results.push(obfuscatePayloadWithXOR(payload));
282
+ results.push(obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY));
283
283
  }
284
284
 
285
285
  // 所有结果应该相同
@@ -293,7 +293,7 @@ describe('xor-obfuscation', () => {
293
293
  name: 'date test',
294
294
  };
295
295
 
296
- const result = obfuscatePayloadWithXOR(payload);
296
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
297
297
  expect(typeof result).toBe('string');
298
298
  expect(() => atob(result)).not.toThrow();
299
299
  });
@@ -306,7 +306,7 @@ describe('xor-obfuscation', () => {
306
306
  normalKey: 'normal value',
307
307
  };
308
308
 
309
- const result = obfuscatePayloadWithXOR(payload);
309
+ const result = obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY);
310
310
  expect(typeof result).toBe('string');
311
311
  expect(() => atob(result)).not.toThrow();
312
312
  });
@@ -314,7 +314,7 @@ describe('xor-obfuscation', () => {
314
314
  it('应该验证混淆后的数据长度合理性', () => {
315
315
  const originalPayload = { test: 'length check' };
316
316
  const originalJSON = JSON.stringify(originalPayload);
317
- const result = obfuscatePayloadWithXOR(originalPayload);
317
+ const result = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
318
318
 
319
319
  // Base64 编码后的长度通常是原始长度的 4/3 倍(向上取整到4的倍数)
320
320
  const expectedMinLength = Math.ceil((originalJSON.length * 4) / 3 / 4) * 4;
@@ -323,7 +323,7 @@ describe('xor-obfuscation', () => {
323
323
 
324
324
  it('应该验证XOR操作的正确性(通过逆向操作)', () => {
325
325
  const originalPayload = { message: 'XOR test', value: 42 };
326
- const obfuscatedResult = obfuscatePayloadWithXOR(originalPayload);
326
+ const obfuscatedResult = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
327
327
 
328
328
  // 手动实现逆向操作来验证 XOR 操作的正确性
329
329
  const base64Decoded = atob(obfuscatedResult);
@@ -357,7 +357,7 @@ describe('xor-obfuscation', () => {
357
357
  [4, 5, 6],
358
358
  ];
359
359
 
360
- const results = payloads.map((payload) => obfuscatePayloadWithXOR(payload));
360
+ const results = payloads.map((payload) => obfuscatePayloadWithXOR(payload, SECRET_XOR_KEY));
361
361
 
362
362
  // 验证所有结果都不相同
363
363
  for (let i = 0; i < results.length; i++) {
@@ -1,5 +1,3 @@
1
- import { SECRET_XOR_KEY } from '@/const/auth';
2
-
3
1
  /**
4
2
  * Convert string to Uint8Array (UTF-8 encoding)
5
3
  */
@@ -24,12 +22,13 @@ const xorProcess = (data: Uint8Array, key: Uint8Array): Uint8Array => {
24
22
  /**
25
23
  * Obfuscate payload with XOR and encode to Base64
26
24
  * @param payload The JSON object to obfuscate
25
+ * @param secretKey The key used for XOR obfuscation
27
26
  * @returns The obfuscated string encoded in Base64
28
27
  */
29
- export const obfuscatePayloadWithXOR = <T>(payload: T): string => {
28
+ export const obfuscatePayloadWithXOR = <T>(payload: T, secretKey: string): string => {
30
29
  const jsonString = JSON.stringify(payload);
31
30
  const dataBytes = stringToUint8Array(jsonString);
32
- const keyBytes = stringToUint8Array(SECRET_XOR_KEY);
31
+ const keyBytes = stringToUint8Array(secretKey);
33
32
 
34
33
  const xoredBytes = xorProcess(dataBytes, keyBytes);
35
34
 
@@ -48,7 +48,7 @@ export const imageUrlToBase64 = async (
48
48
 
49
49
  // Use SSRF-safe fetch on server-side to prevent SSRF attacks
50
50
  const res = isServer
51
- ? await import('ssrf-safe-fetch').then((m) => m.ssrfSafeFetch(imageUrl))
51
+ ? await import('@lobechat/ssrf-safe-fetch').then((m) => m.ssrfSafeFetch(imageUrl))
52
52
  : await fetch(imageUrl);
53
53
 
54
54
  const blob = await res.blob();
@@ -7,7 +7,7 @@ let mockEnableBetterAuth = false;
7
7
  let mockEnableClerk = false;
8
8
  let mockEnableNextAuth = false;
9
9
 
10
- vi.mock('@/const/auth', () => ({
10
+ vi.mock('@/envs/auth', () => ({
11
11
  get enableBetterAuth() {
12
12
  return mockEnableBetterAuth;
13
13
  },
@@ -1,6 +1,6 @@
1
1
  import { headers } from 'next/headers';
2
2
 
3
- import { enableBetterAuth, enableClerk, enableNextAuth } from '@/const/auth';
3
+ import { enableBetterAuth, enableClerk, enableNextAuth } from '@/envs/auth';
4
4
 
5
5
  export const getUserAuth = async () => {
6
6
  if (enableClerk) {
@@ -1,5 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
+ import { SECRET_XOR_KEY } from '@/envs/auth';
4
+
3
5
  import { obfuscatePayloadWithXOR } from '../client/xor-obfuscation';
4
6
  import { getXorPayload } from './xor';
5
7
 
@@ -12,7 +14,7 @@ describe('getXorPayload', () => {
12
14
  };
13
15
 
14
16
  // 使用客户端的混淆函数生成token
15
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
17
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
16
18
 
17
19
  // 使用服务端的解码函数解码
18
20
  const decodedPayload = getXorPayload(obfuscatedToken);
@@ -25,7 +27,7 @@ describe('getXorPayload', () => {
25
27
  userId: '12345',
26
28
  };
27
29
 
28
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
30
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
29
31
  const decodedPayload = getXorPayload(obfuscatedToken);
30
32
 
31
33
  expect(decodedPayload).toEqual(originalPayload);
@@ -40,7 +42,7 @@ describe('getXorPayload', () => {
40
42
  awsSessionToken: 'session-token-example',
41
43
  };
42
44
 
43
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
45
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
44
46
  const decodedPayload = getXorPayload(obfuscatedToken);
45
47
 
46
48
  expect(decodedPayload).toEqual(originalPayload);
@@ -54,7 +56,7 @@ describe('getXorPayload', () => {
54
56
  azureApiVersion: '2024-02-15-preview',
55
57
  };
56
58
 
57
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
59
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
58
60
  const decodedPayload = getXorPayload(obfuscatedToken);
59
61
 
60
62
  expect(decodedPayload).toEqual(originalPayload);
@@ -67,7 +69,7 @@ describe('getXorPayload', () => {
67
69
  cloudflareBaseURLOrAccountID: 'account-id-example',
68
70
  };
69
71
 
70
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
72
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
71
73
  const decodedPayload = getXorPayload(obfuscatedToken);
72
74
 
73
75
  expect(decodedPayload).toEqual(originalPayload);
@@ -76,7 +78,7 @@ describe('getXorPayload', () => {
76
78
  it('should handle empty payload correctly', () => {
77
79
  const originalPayload = {};
78
80
 
79
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
81
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
80
82
  const decodedPayload = getXorPayload(obfuscatedToken);
81
83
 
82
84
  expect(decodedPayload).toEqual(originalPayload);
@@ -89,7 +91,7 @@ describe('getXorPayload', () => {
89
91
  apiKey: 'test-key',
90
92
  };
91
93
 
92
- const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload);
94
+ const obfuscatedToken = obfuscatePayloadWithXOR(originalPayload, SECRET_XOR_KEY);
93
95
  const decodedPayload = getXorPayload(obfuscatedToken);
94
96
 
95
97
  expect(decodedPayload).toEqual(originalPayload);
@@ -1,6 +1,6 @@
1
1
  import { ClientSecretPayload } from '@lobechat/types';
2
2
 
3
- import { SECRET_XOR_KEY } from '@/const/auth';
3
+ import { SECRET_XOR_KEY } from '@/envs/auth';
4
4
 
5
5
  /**
6
6
  * Convert Base64 string to Uint8Array
@@ -13,7 +13,7 @@
13
13
  "happy-dom": "^20.0.11",
14
14
  "node-html-markdown": "^1.3.0",
15
15
  "query-string": "^9.3.1",
16
- "ssrf-safe-fetch": "workspace:*",
16
+ "@lobechat/ssrf-safe-fetch": "workspace:*",
17
17
  "url-join": "^5"
18
18
  }
19
19
  }
@@ -13,7 +13,7 @@ vi.mock('../../utils/withTimeout', () => ({
13
13
  withTimeout: vi.fn(),
14
14
  }));
15
15
 
16
- vi.mock('ssrf-safe-fetch', () => ({
16
+ vi.mock('@lobechat/ssrf-safe-fetch', () => ({
17
17
  ssrfSafeFetch: vi.fn(),
18
18
  }));
19
19
 
@@ -1,4 +1,4 @@
1
- import { ssrfSafeFetch } from 'ssrf-safe-fetch';
1
+ import { ssrfSafeFetch } from '@lobechat/ssrf-safe-fetch';
2
2
 
3
3
  import { CrawlImpl, CrawlSuccessResult } from '../type';
4
4
  import { NetworkConnectionError, PageNotFoundError, TimeoutError } from '../utils/errorType';
@@ -1,3 +1,4 @@
1
+ import { execSync } from 'node:child_process';
1
2
  import * as dotenv from 'dotenv';
2
3
  import dotenvExpand from 'dotenv-expand';
3
4
  import { existsSync } from 'node:fs';
@@ -14,6 +15,61 @@ if (isDesktop) {
14
15
  } else {
15
16
  dotenvExpand.expand(dotenv.config());
16
17
  }
18
+
19
+ // Auth flags - use process.env directly for build-time dead code elimination
20
+ const enableClerk =
21
+ process.env.NEXT_PUBLIC_ENABLE_CLERK_AUTH === '1'
22
+ ? true
23
+ : !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
24
+ const enableBetterAuth = process.env.NEXT_PUBLIC_ENABLE_BETTER_AUTH === '1';
25
+ const enableNextAuth = process.env.NEXT_PUBLIC_ENABLE_NEXT_AUTH === '1';
26
+ const enableAuth = enableClerk || enableBetterAuth || enableNextAuth || false;
27
+
28
+ const getCommandVersion = (command: string): string | null => {
29
+ try {
30
+ return execSync(`${command} --version`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
31
+ .trim()
32
+ .split('\n')[0];
33
+ } catch {
34
+ return null;
35
+ }
36
+ };
37
+
38
+ const printEnvInfo = () => {
39
+ console.log('\n📋 Build Environment Info:');
40
+ console.log('─'.repeat(50));
41
+
42
+ // Runtime versions
43
+ console.log(` Node.js: ${process.version}`);
44
+ console.log(` npm: ${getCommandVersion('npm') ?? 'not installed'}`);
45
+
46
+ const bunVersion = getCommandVersion('bun');
47
+ if (bunVersion) console.log(` bun: ${bunVersion}`);
48
+
49
+ const pnpmVersion = getCommandVersion('pnpm');
50
+ if (pnpmVersion) console.log(` pnpm: ${pnpmVersion}`);
51
+
52
+ // Auth-related env vars
53
+ console.log('\n Auth Environment Variables:');
54
+ console.log(` NEXT_PUBLIC_AUTH_URL: ${process.env.NEXT_PUBLIC_AUTH_URL ?? '(not set)'}`);
55
+ console.log(` NEXTAUTH_URL: ${process.env.NEXTAUTH_URL ?? '(not set)'}`);
56
+ console.log(` APP_URL: ${process.env.APP_URL ?? '(not set)'}`);
57
+ console.log(` VERCEL_URL: ${process.env.VERCEL_URL ?? '(not set)'}`);
58
+ console.log(` AUTH_EMAIL_VERIFICATION: ${process.env.AUTH_EMAIL_VERIFICATION ?? '(not set)'}`);
59
+ console.log(` ENABLE_MAGIC_LINK: ${process.env.ENABLE_MAGIC_LINK ?? '(not set)'}`);
60
+ console.log(` AUTH_SECRET: ${process.env.AUTH_SECRET ? '✓ set' : '✗ not set'}`);
61
+ console.log(` KEY_VAULTS_SECRET: ${process.env.KEY_VAULTS_SECRET ? '✓ set' : '✗ not set'}`);
62
+
63
+ // Auth flags
64
+ console.log('\n Auth Flags:');
65
+ console.log(` enableClerk: ${enableClerk}`);
66
+ console.log(` enableBetterAuth: ${enableBetterAuth}`);
67
+ console.log(` enableNextAuth: ${enableNextAuth}`);
68
+ console.log(` enableAuth: ${enableAuth}`);
69
+
70
+ console.log('─'.repeat(50));
71
+ };
72
+
17
73
  // 创建需要排除的特性映射
18
74
  /* eslint-disable sort-keys-fix/sort-keys-fix */
19
75
  const partialBuildPages = [
@@ -105,8 +161,9 @@ export const runPrebuild = async (targetDir: string = 'src') => {
105
161
  const isMainModule = process.argv[1] === fileURLToPath(import.meta.url);
106
162
 
107
163
  if (isMainModule) {
164
+ printEnvInfo();
108
165
  // 执行删除操作
109
- console.log('Starting prebuild cleanup...');
166
+ console.log('\nStarting prebuild cleanup...');
110
167
  await runPrebuild();
111
168
  console.log('Prebuild cleanup completed.');
112
169
  }
@@ -1,6 +1,7 @@
1
- import { enableBetterAuth, enableNextAuth } from '@lobechat/const';
2
1
  import type { NextRequest } from 'next/server';
3
2
 
3
+ import { enableBetterAuth, enableNextAuth } from '@/envs/auth';
4
+
4
5
  const createHandler = async () => {
5
6
  if (enableBetterAuth) {
6
7
  const [{ toNextJsHandler }, { auth }] = await Promise.all([
@@ -8,15 +8,15 @@ import { ChatErrorType, type ClientSecretPayload } from '@lobechat/types';
8
8
  import { getXorPayload } from '@lobechat/utils/server';
9
9
  import { type NextRequest } from 'next/server';
10
10
 
11
+ import { getServerDB } from '@/database/core/db-adaptor';
12
+ import { type LobeChatDatabase } from '@/database/type';
11
13
  import {
12
14
  LOBE_CHAT_AUTH_HEADER,
13
15
  LOBE_CHAT_OIDC_AUTH_HEADER,
14
16
  OAUTH_AUTHORIZED,
15
17
  enableBetterAuth,
16
18
  enableClerk,
17
- } from '@/const/auth';
18
- import { getServerDB } from '@/database/core/db-adaptor';
19
- import { type LobeChatDatabase } from '@/database/type';
19
+ } from '@/envs/auth';
20
20
  import { ClerkAuth } from '@/libs/clerk-auth';
21
21
  import { validateOIDCJWT } from '@/libs/oidc-provider/jwt';
22
22
  import { createErrorResponse } from '@/utils/errorResponse';
@@ -7,7 +7,7 @@ let enableClerkMock = false;
7
7
  let enableNextAuthMock = false;
8
8
  let enableBetterAuthMock = false;
9
9
 
10
- vi.mock('@/const/auth', async (importOriginal) => {
10
+ vi.mock('@/envs/auth', async (importOriginal) => {
11
11
  const data = await importOriginal();
12
12
 
13
13
  return {
@@ -2,7 +2,7 @@ import { type AuthObject } from '@clerk/backend';
2
2
  import { AgentRuntimeError } from '@lobechat/model-runtime';
3
3
  import { ChatErrorType } from '@lobechat/types';
4
4
 
5
- import { enableBetterAuth, enableClerk, enableNextAuth } from '@/const/auth';
5
+ import { enableBetterAuth, enableClerk, enableNextAuth } from '@/envs/auth';
6
6
 
7
7
  interface CheckAuthParams {
8
8
  apiKey?: string;
@@ -6,7 +6,7 @@ import { getXorPayload } from '@lobechat/utils/server';
6
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
7
 
8
8
  import { checkAuthMethod } from '@/app/(backend)/middleware/auth/utils';
9
- import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED } from '@/const/auth';
9
+ import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED } from '@/envs/auth';
10
10
  import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
11
11
 
12
12
  import { POST } from './route';
@@ -32,7 +32,7 @@ vi.mock('@/server/modules/ModelRuntime', () => ({
32
32
  const mockState = vi.hoisted(() => ({ enableClerk: false }));
33
33
 
34
34
  // 模拟 @/const/auth 模块
35
- vi.mock('@/const/auth', async (importOriginal) => {
35
+ vi.mock('@/envs/auth', async (importOriginal) => {
36
36
  const modules = await importOriginal();
37
37
  return {
38
38
  ...(modules as any),
@@ -4,7 +4,7 @@ import { ChatErrorType } from '@lobechat/types';
4
4
  import { getXorPayload } from '@lobechat/utils/server';
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
6
 
7
- import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
7
+ import { LOBE_CHAT_AUTH_HEADER } from '@/envs/auth';
8
8
  import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
9
9
 
10
10
  import { GET } from './route';
@@ -3,9 +3,9 @@ import { ChatErrorType, TraceNameMap } from '@lobechat/types';
3
3
  import type { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
4
4
  import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway';
5
5
 
6
- import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
7
6
  import { LOBE_CHAT_TRACE_ID } from '@/const/trace';
8
7
  import { getAppConfig } from '@/envs/app';
8
+ import { LOBE_CHAT_AUTH_HEADER } from '@/envs/auth';
9
9
  import { TraceClient } from '@/libs/traces';
10
10
  import { parserPluginSettings } from '@/server/services/pluginGateway/settings';
11
11
  import { getTracePayload } from '@/utils/trace';
@@ -1,5 +1,5 @@
1
1
  import { NextResponse } from 'next/server';
2
- import { ssrfSafeFetch } from 'ssrf-safe-fetch';
2
+ import { ssrfSafeFetch } from '@lobechat/ssrf-safe-fetch';
3
3
 
4
4
  /**
5
5
  * just for a proxy
@@ -2,7 +2,7 @@ import { SignIn } from '@clerk/nextjs';
2
2
  import { BRANDING_NAME } from '@lobechat/business-const';
3
3
  import { notFound } from 'next/navigation';
4
4
 
5
- import { enableClerk } from '@/const/auth';
5
+ import { enableClerk } from '@/envs/auth';
6
6
  import { metadataModule } from '@/server/metadata';
7
7
  import { translation } from '@/server/translation';
8
8
  import { type DynamicLayoutProps } from '@/types/next';
@@ -1,7 +1,7 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { type PropsWithChildren } from 'react';
3
3
 
4
- import { enableBetterAuth } from '@/const/auth';
4
+ import { enableBetterAuth } from '@/envs/auth';
5
5
 
6
6
  const Layout = ({ children }: PropsWithChildren) => {
7
7
  if (!enableBetterAuth) return notFound();
@@ -1,7 +1,7 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import { type PropsWithChildren } from 'react';
3
3
 
4
- import { enableBetterAuth } from '@/const/auth';
4
+ import { enableBetterAuth } from '@/envs/auth';
5
5
 
6
6
  const Layout = ({ children }: PropsWithChildren) => {
7
7
  if (!enableBetterAuth) return notFound();
@@ -8,10 +8,10 @@ import type { CheckUserResponseData } from '@/app/(backend)/api/auth/check-user/
8
8
  import type { ResolveUsernameResponseData } from '@/app/(backend)/api/auth/resolve-username/route';
9
9
  import { useBusinessSignin } from '@/business/client/hooks/useBusinessSignin';
10
10
  import { message } from '@/components/AntdStaticMethods';
11
- import { getAuthConfig } from '@/envs/auth';
12
11
  import { requestPasswordReset, signIn } from '@/libs/better-auth/auth-client';
13
12
  import { isBuiltinProvider, normalizeProviderId } from '@/libs/better-auth/utils/client';
14
13
  import { useServerConfigStore } from '@/store/serverConfig';
14
+ import { serverConfigSelectors } from '@/store/serverConfig/selectors';
15
15
 
16
16
  import { EMAIL_REGEX, USERNAME_REGEX } from './SignInEmailStep';
17
17
 
@@ -31,7 +31,7 @@ export const useSignIn = () => {
31
31
  const { t } = useTranslation('auth');
32
32
  const router = useRouter();
33
33
  const searchParams = useSearchParams();
34
- const { NEXT_PUBLIC_ENABLE_MAGIC_LINK: enableMagicLink } = getAuthConfig();
34
+ const enableMagicLink = useServerConfigStore(serverConfigSelectors.enableMagicLink);
35
35
  const [form] = Form.useForm<SignInFormValues>();
36
36
  const [loading, setLoading] = useState(false);
37
37
  const [socialLoading, setSocialLoading] = useState<string | null>(null);
@@ -1,7 +1,7 @@
1
1
  import { SignUp } from '@clerk/nextjs';
2
2
  import { notFound } from 'next/navigation';
3
3
 
4
- import { enableBetterAuth, enableClerk } from '@/const/auth';
4
+ import { enableBetterAuth, enableClerk } from '@/envs/auth';
5
5
  import { metadataModule } from '@/server/metadata';
6
6
  import { translation } from '@/server/translation';
7
7
  import { type DynamicLayoutProps } from '@/types/next';