@lobehub/lobehub 2.0.0-next.343 → 2.0.0-next.345
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/.cursor/rules/i18n.mdc +1 -1
- package/.cursor/rules/modal-imperative.mdc +162 -0
- package/.cursor/rules/rules-index.mdc +1 -0
- package/.env.example +0 -14
- package/.eslintrc.js +8 -1
- package/CHANGELOG.md +66 -0
- package/Dockerfile +3 -13
- package/README.md +3 -5
- package/README.zh-CN.md +3 -5
- package/changelog/v1.json +24 -0
- package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +11 -42
- package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +10 -41
- package/e2e/src/support/webServer.ts +2 -0
- package/locales/ar/error.json +0 -4
- package/locales/bg-BG/error.json +0 -4
- package/locales/de-DE/error.json +0 -4
- package/locales/en-US/error.json +0 -4
- package/locales/es-ES/error.json +0 -4
- package/locales/fa-IR/error.json +0 -4
- package/locales/fr-FR/error.json +0 -4
- package/locales/it-IT/error.json +0 -4
- package/locales/ja-JP/error.json +0 -4
- package/locales/ko-KR/error.json +0 -4
- package/locales/nl-NL/error.json +0 -4
- package/locales/pl-PL/error.json +0 -4
- package/locales/pt-BR/error.json +0 -4
- package/locales/ru-RU/error.json +0 -4
- package/locales/tr-TR/error.json +0 -4
- package/locales/vi-VN/error.json +0 -4
- package/locales/zh-CN/error.json +0 -4
- package/locales/zh-TW/error.json +0 -4
- package/package.json +7 -9
- package/packages/builtin-agents/package.json +2 -0
- package/packages/builtin-agents/src/agents/agent-builder/index.ts +4 -2
- package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +4 -2
- package/packages/builtin-agents/src/agents/page-agent/index.ts +5 -2
- package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +161 -12
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +9 -9
- package/packages/context-engine/src/providers/GroupContextInjector.ts +19 -33
- package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +79 -43
- package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +5 -15
- package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +24 -3
- package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
- package/packages/model-bank/src/modelProviders/fal.ts +0 -1
- package/packages/types/src/fetch.ts +1 -2
- package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
- package/packages/utils/src/server/auth.ts +1 -9
- package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
- package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
- package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
- package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
- package/scripts/countEnWord.ts +1 -1
- package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
- package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
- package/scripts/prebuild.mts +10 -8
- package/scripts/serverLauncher/startServer.js +23 -5
- package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
- package/src/app/(backend)/middleware/auth/index.ts +0 -15
- package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
- package/src/app/(backend)/middleware/auth/utils.ts +2 -17
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
- package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
- package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
- package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
- package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
- package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
- package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
- package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
- package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
- package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
- package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
- package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
- package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
- package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
- package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
- package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
- package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
- package/src/app/robots.tsx +1 -1
- package/src/envs/auth.ts +2 -27
- package/src/envs/llm.ts +2 -2
- package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
- package/src/features/ChatMiniMap/utils.ts +1 -1
- package/src/features/CommandMenu/SearchResults.tsx +1 -1
- package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
- package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
- package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
- package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
- package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
- package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
- package/src/features/IntegrationDetailModal/index.tsx +21 -283
- package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
- package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
- package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
- package/src/features/ProfileEditor/AgentTool.tsx +14 -20
- package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
- package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
- package/src/features/SkillStore/Search/index.tsx +1 -1
- package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
- package/src/features/SkillStore/index.tsx +15 -33
- package/src/features/User/UserPanel/PanelContent.tsx +0 -8
- package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
- package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
- package/src/features/User/__tests__/useMenu.test.tsx +2 -43
- package/src/layout/AuthProvider/index.tsx +0 -5
- package/src/libs/next/config/define-config.ts +6 -0
- package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
- package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
- package/src/libs/next/proxy/define-config.ts +4 -53
- package/src/libs/next-auth/adapter/index.ts +1 -2
- package/src/libs/oidc-provider/provider.test.ts +5 -316
- package/src/libs/trpc/lambda/context.test.ts +0 -13
- package/src/libs/trpc/lambda/context.ts +3 -22
- package/src/libs/trpc/middleware/userAuth.ts +2 -4
- package/src/libs/trusted-client/getSessionUser.ts +2 -17
- package/src/locales/default/error.ts +0 -6
- package/src/locales/default/index.ts +0 -2
- package/src/proxy.ts +0 -1
- package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
- package/src/server/routers/lambda/user.ts +6 -63
- package/src/server/services/changelog/index.test.ts +3 -2
- package/src/server/services/changelog/index.ts +1 -1
- package/src/server/services/user/index.ts +0 -83
- package/src/services/chat/index.ts +1 -2
- package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
- package/src/store/user/slices/auth/action.test.ts +1 -81
- package/src/store/user/slices/auth/action.ts +3 -28
- package/src/store/user/slices/auth/initialState.ts +1 -18
- package/src/store/user/slices/auth/selectors.test.ts +2 -127
- package/src/store/user/slices/auth/selectors.ts +1 -21
- package/src/utils/errorResponse.ts +1 -4
- package/src/utils/markdownToTxt.ts +20 -0
- package/locales/ar/clerk.json +0 -545
- package/locales/bg-BG/clerk.json +0 -545
- package/locales/de-DE/clerk.json +0 -545
- package/locales/en-US/clerk.json +0 -545
- package/locales/es-ES/clerk.json +0 -545
- package/locales/fa-IR/clerk.json +0 -545
- package/locales/fr-FR/clerk.json +0 -545
- package/locales/it-IT/clerk.json +0 -545
- package/locales/ja-JP/clerk.json +0 -545
- package/locales/ko-KR/clerk.json +0 -545
- package/locales/nl-NL/clerk.json +0 -545
- package/locales/pl-PL/clerk.json +0 -545
- package/locales/pt-BR/clerk.json +0 -545
- package/locales/ru-RU/clerk.json +0 -545
- package/locales/tr-TR/clerk.json +0 -545
- package/locales/vi-VN/clerk.json +0 -545
- package/locales/zh-CN/clerk.json +0 -545
- package/locales/zh-TW/clerk.json +0 -545
- package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
- package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
- package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
- package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
- package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
- package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
- package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
- package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
- package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
- package/src/libs/clerk-auth/index.test.ts +0 -216
- package/src/libs/clerk-auth/index.ts +0 -80
- package/src/locales/default/clerk.ts +0 -677
- package/src/server/services/user/index.test.ts +0 -220
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
* Files to ignore at file level (won't be scanned at all)
|
|
12
12
|
*/
|
|
13
13
|
export const IGNORED_FILES = [
|
|
14
|
-
'clerk.ts', // Clerk third-party library translations
|
|
15
14
|
'providers.ts', // Dynamically generated from DEFAULT_MODEL_PROVIDER_LIST
|
|
16
15
|
'models.ts', // Dynamically generated from LOBE_DEFAULT_MODEL_LIST
|
|
17
16
|
'auth.ts', // Auth-related dynamic keys
|
|
@@ -77,7 +76,7 @@ export const PROTECTED_KEY_PATTERNS = [
|
|
|
77
76
|
* How to use:
|
|
78
77
|
*
|
|
79
78
|
* 1. IGNORED_FILES - Files to completely skip during analysis:
|
|
80
|
-
* Add filename with .ts extension (e.g., '
|
|
79
|
+
* Add filename with .ts extension (e.g., 'auth.ts')
|
|
81
80
|
* These files won't be scanned at all
|
|
82
81
|
*
|
|
83
82
|
* 2. PROTECTED_KEY_PATTERNS - Namespace/patterns to protect:
|
package/scripts/prebuild.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import * as dotenv from 'dotenv';
|
|
3
4
|
import dotenvExpand from 'dotenv-expand';
|
|
4
5
|
import { existsSync } from 'node:fs';
|
|
@@ -6,6 +7,10 @@ import { rm } from 'node:fs/promises';
|
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
9
|
|
|
10
|
+
// Use createRequire for CommonJS module compatibility
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const { checkDeprecatedClerkEnv } = require('./_shared/checkDeprecatedClerkEnv.js');
|
|
13
|
+
|
|
9
14
|
const isDesktop = process.env.NEXT_PUBLIC_IS_DESKTOP_APP === '1';
|
|
10
15
|
const isBundleAnalyzer = process.env.ANALYZE === 'true' && process.env.CI === 'true';
|
|
11
16
|
|
|
@@ -17,13 +22,9 @@ if (isDesktop) {
|
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
// Auth flags - use process.env directly for build-time dead code elimination
|
|
20
|
-
|
|
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
|
+
// Better Auth is the default auth solution when NextAuth is not explicitly enabled
|
|
25
26
|
const enableNextAuth = process.env.NEXT_PUBLIC_ENABLE_NEXT_AUTH === '1';
|
|
26
|
-
const
|
|
27
|
+
const enableBetterAuth = !enableNextAuth;
|
|
27
28
|
|
|
28
29
|
const getCommandVersion = (command: string): string | null => {
|
|
29
30
|
try {
|
|
@@ -62,10 +63,8 @@ const printEnvInfo = () => {
|
|
|
62
63
|
|
|
63
64
|
// Auth flags
|
|
64
65
|
console.log('\n Auth Flags:');
|
|
65
|
-
console.log(` enableClerk: ${enableClerk}`);
|
|
66
66
|
console.log(` enableBetterAuth: ${enableBetterAuth}`);
|
|
67
67
|
console.log(` enableNextAuth: ${enableNextAuth}`);
|
|
68
|
-
console.log(` enableAuth: ${enableAuth}`);
|
|
69
68
|
|
|
70
69
|
console.log('─'.repeat(50));
|
|
71
70
|
};
|
|
@@ -161,6 +160,9 @@ export const runPrebuild = async (targetDir: string = 'src') => {
|
|
|
161
160
|
const isMainModule = process.argv[1] === fileURLToPath(import.meta.url);
|
|
162
161
|
|
|
163
162
|
if (isMainModule) {
|
|
163
|
+
// Check for deprecated Clerk env vars first - fail fast if found
|
|
164
|
+
checkDeprecatedClerkEnv();
|
|
165
|
+
|
|
164
166
|
printEnvInfo();
|
|
165
167
|
// 执行删除操作
|
|
166
168
|
console.log('\nStarting prebuild cleanup...');
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
const dns = require('node:dns').promises;
|
|
2
2
|
const fs = require('node:fs').promises;
|
|
3
|
+
const path = require('node:path');
|
|
3
4
|
const { spawn } = require('node:child_process');
|
|
5
|
+
const { existsSync } = require('node:fs');
|
|
6
|
+
|
|
7
|
+
// Resolve shared module path for both local dev and Docker environments
|
|
8
|
+
// Local: scripts/serverLauncher/startServer.js -> scripts/_shared/...
|
|
9
|
+
// Docker: /app/startServer.js -> /app/scripts/_shared/...
|
|
10
|
+
const localPath = path.join(__dirname, '..', '_shared', 'checkDeprecatedClerkEnv.js');
|
|
11
|
+
const dockerPath = '/app/scripts/_shared/checkDeprecatedClerkEnv.js';
|
|
12
|
+
const sharedModulePath = existsSync(localPath) ? localPath : dockerPath;
|
|
13
|
+
|
|
14
|
+
const { checkDeprecatedClerkEnv } = require(sharedModulePath);
|
|
4
15
|
|
|
5
16
|
// Set file paths
|
|
6
17
|
const DB_MIGRATION_SCRIPT_PATH = '/app/docker.cjs';
|
|
@@ -9,8 +20,7 @@ const PROXYCHAINS_CONF_PATH = '/etc/proxychains4.conf';
|
|
|
9
20
|
|
|
10
21
|
// Function to check if a string is a valid IP address
|
|
11
22
|
const isValidIP = (ip, version = 4) => {
|
|
12
|
-
const ipv4Regex =
|
|
13
|
-
/^(25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3}$/;
|
|
23
|
+
const ipv4Regex = /^(25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3}$/;
|
|
14
24
|
const ipv6Regex =
|
|
15
25
|
/^(([\da-f]{1,4}:){7}[\da-f]{1,4}|([\da-f]{1,4}:){1,7}:|([\da-f]{1,4}:){1,6}:[\da-f]{1,4}|([\da-f]{1,4}:){1,5}(:[\da-f]{1,4}){1,2}|([\da-f]{1,4}:){1,4}(:[\da-f]{1,4}){1,3}|([\da-f]{1,4}:){1,3}(:[\da-f]{1,4}){1,4}|([\da-f]{1,4}:){1,2}(:[\da-f]{1,4}){1,5}|[\da-f]{1,4}:((:[\da-f]{1,4}){1,6})|:((:[\da-f]{1,4}){1,7}|:)|fe80:(:[\da-f]{0,4}){0,4}%[\da-z]+|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}\d){0,1}\d)\.){3}(25[0-5]|(2[0-4]|1{0,1}\d){0,1}\d)|([\da-f]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}\d){0,1}\d)\.){3}(25[0-5]|(2[0-4]|1{0,1}\d){0,1}\d))$/;
|
|
16
26
|
|
|
@@ -74,10 +84,13 @@ const runProxyChainsConfGenerator = async (url) => {
|
|
|
74
84
|
|
|
75
85
|
let ip = isValidIP(host, 4) ? host : await resolveHostIP(host, 4);
|
|
76
86
|
|
|
77
|
-
const proxyDNSConfig =
|
|
87
|
+
const proxyDNSConfig =
|
|
88
|
+
process.env.ENABLE_PROXY_DNS === '1'
|
|
89
|
+
? `
|
|
78
90
|
proxy_dns
|
|
79
91
|
remote_dns_subnet 224
|
|
80
|
-
`.trim()
|
|
92
|
+
`.trim()
|
|
93
|
+
: '';
|
|
81
94
|
|
|
82
95
|
const configContent = `
|
|
83
96
|
localnet 127.0.0.0/8
|
|
@@ -91,7 +104,9 @@ tcp_connect_time_out 8000
|
|
|
91
104
|
tcp_read_time_out 15000
|
|
92
105
|
[ProxyList]
|
|
93
106
|
${protocol} ${ip} ${port} ${user} ${pass}
|
|
94
|
-
|
|
107
|
+
`
|
|
108
|
+
.replaceAll(/\n{2,}/g, '\n')
|
|
109
|
+
.trim();
|
|
95
110
|
|
|
96
111
|
await fs.writeFile(PROXYCHAINS_CONF_PATH, configContent);
|
|
97
112
|
console.log(`✅ ProxyChains: All outgoing traffic routed via ${url}.`);
|
|
@@ -124,6 +139,9 @@ const runServer = async () => {
|
|
|
124
139
|
|
|
125
140
|
// Main execution block
|
|
126
141
|
(async () => {
|
|
142
|
+
// Check for deprecated Clerk env vars first - fail fast if found
|
|
143
|
+
checkDeprecatedClerkEnv({ action: 'restart' });
|
|
144
|
+
|
|
127
145
|
console.log('🌐 DNS Server:', dns.getServers());
|
|
128
146
|
console.log('-------------------------------------');
|
|
129
147
|
|
|
@@ -8,10 +8,6 @@ import { createErrorResponse } from '@/utils/errorResponse';
|
|
|
8
8
|
import { RequestHandler, checkAuth } from './index';
|
|
9
9
|
import { checkAuthMethod } from './utils';
|
|
10
10
|
|
|
11
|
-
vi.mock('@clerk/nextjs/server', () => ({
|
|
12
|
-
getAuth: vi.fn(),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
11
|
vi.mock('@/utils/errorResponse', () => ({
|
|
16
12
|
createErrorResponse: vi.fn(),
|
|
17
13
|
}));
|
|
@@ -24,6 +20,14 @@ vi.mock('@lobechat/utils/server', () => ({
|
|
|
24
20
|
getXorPayload: vi.fn(),
|
|
25
21
|
}));
|
|
26
22
|
|
|
23
|
+
vi.mock('@/envs/auth', async (importOriginal) => {
|
|
24
|
+
const actual = await importOriginal<typeof import('@/envs/auth')>();
|
|
25
|
+
return {
|
|
26
|
+
...actual,
|
|
27
|
+
enableBetterAuth: false,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
27
31
|
describe('checkAuth', () => {
|
|
28
32
|
const mockHandler: RequestHandler = vi.fn();
|
|
29
33
|
const mockRequest = new Request('https://example.com');
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type AuthObject } from '@clerk/backend';
|
|
2
1
|
import {
|
|
3
2
|
AgentRuntimeError,
|
|
4
3
|
type ChatCompletionErrorPayload,
|
|
@@ -6,7 +5,6 @@ import {
|
|
|
6
5
|
} from '@lobechat/model-runtime';
|
|
7
6
|
import { ChatErrorType, type ClientSecretPayload } from '@lobechat/types';
|
|
8
7
|
import { getXorPayload } from '@lobechat/utils/server';
|
|
9
|
-
import { type NextRequest } from 'next/server';
|
|
10
8
|
|
|
11
9
|
import { getServerDB } from '@/database/core/db-adaptor';
|
|
12
10
|
import { type LobeChatDatabase } from '@/database/type';
|
|
@@ -15,9 +13,7 @@ import {
|
|
|
15
13
|
LOBE_CHAT_OIDC_AUTH_HEADER,
|
|
16
14
|
OAUTH_AUTHORIZED,
|
|
17
15
|
enableBetterAuth,
|
|
18
|
-
enableClerk,
|
|
19
16
|
} from '@/envs/auth';
|
|
20
|
-
import { ClerkAuth } from '@/libs/clerk-auth';
|
|
21
17
|
import { validateOIDCJWT } from '@/libs/oidc-provider/jwt';
|
|
22
18
|
import { createErrorResponse } from '@/utils/errorResponse';
|
|
23
19
|
|
|
@@ -77,16 +73,6 @@ export const checkAuth =
|
|
|
77
73
|
|
|
78
74
|
if (!authorization) throw AgentRuntimeError.createError(ChatErrorType.Unauthorized);
|
|
79
75
|
|
|
80
|
-
// check the Auth With payload and clerk auth
|
|
81
|
-
let clerkAuth = {} as AuthObject;
|
|
82
|
-
|
|
83
|
-
// TODO: V2 完整移除 client 模式下的 clerk 集成代码
|
|
84
|
-
if (enableClerk) {
|
|
85
|
-
const auth = new ClerkAuth();
|
|
86
|
-
const data = auth.getAuthFromRequest(req as NextRequest);
|
|
87
|
-
clerkAuth = data.clerkAuth;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
76
|
jwtPayload = getXorPayload(authorization);
|
|
91
77
|
|
|
92
78
|
const oidcAuthorization = req.headers.get(LOBE_CHAT_OIDC_AUTH_HEADER);
|
|
@@ -106,7 +92,6 @@ export const checkAuth =
|
|
|
106
92
|
checkAuthMethod({
|
|
107
93
|
apiKey: jwtPayload.apiKey,
|
|
108
94
|
betterAuthAuthorized,
|
|
109
|
-
clerkAuth,
|
|
110
95
|
nextAuthAuthorized: oauthAuthorized,
|
|
111
96
|
});
|
|
112
97
|
} catch (e) {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { type AuthObject } from '@clerk/backend';
|
|
2
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
2
|
|
|
4
3
|
import { checkAuthMethod } from './utils';
|
|
5
4
|
|
|
6
|
-
let enableClerkMock = false;
|
|
7
5
|
let enableNextAuthMock = false;
|
|
8
6
|
let enableBetterAuthMock = false;
|
|
9
7
|
|
|
@@ -12,9 +10,6 @@ vi.mock('@/envs/auth', async (importOriginal) => {
|
|
|
12
10
|
|
|
13
11
|
return {
|
|
14
12
|
...(data as any),
|
|
15
|
-
get enableClerk() {
|
|
16
|
-
return enableClerkMock;
|
|
17
|
-
},
|
|
18
13
|
get enableBetterAuth() {
|
|
19
14
|
return enableBetterAuthMock;
|
|
20
15
|
},
|
|
@@ -29,29 +24,6 @@ describe('checkAuthMethod', () => {
|
|
|
29
24
|
vi.clearAllMocks();
|
|
30
25
|
});
|
|
31
26
|
|
|
32
|
-
it('should pass with valid Clerk auth', () => {
|
|
33
|
-
enableClerkMock = true;
|
|
34
|
-
expect(() =>
|
|
35
|
-
checkAuthMethod({
|
|
36
|
-
clerkAuth: { userId: 'someUserId' } as AuthObject,
|
|
37
|
-
}),
|
|
38
|
-
).not.toThrow();
|
|
39
|
-
|
|
40
|
-
enableClerkMock = false;
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should throw error with invalid Clerk auth', () => {
|
|
44
|
-
enableClerkMock = true;
|
|
45
|
-
try {
|
|
46
|
-
checkAuthMethod({
|
|
47
|
-
clerkAuth: {} as any,
|
|
48
|
-
});
|
|
49
|
-
} catch (e) {
|
|
50
|
-
expect(e).toEqual({ errorType: 'InvalidClerkUser' });
|
|
51
|
-
}
|
|
52
|
-
enableClerkMock = false;
|
|
53
|
-
});
|
|
54
|
-
|
|
55
27
|
it('should pass with valid Next auth', () => {
|
|
56
28
|
enableNextAuthMock = true;
|
|
57
29
|
expect(() =>
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AgentRuntimeError } from '@lobechat/model-runtime';
|
|
3
|
-
import { ChatErrorType } from '@lobechat/types';
|
|
4
|
-
|
|
5
|
-
import { enableBetterAuth, enableClerk, enableNextAuth } from '@/envs/auth';
|
|
1
|
+
import { enableBetterAuth, enableNextAuth } from '@/envs/auth';
|
|
6
2
|
|
|
7
3
|
interface CheckAuthParams {
|
|
8
4
|
apiKey?: string;
|
|
9
5
|
betterAuthAuthorized?: boolean;
|
|
10
|
-
clerkAuth?: AuthObject;
|
|
11
6
|
nextAuthAuthorized?: boolean;
|
|
12
7
|
}
|
|
13
8
|
/**
|
|
@@ -16,20 +11,10 @@ interface CheckAuthParams {
|
|
|
16
11
|
* @param {CheckAuthParams} params - Authentication parameters extracted from headers.
|
|
17
12
|
* @param {string} [params.apiKey] - The user API key.
|
|
18
13
|
* @param {boolean} [params.betterAuthAuthorized] - Whether the Better Auth session exists.
|
|
19
|
-
* @param {AuthObject} [params.clerkAuth] - Clerk authentication payload from middleware.
|
|
20
14
|
* @param {boolean} [params.nextAuthAuthorized] - Whether the OAuth 2 header is provided.
|
|
21
|
-
* @throws {AgentRuntimeError} If authentication fails.
|
|
22
15
|
*/
|
|
23
16
|
export const checkAuthMethod = (params: CheckAuthParams) => {
|
|
24
|
-
const { apiKey, betterAuthAuthorized, nextAuthAuthorized
|
|
25
|
-
// clerk auth handler
|
|
26
|
-
if (enableClerk) {
|
|
27
|
-
// if there is no userId, means the use is not login, just throw error
|
|
28
|
-
if (!(clerkAuth as any)?.userId)
|
|
29
|
-
throw AgentRuntimeError.createError(ChatErrorType.InvalidClerkUser);
|
|
30
|
-
// if the user is login, just return
|
|
31
|
-
else return;
|
|
32
|
-
}
|
|
17
|
+
const { apiKey, betterAuthAuthorized, nextAuthAuthorized } = params;
|
|
33
18
|
|
|
34
19
|
// if better auth session exists
|
|
35
20
|
if (enableBetterAuth && betterAuthAuthorized) return;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// @vitest-environment node
|
|
2
|
-
import { getAuth } from '@clerk/nextjs/server';
|
|
3
2
|
import { LobeRuntimeAI, ModelRuntime } from '@lobechat/model-runtime';
|
|
4
3
|
import { ChatErrorType } from '@lobechat/types';
|
|
5
4
|
import { getXorPayload } from '@lobechat/utils/server';
|
|
@@ -11,10 +10,6 @@ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
|
|
|
11
10
|
|
|
12
11
|
import { POST } from './route';
|
|
13
12
|
|
|
14
|
-
vi.mock('@clerk/nextjs/server', () => ({
|
|
15
|
-
getAuth: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
13
|
vi.mock('@/app/(backend)/middleware/auth/utils', () => ({
|
|
19
14
|
checkAuthMethod: vi.fn(),
|
|
20
15
|
}));
|
|
@@ -28,17 +23,11 @@ vi.mock('@/server/modules/ModelRuntime', () => ({
|
|
|
28
23
|
createTraceOptions: vi.fn().mockReturnValue({}),
|
|
29
24
|
}));
|
|
30
25
|
|
|
31
|
-
// Use vi.hoisted to ensure mockState is initialized before mocks are set up
|
|
32
|
-
const mockState = vi.hoisted(() => ({ enableClerk: false }));
|
|
33
|
-
|
|
34
|
-
// 模拟 @/const/auth 模块
|
|
35
26
|
vi.mock('@/envs/auth', async (importOriginal) => {
|
|
36
|
-
const
|
|
27
|
+
const actual = await importOriginal<typeof import('@/envs/auth')>();
|
|
37
28
|
return {
|
|
38
|
-
...
|
|
39
|
-
|
|
40
|
-
return mockState.enableClerk;
|
|
41
|
-
},
|
|
29
|
+
...actual,
|
|
30
|
+
enableBetterAuth: false,
|
|
42
31
|
};
|
|
43
32
|
});
|
|
44
33
|
|
|
@@ -58,7 +47,6 @@ beforeEach(() => {
|
|
|
58
47
|
afterEach(() => {
|
|
59
48
|
// 清除模拟调用历史
|
|
60
49
|
vi.clearAllMocks();
|
|
61
|
-
mockState.enableClerk = false;
|
|
62
50
|
});
|
|
63
51
|
|
|
64
52
|
describe('POST handler', () => {
|
|
@@ -108,42 +96,6 @@ describe('POST handler', () => {
|
|
|
108
96
|
});
|
|
109
97
|
});
|
|
110
98
|
|
|
111
|
-
it('should have pass clerk Auth when enable clerk', async () => {
|
|
112
|
-
mockState.enableClerk = true;
|
|
113
|
-
|
|
114
|
-
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
115
|
-
apiKey: 'test-api-key',
|
|
116
|
-
azureApiVersion: 'v1',
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const mockParams = Promise.resolve({ provider: 'test-provider' });
|
|
120
|
-
vi.mocked(getAuth).mockReturnValue({} as any);
|
|
121
|
-
vi.mocked(checkAuthMethod).mockReset();
|
|
122
|
-
|
|
123
|
-
const mockRuntime: LobeRuntimeAI = { baseURL: 'abc', chat: vi.fn() };
|
|
124
|
-
|
|
125
|
-
// Mock initModelRuntimeFromDB
|
|
126
|
-
vi.mocked(initModelRuntimeFromDB).mockResolvedValue(new ModelRuntime(mockRuntime));
|
|
127
|
-
|
|
128
|
-
const request = new Request(new URL('https://test.com'), {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
body: JSON.stringify({ model: 'test-model' }),
|
|
131
|
-
headers: {
|
|
132
|
-
[LOBE_CHAT_AUTH_HEADER]: 'some-valid-token',
|
|
133
|
-
[OAUTH_AUTHORIZED]: '1',
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
await POST(request, { params: mockParams });
|
|
138
|
-
|
|
139
|
-
expect(checkAuthMethod).toBeCalledWith({
|
|
140
|
-
apiKey: 'test-api-key',
|
|
141
|
-
betterAuthAuthorized: false,
|
|
142
|
-
clerkAuth: {},
|
|
143
|
-
nextAuthAuthorized: true,
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
99
|
it('should return InternalServerError error when throw a unknown error', async () => {
|
|
148
100
|
const mockParams = Promise.resolve({ provider: 'test-provider' });
|
|
149
101
|
vi.mocked(getXorPayload).mockImplementationOnce(() => {
|
|
@@ -9,10 +9,6 @@ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
|
|
|
9
9
|
|
|
10
10
|
import { GET } from './route';
|
|
11
11
|
|
|
12
|
-
vi.mock('@clerk/nextjs/server', () => ({
|
|
13
|
-
getAuth: vi.fn(),
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
12
|
vi.mock('@/app/(backend)/middleware/auth/utils', () => ({
|
|
17
13
|
checkAuthMethod: vi.fn(),
|
|
18
14
|
}));
|
|
@@ -21,6 +17,14 @@ vi.mock('@lobechat/utils/server', () => ({
|
|
|
21
17
|
getXorPayload: vi.fn(),
|
|
22
18
|
}));
|
|
23
19
|
|
|
20
|
+
vi.mock('@/envs/auth', async (importOriginal) => {
|
|
21
|
+
const actual = await importOriginal<typeof import('@/envs/auth')>();
|
|
22
|
+
return {
|
|
23
|
+
...actual,
|
|
24
|
+
enableBetterAuth: false,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
24
28
|
vi.mock('@/server/modules/ModelRuntime', () => ({
|
|
25
29
|
initModelRuntimeFromDB: vi.fn(),
|
|
26
30
|
}));
|
|
@@ -68,7 +68,8 @@ const BtnListLoading = memo(() => {
|
|
|
68
68
|
* ref: https://authjs.dev/guides/pages/signin
|
|
69
69
|
*/
|
|
70
70
|
export default memo(() => {
|
|
71
|
-
const { t } = useTranslation('
|
|
71
|
+
const { t } = useTranslation('auth');
|
|
72
|
+
const { t: tCommon } = useTranslation('common');
|
|
72
73
|
const router = useRouter();
|
|
73
74
|
const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
|
|
74
75
|
|
|
@@ -100,9 +101,9 @@ export default memo(() => {
|
|
|
100
101
|
};
|
|
101
102
|
|
|
102
103
|
const footerBtns = [
|
|
103
|
-
{ href: DOCUMENTS_REFER_URL, id: 0, label:
|
|
104
|
-
{ href: PRIVACY_URL, id: 1, label: t('
|
|
105
|
-
{ href: TERMS_URL, id: 2, label: t('
|
|
104
|
+
{ href: DOCUMENTS_REFER_URL, id: 0, label: tCommon('document') },
|
|
105
|
+
{ href: PRIVACY_URL, id: 1, label: t('footer.privacy') },
|
|
106
|
+
{ href: TERMS_URL, id: 2, label: t('footer.terms') },
|
|
106
107
|
];
|
|
107
108
|
|
|
108
109
|
return (
|
|
@@ -116,10 +117,10 @@ export default memo(() => {
|
|
|
116
117
|
<div>
|
|
117
118
|
<LobeHub size={48} />
|
|
118
119
|
</div>
|
|
119
|
-
{t('
|
|
120
|
+
{t('signin.title')}
|
|
120
121
|
</Text>
|
|
121
122
|
<Text as={'p'} className={styles.description}>
|
|
122
|
-
{t('
|
|
123
|
+
{t('signin.subtitle', { appName: BRANDING_NAME })}
|
|
123
124
|
</Text>
|
|
124
125
|
</div>
|
|
125
126
|
{/* Content */}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { enableBetterAuth, enableClerk } from '@/envs/auth';
|
|
1
|
+
import { enableBetterAuth } from '@/envs/auth';
|
|
4
2
|
import { notFound } from '@/libs/next/navigation';
|
|
5
3
|
import { metadataModule } from '@/server/metadata';
|
|
6
4
|
import { translation } from '@/server/translation';
|
|
@@ -12,15 +10,6 @@ import BetterAuthSignUpForm from './BetterAuthSignUpForm';
|
|
|
12
10
|
export const generateMetadata = async (props: DynamicLayoutProps) => {
|
|
13
11
|
const locale = await RouteVariants.getLocale(props);
|
|
14
12
|
|
|
15
|
-
if (enableClerk) {
|
|
16
|
-
const { t } = await translation('clerk', locale);
|
|
17
|
-
return metadataModule.generate({
|
|
18
|
-
description: t('signUp.start.subtitle'),
|
|
19
|
-
title: t('signUp.start.title'),
|
|
20
|
-
url: '/signup',
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
13
|
if (enableBetterAuth) {
|
|
25
14
|
const { t } = await translation('auth', locale);
|
|
26
15
|
return metadataModule.generate({
|
|
@@ -37,10 +26,6 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
|
|
|
37
26
|
};
|
|
38
27
|
|
|
39
28
|
const Page = () => {
|
|
40
|
-
if (enableClerk) {
|
|
41
|
-
return <SignUp path="/signup" />;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
29
|
if (enableBetterAuth) {
|
|
45
30
|
return <BetterAuthSignUpForm />;
|
|
46
31
|
}
|
|
@@ -42,7 +42,7 @@ const AgentList = memo<{ onMoreClick?: () => void }>(({ onMoreClick }) => {
|
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
44
|
<>
|
|
45
|
-
<InboxItem />
|
|
45
|
+
<InboxItem style={{ minHeight: 36 }} />
|
|
46
46
|
{showPinned && <SessionList dataSource={pinnedList!} />}
|
|
47
47
|
{showCustom && <Group dataSource={customList!} />}
|
|
48
48
|
{showDefault && (
|
|
@@ -4,10 +4,10 @@ import { getKlavisServerByServerIdentifier, getLobehubSkillProviderById } from '
|
|
|
4
4
|
import { Avatar, Flexbox, Icon } from '@lobehub/ui';
|
|
5
5
|
import { createStaticStyles } from 'antd-style';
|
|
6
6
|
import { Blocks } from 'lucide-react';
|
|
7
|
-
import { type ReactNode, createElement, memo,
|
|
7
|
+
import { type ReactNode, createElement, memo, useCallback, useMemo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { createSkillStoreModal } from '@/features/SkillStore';
|
|
11
11
|
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
12
12
|
import { useToolStore } from '@/store/tool';
|
|
13
13
|
|
|
@@ -61,7 +61,6 @@ const BANNER_SKILL_IDS = [
|
|
|
61
61
|
|
|
62
62
|
const SkillInstallBanner = memo(() => {
|
|
63
63
|
const { t } = useTranslation('plugin');
|
|
64
|
-
const [open, setOpen] = useState(false);
|
|
65
64
|
|
|
66
65
|
const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
|
|
67
66
|
const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);
|
|
@@ -108,20 +107,21 @@ const SkillInstallBanner = memo(() => {
|
|
|
108
107
|
return items;
|
|
109
108
|
}, []);
|
|
110
109
|
|
|
110
|
+
const handleOpenStore = useCallback(() => {
|
|
111
|
+
createSkillStoreModal();
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
111
114
|
// Don't show banner if no skills are enabled
|
|
112
115
|
if (!isLobehubSkillEnabled && !isKlavisEnabled) return null;
|
|
113
116
|
|
|
114
117
|
return (
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
</div>
|
|
123
|
-
<SkillStore open={open} setOpen={setOpen} />
|
|
124
|
-
</>
|
|
118
|
+
<div className={styles.banner} onClick={handleOpenStore}>
|
|
119
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
120
|
+
<Icon className={styles.icon} icon={Blocks} size={18} />
|
|
121
|
+
<span className={styles.text}>{t('skillInstallBanner.title')}</span>
|
|
122
|
+
</Flexbox>
|
|
123
|
+
{avatarItems.length > 0 && <Avatar.Group items={avatarItems} shape="circle" size={24} />}
|
|
124
|
+
</div>
|
|
125
125
|
);
|
|
126
126
|
});
|
|
127
127
|
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import { Avatar, Block, Center, Flexbox, Icon, Text } from '@lobehub/ui';
|
|
4
4
|
import { cssVar } from 'antd-style';
|
|
5
5
|
import { FileTextIcon } from 'lucide-react';
|
|
6
|
-
import markdownToTxt from 'markdown-to-txt';
|
|
7
6
|
import { memo } from 'react';
|
|
8
7
|
|
|
9
8
|
import Time from '@/app/[variants]/(main)/home/features/components/Time';
|
|
10
9
|
import { RECENT_BLOCK_SIZE } from '@/app/[variants]/(main)/home/features/const';
|
|
11
10
|
import { type FileListItem } from '@/types/files';
|
|
11
|
+
import markdownToTxt from '@/utils/markdownToTxt';
|
|
12
12
|
|
|
13
13
|
// Helper to extract title from markdown content
|
|
14
14
|
const extractTitle = (content: string): string | null => {
|
|
@@ -24,7 +24,7 @@ const getPreviewText = (item: FileListItem): string => {
|
|
|
24
24
|
if (!item.content) return '';
|
|
25
25
|
|
|
26
26
|
// Convert markdown to plain text
|
|
27
|
-
let plainText = markdownToTxt(item.content);
|
|
27
|
+
let plainText = markdownToTxt(item.content.slice(0, 120));
|
|
28
28
|
|
|
29
29
|
// Remove the title line if it exists
|
|
30
30
|
const title = extractTitle(item.content);
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
Mic2,
|
|
21
21
|
PaletteIcon,
|
|
22
22
|
PieChart,
|
|
23
|
-
ShieldCheck,
|
|
24
23
|
Sparkles,
|
|
25
24
|
UserCircle,
|
|
26
25
|
} from 'lucide-react';
|
|
@@ -32,7 +31,7 @@ import { electronSyncSelectors } from '@/store/electron/selectors';
|
|
|
32
31
|
import { SettingsTabs } from '@/store/global/initialState';
|
|
33
32
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
34
33
|
import { useUserStore } from '@/store/user';
|
|
35
|
-
import {
|
|
34
|
+
import { userProfileSelectors } from '@/store/user/slices/auth/selectors';
|
|
36
35
|
|
|
37
36
|
export enum SettingsGroupKey {
|
|
38
37
|
AIConfig = 'ai-config',
|
|
@@ -61,8 +60,7 @@ export const useCategory = () => {
|
|
|
61
60
|
const mobile = useServerConfigStore((s) => s.isMobile);
|
|
62
61
|
const { enableSTT, hideDocs, showAiImage, showApiKeyManage } =
|
|
63
62
|
useServerConfigStore(featureFlagsSelectors);
|
|
64
|
-
const [
|
|
65
|
-
authSelectors.isLoginWithClerk(s),
|
|
63
|
+
const [avatar, username] = useUserStore((s) => [
|
|
66
64
|
userProfileSelectors.userAvatar(s),
|
|
67
65
|
userProfileSelectors.nickName(s),
|
|
68
66
|
]);
|
|
@@ -87,11 +85,6 @@ export const useCategory = () => {
|
|
|
87
85
|
key: SettingsTabs.Profile,
|
|
88
86
|
label: username ? username : tAuth('tab.profile'),
|
|
89
87
|
},
|
|
90
|
-
isLoginWithClerk && {
|
|
91
|
-
icon: ShieldCheck,
|
|
92
|
-
key: SettingsTabs.Security,
|
|
93
|
-
label: tAuth('tab.security'),
|
|
94
|
-
},
|
|
95
88
|
{
|
|
96
89
|
icon: ChartColumnBigIcon,
|
|
97
90
|
key: SettingsTabs.Stats,
|
|
@@ -237,18 +230,7 @@ export const useCategory = () => {
|
|
|
237
230
|
});
|
|
238
231
|
|
|
239
232
|
return groups;
|
|
240
|
-
}, [
|
|
241
|
-
t,
|
|
242
|
-
tAuth,
|
|
243
|
-
enableSTT,
|
|
244
|
-
hideDocs,
|
|
245
|
-
mobile,
|
|
246
|
-
showAiImage,
|
|
247
|
-
showApiKeyManage,
|
|
248
|
-
isLoginWithClerk,
|
|
249
|
-
avatarUrl,
|
|
250
|
-
username,
|
|
251
|
-
]);
|
|
233
|
+
}, [t, tAuth, enableSTT, hideDocs, mobile, showAiImage, showApiKeyManage, avatarUrl, username]);
|
|
252
234
|
|
|
253
235
|
return categoryGroups;
|
|
254
236
|
};
|
|
@@ -7,7 +7,6 @@ import { useCallback, useState } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
import { fetchErrorNotification } from '@/components/Error/fetchErrorNotification';
|
|
10
|
-
import { enableAuth } from '@/envs/auth';
|
|
11
10
|
import UserAvatar from '@/features/User/UserAvatar';
|
|
12
11
|
import { useUserStore } from '@/store/user';
|
|
13
12
|
import { authSelectors } from '@/store/user/selectors';
|
|
@@ -54,7 +53,7 @@ const AvatarRow = ({ mobile }: AvatarRowProps) => {
|
|
|
54
53
|
[updateAvatar],
|
|
55
54
|
);
|
|
56
55
|
|
|
57
|
-
const canUpload =
|
|
56
|
+
const canUpload = isLogin;
|
|
58
57
|
|
|
59
58
|
const avatarContent = canUpload ? (
|
|
60
59
|
<Spin indicator={<LoadingOutlined spin />} spinning={uploading}>
|