@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.
- package/.github/workflows/claude-auto-testing.yml +6 -3
- package/.github/workflows/claude-dedupe-issues.yml +8 -1
- package/.github/workflows/claude-issue-triage.yml +8 -14
- package/.github/workflows/claude-translate-comments.yml +6 -3
- package/.github/workflows/claude-translator.yml +12 -14
- package/.github/workflows/claude.yml +10 -20
- package/.github/workflows/test.yml +26 -0
- package/CHANGELOG.md +33 -0
- package/changelog/v1.json +9 -0
- package/locales/zh-CN/components.json +1 -0
- package/package.json +3 -3
- package/packages/const/src/index.ts +0 -1
- package/packages/memory-user-memory/package.json +1 -0
- package/packages/memory-user-memory/src/extractors/context.test.ts +3 -2
- package/packages/memory-user-memory/src/extractors/experience.test.ts +3 -2
- package/packages/memory-user-memory/src/extractors/identity.test.ts +23 -6
- package/packages/memory-user-memory/src/extractors/preference.test.ts +3 -2
- package/packages/memory-user-memory/vitest.config.ts +4 -0
- package/packages/model-runtime/src/providers/replicate/index.ts +1 -1
- package/packages/ssrf-safe-fetch/index.test.ts +2 -2
- package/packages/ssrf-safe-fetch/package.json +3 -2
- package/packages/types/src/serverConfig.ts +2 -0
- package/packages/utils/package.json +1 -1
- package/packages/utils/src/client/xor-obfuscation.test.ts +32 -32
- package/packages/utils/src/client/xor-obfuscation.ts +3 -4
- package/packages/utils/src/imageToBase64.ts +1 -1
- package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
- package/packages/utils/src/server/auth.ts +1 -1
- package/packages/utils/src/server/xor.test.ts +9 -7
- package/packages/utils/src/server/xor.ts +1 -1
- package/packages/web-crawler/package.json +1 -1
- package/packages/web-crawler/src/crawImpl/__tests__/naive.test.ts +1 -1
- package/packages/web-crawler/src/crawImpl/naive.ts +1 -1
- package/scripts/prebuild.mts +58 -1
- package/src/app/(backend)/api/auth/[...all]/route.ts +2 -1
- package/src/app/(backend)/middleware/auth/index.ts +3 -3
- package/src/app/(backend)/middleware/auth/utils.test.ts +1 -1
- package/src/app/(backend)/middleware/auth/utils.ts +1 -1
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +2 -2
- package/src/app/(backend)/webapi/models/[provider]/route.test.ts +1 -1
- package/src/app/(backend)/webapi/plugin/gateway/route.ts +1 -1
- package/src/app/(backend)/webapi/proxy/route.ts +1 -1
- package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/signin/useSignIn.ts +2 -2
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +12 -6
- package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
- package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -1
- package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +1 -1
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +1 -1
- package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -1
- package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +1 -1
- package/src/components/ModelSelect/index.tsx +103 -72
- package/src/envs/auth.ts +30 -9
- package/src/features/Conversation/Messages/AssistantGroup/components/EditState.tsx +15 -32
- package/src/features/Conversation/Messages/AssistantGroup/index.tsx +9 -7
- package/src/features/EditorModal/EditorCanvas.tsx +31 -50
- package/src/features/EditorModal/TextareCanvas.tsx +3 -1
- package/src/features/EditorModal/index.tsx +14 -4
- package/src/features/ModelSwitchPanel/components/Footer.tsx +42 -0
- package/src/features/ModelSwitchPanel/components/List/MultipleProvidersModelItem.tsx +103 -0
- package/src/features/ModelSwitchPanel/components/List/SingleProviderModelItem.tsx +24 -0
- package/src/features/ModelSwitchPanel/components/List/VirtualItemRenderer.tsx +180 -0
- package/src/features/ModelSwitchPanel/components/List/index.tsx +99 -0
- package/src/features/ModelSwitchPanel/components/PanelContent.tsx +77 -0
- package/src/features/ModelSwitchPanel/components/Toolbar.tsx +54 -0
- package/src/features/ModelSwitchPanel/const.ts +29 -0
- package/src/features/ModelSwitchPanel/hooks/useBuildVirtualItems.ts +122 -0
- package/src/features/ModelSwitchPanel/hooks/useCurrentModelName.ts +18 -0
- package/src/features/ModelSwitchPanel/hooks/useDelayedRender.ts +18 -0
- package/src/features/ModelSwitchPanel/hooks/useModelAndProvider.ts +14 -0
- package/src/features/ModelSwitchPanel/hooks/usePanelHandlers.ts +33 -0
- package/src/features/ModelSwitchPanel/hooks/usePanelSize.ts +33 -0
- package/src/features/ModelSwitchPanel/hooks/usePanelState.ts +20 -0
- package/src/features/ModelSwitchPanel/index.tsx +25 -706
- package/src/features/ModelSwitchPanel/styles.ts +58 -0
- package/src/features/ModelSwitchPanel/types.ts +73 -0
- package/src/features/ModelSwitchPanel/utils.ts +24 -0
- package/src/features/User/UserPanel/PanelContent.tsx +1 -1
- package/src/features/User/__tests__/PanelContent.test.tsx +1 -1
- package/src/features/User/__tests__/UserAvatar.test.tsx +1 -1
- package/src/features/User/__tests__/useMenu.test.tsx +1 -1
- package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -1
- package/src/libs/better-auth/auth-client.ts +7 -3
- package/src/libs/better-auth/define-config.ts +2 -2
- package/src/libs/next/proxy/define-config.ts +1 -2
- package/src/libs/oidc-provider/provider.test.ts +1 -1
- package/src/libs/trpc/async/context.ts +1 -1
- package/src/libs/trpc/lambda/context.ts +7 -8
- package/src/libs/trpc/middleware/userAuth.ts +1 -1
- package/src/libs/trusted-client/getSessionUser.ts +1 -1
- package/src/locales/default/components.ts +1 -0
- package/src/server/globalConfig/index.ts +2 -0
- package/src/server/routers/async/caller.ts +1 -1
- package/src/server/routers/lambda/__tests__/user.test.ts +2 -2
- package/src/server/routers/lambda/user.ts +2 -1
- package/src/services/_auth.ts +3 -3
- package/src/services/chat/index.ts +1 -1
- package/src/services/chat/mecha/contextEngineering.ts +1 -1
- package/src/store/global/initialState.ts +10 -0
- package/src/store/global/selectors/systemStatus.ts +5 -0
- package/src/store/serverConfig/selectors.ts +5 -1
- package/src/store/tool/slices/mcpStore/action.ts +74 -75
- package/src/store/user/slices/auth/action.test.ts +1 -1
- package/src/store/user/slices/auth/action.ts +1 -1
- package/src/store/user/slices/auth/initialState.ts +1 -1
- package/src/store/user/slices/auth/selectors.test.ts +1 -1
- package/src/store/user/slices/auth/selectors.ts +1 -1
- package/src/store/user/slices/common/action.ts +1 -1
- package/src/store/userMemory/slices/context/action.ts +6 -6
- 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 '@/
|
|
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(
|
|
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();
|
|
@@ -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);
|
package/scripts/prebuild.mts
CHANGED
|
@@ -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('
|
|
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 '@/
|
|
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';
|
|
@@ -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 '@/
|
|
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 '@/
|
|
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('@/
|
|
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 '@/
|
|
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';
|
|
@@ -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 '@/
|
|
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 '@/
|
|
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 '@/
|
|
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
|
|
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 '@/
|
|
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';
|