mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
🐛 fix: fix open chat page with float link modal (#9235)
* refactor @lobechat/database * move config/file and llm to envs * move config/auth to envs * refactor * fix tests * fix tests * upgrade
This commit is contained in:
+95
-93
@@ -2,117 +2,115 @@
|
||||
globs: *.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
# LobeChat 国际化指南
|
||||
# LobeChat Internationalization Guide
|
||||
|
||||
## 架构概览
|
||||
## Key Points
|
||||
|
||||
LobeChat 使用 react-i18next 进行国际化,采用良好的命名空间架构:
|
||||
- Default language: Chinese (zh-CN) as the source language
|
||||
- Supported languages: 18 languages including English, Japanese, Korean, Arabic, etc.
|
||||
- Framework: react-i18next with Next.js app router
|
||||
- Translation automation: @lobehub/i18n-cli for automatic translation, config file: .i18nrc.js
|
||||
- Never manually modify any json file. You can only modify files in `default` folder
|
||||
|
||||
- 默认语言:中文(zh-CN),作为源语言
|
||||
- 支持语言:18 种语言,包括英语、日语、韩语、阿拉伯语等
|
||||
- 框架:react-i18next 配合 Next.js app router
|
||||
- 翻译自动化:@lobehub/i18n-cli 用于自动翻译,配置文件:.i18nrc.js
|
||||
|
||||
## 目录结构
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/locales/
|
||||
├── default/ # 源语言文件(zh-CN)
|
||||
│ ├── index.ts # 命名空间导出
|
||||
│ ├── common.ts # 通用翻译
|
||||
│ ├── chat.ts # 聊天相关翻译
|
||||
│ ├── setting.ts # 设置翻译
|
||||
│ └── ... # 其他命名空间文件
|
||||
└── resources.ts # 类型定义和语言配置
|
||||
├── default/ # Source language files (zh-CN)
|
||||
│ ├── index.ts # Namespace exports
|
||||
│ ├── common.ts # Common translations
|
||||
│ ├── chat.ts # Chat-related translations
|
||||
│ ├── setting.ts # Settings translations
|
||||
│ └── ... # Other namespace files
|
||||
└── resources.ts # Type definitions and language configuration
|
||||
|
||||
locales/ # 翻译文件
|
||||
├── en-US/ # 英语翻译
|
||||
│ ├── common.json # 通用翻译
|
||||
│ ├── chat.json # 聊天翻译
|
||||
│ ├── setting.json # 设置翻译
|
||||
│ └── ... # 其他命名空间 JSON 文件
|
||||
├── ja-JP/ # 日语翻译
|
||||
locales/ # Translation files
|
||||
├── en-US/ # English translations
|
||||
│ ├── common.json # Common translations
|
||||
│ ├── chat.json # Chat translations
|
||||
│ ├── setting.json # Settings translations
|
||||
│ └── ... # Other namespace JSON files
|
||||
├── ja-JP/ # Japanese translations
|
||||
│ ├── common.json
|
||||
│ ├── chat.json
|
||||
│ └── ...
|
||||
└── ... # 其他语言文件夹
|
||||
└── ... # Other language folders
|
||||
```
|
||||
|
||||
## 添加新翻译的工作流程
|
||||
## Workflow for Adding New Translations
|
||||
|
||||
### 1. 添加新的翻译键
|
||||
### 1. Adding New Translation Keys
|
||||
|
||||
第一步:在 src/locales/default 目录下的相应命名空间文件中添加翻译键
|
||||
Step 1: Add translation keys in the corresponding namespace files under src/locales/default directory
|
||||
|
||||
```typescript
|
||||
// 示例:src/locales/default/common.ts
|
||||
// Example: src/locales/default/common.ts
|
||||
export default {
|
||||
// ... 现有键
|
||||
newFeature: {
|
||||
title: "新功能标题",
|
||||
description: "功能描述文案",
|
||||
button: "操作按钮",
|
||||
},
|
||||
// ... existing keys
|
||||
newFeature: {
|
||||
title: '新功能标题',
|
||||
description: '功能描述文案',
|
||||
button: '操作按钮',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
第二步:如果创建新命名空间,需要在 src/locales/default/index.ts 中导出
|
||||
Step 2: If creating a new namespace, export it in src/locales/default/index.ts
|
||||
|
||||
```typescript
|
||||
import newNamespace from "./newNamespace";
|
||||
import newNamespace from './newNamespace';
|
||||
|
||||
const resources = {
|
||||
// ... 现有命名空间
|
||||
newNamespace,
|
||||
// ... existing namespaces
|
||||
newNamespace,
|
||||
} as const;
|
||||
```
|
||||
|
||||
### 2. 翻译过程
|
||||
### 2. Translation Process
|
||||
|
||||
开发模式:
|
||||
Development mode:
|
||||
|
||||
一般情况下不需要你帮我跑自动翻译工具,跑一次很久,需要的时候我会自己跑。
|
||||
但是为了立马能看到效果,还是需要先翻译 `locales/zh-CN/namespace.json`,不需要翻译其它语言。
|
||||
Generally, you don't need to help me run the automatic translation tool as it takes a long time. I'll run it myself when needed. However, to see immediate results, you still need to translate `locales/zh-CN/namespace.json` first, no need to translate other languages.
|
||||
|
||||
生产模式:
|
||||
Production mode:
|
||||
|
||||
```bash
|
||||
# 为所有语言生成翻译
|
||||
# Generate translations for all languages
|
||||
npm run i18n
|
||||
```
|
||||
|
||||
## 在组件中使用
|
||||
## Usage in Components
|
||||
|
||||
### 基本用法
|
||||
### Basic Usage
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const MyComponent = () => {
|
||||
const { t } = useTranslation("common");
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{t("newFeature.title")}</h1>
|
||||
<p>{t("newFeature.description")}</p>
|
||||
<button>{t("newFeature.button")}</button>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<h1>{t('newFeature.title')}</h1>
|
||||
<p>{t('newFeature.description')}</p>
|
||||
<button>{t('newFeature.button')}</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 带参数的用法
|
||||
### Usage with Parameters
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation("common");
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
<p>{t("welcome.message", { name: "John" })}</p>;
|
||||
<p>{t('welcome.message', { name: 'John' })}</p>;
|
||||
|
||||
// 对应的语言文件:
|
||||
// welcome: { message: '欢迎 {{name}} 使用!' }
|
||||
// Corresponding language file:
|
||||
// welcome: { message: 'Welcome {{name}}!' }
|
||||
```
|
||||
|
||||
### 多个命名空间
|
||||
### Multiple Namespaces
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation(['common', 'chat']);
|
||||
@@ -121,59 +119,63 @@ const { t } = useTranslation(['common', 'chat']);
|
||||
<span>{t('chat:typing')}</span>
|
||||
```
|
||||
|
||||
## 类型安全
|
||||
## Type Safety
|
||||
|
||||
项目使用 TypeScript 实现类型安全的翻译,类型从 src/locales/resources.ts 自动生成:
|
||||
The project uses TypeScript to implement type-safe translations, with types automatically generated from src/locales/resources.ts:
|
||||
|
||||
```typescript
|
||||
import type { DefaultResources, NS, Locales } from "@/locales/resources";
|
||||
import type { DefaultResources, Locales, NS } from '@/locales/resources';
|
||||
|
||||
// 可用类型:
|
||||
// - NS: 可用命名空间键 ('common' | 'chat' | 'setting' | ...)
|
||||
// - Locales: 支持的语言代码 ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||
// Available types:
|
||||
// - NS: Available namespace keys ('common' | 'chat' | 'setting' | ...)
|
||||
// - Locales: Supported language codes ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||
|
||||
const namespace: NS = "common";
|
||||
const locale: Locales = "en-US";
|
||||
const namespace: NS = 'common';
|
||||
const locale: Locales = 'en-US';
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
## Best Practices
|
||||
|
||||
### 1. 命名空间组织
|
||||
### 1. Namespace Organization
|
||||
|
||||
- common: 共享 UI 元素(按钮、标签、操作)
|
||||
- chat: 聊天特定功能
|
||||
- setting: 配置和设置
|
||||
- error: 错误消息和处理
|
||||
- [feature]: 功能特定或页面特定的命名空间
|
||||
- components: 可复用组件文案
|
||||
- common: Shared UI elements (buttons, labels, actions)
|
||||
- chat: Chat-specific functionality
|
||||
- setting: Configuration and settings
|
||||
- error: Error messages and handling
|
||||
- [feature]: Feature-specific or page-specific namespaces
|
||||
- components: Reusable component text
|
||||
|
||||
### 2. 键命名约定
|
||||
### 2. Key Naming Conventions
|
||||
|
||||
```typescript
|
||||
// ✅ 好:层次结构
|
||||
// ✅ Good: Hierarchical structure
|
||||
export default {
|
||||
modal: {
|
||||
confirm: {
|
||||
title: "确认操作",
|
||||
message: "确定要执行此操作吗?",
|
||||
actions: {
|
||||
confirm: "确认",
|
||||
cancel: "取消",
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
confirm: {
|
||||
title: '确认操作',
|
||||
message: '确定要执行此操作吗?',
|
||||
actions: {
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ❌ 避免:扁平结构
|
||||
// ❌ Avoid: Flat structure
|
||||
export default {
|
||||
modalConfirmTitle: "确认操作",
|
||||
modalConfirmMessage: "确定要执行此操作吗?",
|
||||
modalConfirmTitle: '确认操作',
|
||||
modalConfirmMessage: '确定要执行此操作吗?',
|
||||
};
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
## Troubleshooting
|
||||
|
||||
### 缺少翻译键
|
||||
### Missing Translation Keys
|
||||
|
||||
- Check if the key exists in src/locales/default/namespace.ts
|
||||
- Ensure the namespace is correctly imported in the component
|
||||
- Ensure new namespaces are exported in src/locales/default/index.ts
|
||||
|
||||
- 检查键是否存在于 src/locales/default/namespace.ts 中
|
||||
- 确保在组件中正确导入命名空间
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@
|
||||
"@lobehub/charts": "^2.1.2",
|
||||
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
||||
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
||||
"@lobehub/editor": "^1.8.0",
|
||||
"@lobehub/editor": "^1.8.5",
|
||||
"@lobehub/icons": "^2.32.2",
|
||||
"@lobehub/market-sdk": "^0.22.7",
|
||||
"@lobehub/tts": "^2.0.1",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
export const enableClerk = authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH;
|
||||
export const enableNextAuth = authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
"name": "@lobechat/database",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./schemas": "./src/schemas/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run test:client-db && npm run test:server-db",
|
||||
"test:client-db": "vitest run",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './type';
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { clientDB, initializeDB } from '@/database/client/db';
|
||||
|
||||
import {
|
||||
agents,
|
||||
agentsKnowledgeBases,
|
||||
@@ -16,9 +17,8 @@ import {
|
||||
topics,
|
||||
userSettings,
|
||||
users,
|
||||
} from '@/database/schemas';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
|
||||
} from '../../schemas';
|
||||
import { LobeChatDatabase } from '../../type';
|
||||
import { DATA_EXPORT_CONFIG, DataExporterRepos } from './index';
|
||||
|
||||
let db = clientDB as LobeChatDatabase;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ChatErrorType } from '@lobechat/types';
|
||||
import OpenAI, { ClientOptions } from 'openai';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { getLLMConfig } from '@/config/llm';
|
||||
import { getLLMConfig } from '@/envs/llm';
|
||||
|
||||
// create Azure OpenAI Instance
|
||||
export const createAzureOpenai = (params: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChatErrorType } from '@lobechat/types';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
import { getLLMConfig } from '@/config/llm';
|
||||
import { getLLMConfig } from '@/envs/llm';
|
||||
|
||||
// create OpenAI instance
|
||||
export const createOpenai = (userApiKey: string | null, endpoint?: string | null) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { serverDB } from '@/database/server';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
import { pino } from '@/libs/logger';
|
||||
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { headers } from 'next/headers';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
export type CasdoorUserEntity = {
|
||||
avatar?: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { isServerMode } from '@/const/version';
|
||||
import { serverDB } from '@/database/server';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
import { pino } from '@/libs/logger';
|
||||
import { UserService } from '@/server/services/user';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { serverDB } from '@/database/server';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
import { pino } from '@/libs/logger';
|
||||
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { headers } from 'next/headers';
|
||||
import { createHmac } from 'node:crypto';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
export type LogtToUserEntity = {
|
||||
applicationId?: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @vitest-environment node
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { analyticsEnv, getAnalyticsConfig } from '../../envs/analytics';
|
||||
import { analyticsEnv, getAnalyticsConfig } from '../analytics';
|
||||
|
||||
beforeEach(() => {
|
||||
// 在每个测试用例之前,清除所有的 console.warn mock
|
||||
@@ -1,7 +1,7 @@
|
||||
// @vitest-environment node
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { getAppConfig } from '../../envs/app';
|
||||
import { getAppConfig } from '../app';
|
||||
|
||||
// Stub the global process object to safely mock environment variables
|
||||
vi.stubGlobal('process', {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { getDebugConfig } from '../../envs/debug';
|
||||
import { getDebugConfig } from '../debug';
|
||||
|
||||
// 测试前重置 process.env
|
||||
vi.stubGlobal('process', {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import Clerk from './Clerk';
|
||||
import NextAuth from './NextAuth';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextAuthConfig } from 'next-auth';
|
||||
|
||||
import { getAuthConfig } from '@/config/auth';
|
||||
import { getServerDBConfig } from '@/config/db';
|
||||
import { getAuthConfig } from '@/envs/auth';
|
||||
|
||||
import { LobeNextAuthDbAdapter } from './adapter';
|
||||
import { ssoProviders } from './sso-providers';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Auth0 from 'next-auth/providers/auth0';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OIDCConfig } from '@auth/core/providers';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Authentik from 'next-auth/providers/authentik';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AzureAD from 'next-auth/providers/azure-ad';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { getMicrosoftEntraIdIssuer } from './microsoft-entra-id-helper';
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OIDCConfig } from '@auth/core/providers';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OIDCConfig } from '@auth/core/providers';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import GitHub from 'next-auth/providers/github';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OIDCConfig, OIDCUserConfig } from '@auth/core/providers';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { CommonProviderConfig } from './sso.config';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
function getTenantId() {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Zitadel from 'next-auth/providers/zitadel';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
const provider = {
|
||||
id: 'zitadel',
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import debug from 'debug';
|
||||
import { eq, sql } from 'drizzle-orm';
|
||||
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import {
|
||||
oidcAccessTokens,
|
||||
oidcAuthorizationCodes,
|
||||
@@ -10,8 +8,9 @@ import {
|
||||
oidcInteractions,
|
||||
oidcRefreshTokens,
|
||||
oidcSessions,
|
||||
} from '@/database/schemas/oidc';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
} from '@lobechat/database/schemas';
|
||||
import debug from 'debug';
|
||||
import { eq, sql } from 'drizzle-orm';
|
||||
|
||||
// 创建 adapter 日志命名空间
|
||||
const log = debug('lobe-oidc:adapter');
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import debug from 'debug';
|
||||
import Provider, { Configuration, KoaContextWithOIDC, errors } from 'oidc-provider';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { serverDBEnv } from '@/config/db';
|
||||
import { UserModel } from '@/database/models/user';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { appEnv } from '@/envs/app';
|
||||
import { getJWKS } from '@/libs/oidc-provider/jwt';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import debug from 'debug';
|
||||
|
||||
import { serverDBEnv } from '@/config/db';
|
||||
import { UserModel } from '@/database/models/user';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
|
||||
import { asyncTrpc } from './init';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { ClientSecretPayload } from '@lobechat/types';
|
||||
import debug from 'debug';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
||||
|
||||
const log = debug('lobe-async:context');
|
||||
|
||||
+1
-1
@@ -5,11 +5,11 @@ import { NextRequest, NextResponse } from 'next/server';
|
||||
import { UAParser } from 'ua-parser-js';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { OAUTH_AUTHORIZED } from '@/const/auth';
|
||||
import { LOBE_LOCALE_COOKIE } from '@/const/locale';
|
||||
import { LOBE_THEME_APPEARANCE } from '@/const/theme';
|
||||
import { appEnv } from '@/envs/app';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
import NextAuth from '@/libs/next-auth';
|
||||
import { Locales } from '@/locales/resources';
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ vi.mock('@/config/modelProviders', () => ({
|
||||
}));
|
||||
|
||||
// Mock LLM config
|
||||
vi.mock('@/config/llm', () => ({
|
||||
vi.mock('@/envs/llm', () => ({
|
||||
getLLMConfig: () => ({
|
||||
ENABLED_AZURE_OPENAI: true,
|
||||
ENABLED_AWS_BEDROCK: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ModelProvider } from '@lobechat/model-runtime';
|
||||
|
||||
import { getLLMConfig } from '@/config/llm';
|
||||
import * as ProviderCards from '@/config/modelProviders';
|
||||
import { getLLMConfig } from '@/envs/llm';
|
||||
import { ModelProviderCard } from '@/types/llm';
|
||||
import { extractEnabledModels, transformToChatModelCards } from '@/utils/_deprecated/parseModels';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ vi.mock('model-bank', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/config/llm', () => ({
|
||||
vi.mock('@/envs/llm', () => ({
|
||||
getLLMConfig: vi.fn(() => ({
|
||||
ENABLED_OPENAI: true,
|
||||
ENABLED_ANTHROPIC: false,
|
||||
@@ -83,7 +83,7 @@ describe('genServerAiProvidersConfig', () => {
|
||||
};
|
||||
|
||||
// Mock the LLM config to include our custom key
|
||||
const { getLLMConfig } = vi.mocked(await import('@/config/llm'));
|
||||
const { getLLMConfig } = vi.mocked(await import('@/envs/llm'));
|
||||
getLLMConfig.mockReturnValue({
|
||||
ENABLED_OPENAI: true,
|
||||
ENABLED_ANTHROPIC: false,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { extractEnabledModels, transformToAiModelList } from '@lobechat/utils';
|
||||
import * as AiModels from 'model-bank';
|
||||
import { AiFullModelCard } from 'model-bank';
|
||||
|
||||
import { getLLMConfig } from '@/config/llm';
|
||||
import { getLLMConfig } from '@/envs/llm';
|
||||
|
||||
interface ProviderSpecificConfig {
|
||||
enabled?: boolean;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { authEnv } from '@/config/auth';
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { enableNextAuth } from '@/const/auth';
|
||||
import { isDesktop } from '@/const/version';
|
||||
import { appEnv, getAppConfig } from '@/envs/app';
|
||||
import { authEnv } from '@/envs/auth';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { knowledgeEnv } from '@/envs/knowledge';
|
||||
import { langfuseEnv } from '@/envs/langfuse';
|
||||
import { parseSystemAgent } from '@/server/globalConfig/parseSystemAgent';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getLLMConfig } from '@/config/llm';
|
||||
import { getLLMConfig } from '@/envs/llm';
|
||||
|
||||
interface KeyStore {
|
||||
index: number;
|
||||
|
||||
@@ -27,7 +27,7 @@ import { describe, expect, it, vi } from 'vitest';
|
||||
import { initModelRuntimeWithUserPayload } from './index';
|
||||
|
||||
// 模拟依赖项
|
||||
vi.mock('@/config/llm', () => ({
|
||||
vi.mock('@/envs/llm', () => ({
|
||||
getLLMConfig: vi.fn(() => ({
|
||||
// 确保为每个provider提供必要的配置信息
|
||||
OPENAI_API_KEY: 'test-openai-key',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ModelProvider, ModelRuntime } from '@lobechat/model-runtime';
|
||||
import { ClientSecretPayload } from '@lobechat/types';
|
||||
|
||||
import { getLLMConfig } from '@/config/llm';
|
||||
import { getLLMConfig } from '@/envs/llm';
|
||||
|
||||
import apiKeyManager from './apiKeyManager';
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { YEAR } from '@/utils/units';
|
||||
import { inferContentTypeFromImageUrl } from '@/utils/url';
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import pMap from 'p-map';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { serverDBEnv } from '@/config/db';
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@/const/settings/knowledge';
|
||||
import { ASYNC_TASK_TIMEOUT, AsyncTaskModel } from '@/database/models/asyncTask';
|
||||
import { ChunkModel } from '@/database/models/chunk';
|
||||
import { EmbeddingModel } from '@/database/models/embedding';
|
||||
import { FileModel } from '@/database/models/file';
|
||||
import { NewChunkItem, NewEmbeddingsItem } from '@/database/schemas';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { asyncAuthedProcedure, asyncRouter as router } from '@/libs/trpc/async';
|
||||
import { getServerDefaultFilesConfig } from '@/server/globalConfig';
|
||||
import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
|
||||
import { SessionModel } from '@/database/models/session';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { getServerDefaultAgentConfig } from '@/server/globalConfig';
|
||||
|
||||
export class AgentService {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { MessageModel } from '@/database/models/message';
|
||||
import { TopicModel } from '@/database/models/topic';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { FileService } from '@/server/services/file';
|
||||
|
||||
import { AiChatService } from '.';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
|
||||
import { MessageModel } from '@/database/models/message';
|
||||
import { TopicModel } from '@/database/models/topic';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { FileService } from '@/server/services/file';
|
||||
|
||||
export class AiChatService {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { ClientSecretPayload } from '@lobechat/types';
|
||||
|
||||
import { AsyncTaskModel } from '@/database/models/asyncTask';
|
||||
import { FileModel } from '@/database/models/file';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { ChunkContentParams, ContentChunk } from '@/server/modules/ContentChunk';
|
||||
import { createAsyncCaller } from '@/server/routers/async';
|
||||
import {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { loadFile } from '@lobechat/file-loaders';
|
||||
import debug from 'debug';
|
||||
|
||||
import { DocumentModel } from '@/database/models/document';
|
||||
import { FileModel } from '@/database/models/file';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { LobeDocument } from '@/types/document';
|
||||
|
||||
import { FileService } from '../file';
|
||||
|
||||
@@ -10,7 +10,7 @@ const config = {
|
||||
};
|
||||
|
||||
// 模拟 fileEnv
|
||||
vi.mock('@/config/file', () => ({
|
||||
vi.mock('@/envs/file', () => ({
|
||||
get fileEnv() {
|
||||
return config;
|
||||
},
|
||||
@@ -71,12 +71,12 @@ describe('S3StaticFileImpl', () => {
|
||||
it('should handle full URL input by extracting key (S3_SET_ACL=false)', async () => {
|
||||
config.S3_SET_ACL = false;
|
||||
const fullUrl = 'https://s3.example.com/bucket/path/to/file.jpg?X-Amz-Signature=expired';
|
||||
|
||||
|
||||
// Mock getKeyFromFullUrl to return the extracted key
|
||||
vi.spyOn(fileService, 'getKeyFromFullUrl').mockReturnValue('path/to/file.jpg');
|
||||
|
||||
|
||||
const result = await fileService.getFullFileUrl(fullUrl);
|
||||
|
||||
|
||||
expect(fileService.getKeyFromFullUrl).toHaveBeenCalledWith(fullUrl);
|
||||
expect(result).toBe('https://presigned.example.com/test.jpg');
|
||||
config.S3_SET_ACL = true;
|
||||
@@ -84,33 +84,33 @@ describe('S3StaticFileImpl', () => {
|
||||
|
||||
it('should handle full URL input by extracting key (S3_SET_ACL=true)', async () => {
|
||||
const fullUrl = 'https://s3.example.com/bucket/path/to/file.jpg';
|
||||
|
||||
|
||||
vi.spyOn(fileService, 'getKeyFromFullUrl').mockReturnValue('path/to/file.jpg');
|
||||
|
||||
|
||||
const result = await fileService.getFullFileUrl(fullUrl);
|
||||
|
||||
|
||||
expect(fileService.getKeyFromFullUrl).toHaveBeenCalledWith(fullUrl);
|
||||
expect(result).toBe('https://example.com/path/to/file.jpg');
|
||||
});
|
||||
|
||||
it('should handle normal key input without extraction', async () => {
|
||||
const key = 'path/to/file.jpg';
|
||||
|
||||
|
||||
const spy = vi.spyOn(fileService, 'getKeyFromFullUrl');
|
||||
|
||||
|
||||
const result = await fileService.getFullFileUrl(key);
|
||||
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
expect(result).toBe('https://example.com/path/to/file.jpg');
|
||||
});
|
||||
|
||||
it('should handle http:// URLs for legacy compatibility', async () => {
|
||||
const httpUrl = 'http://s3.example.com/bucket/path/to/file.jpg';
|
||||
|
||||
|
||||
vi.spyOn(fileService, 'getKeyFromFullUrl').mockReturnValue('path/to/file.jpg');
|
||||
|
||||
|
||||
const result = await fileService.getFullFileUrl(httpUrl);
|
||||
|
||||
|
||||
expect(fileService.getKeyFromFullUrl).toHaveBeenCalledWith(httpUrl);
|
||||
expect(result).toBe('https://example.com/path/to/file.jpg');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { S3 } from '@/server/modules/S3';
|
||||
|
||||
import { FileServiceImpl } from './type';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { serverDBEnv } from '@/config/db';
|
||||
import { FileModel } from '@/database/models/file';
|
||||
import { FileItem } from '@/database/schemas';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { TempFileManager } from '@/server/utils/tempFileManager';
|
||||
import { nanoid } from '@/utils/uuid';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { parseDataUri } from '@lobechat/model-runtime';
|
||||
import debug from 'debug';
|
||||
import { sha256 } from 'js-sha256';
|
||||
@@ -6,7 +7,6 @@ import { IMAGE_GENERATION_CONFIG } from 'model-bank';
|
||||
import { nanoid } from 'nanoid';
|
||||
import sharp from 'sharp';
|
||||
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { FileService } from '@/server/services/file';
|
||||
import { calculateThumbnailDimensions } from '@/utils/number';
|
||||
import { getYYYYmmddHHMMss } from '@/utils/time';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import { Adapter, AdapterAccount } from 'next-auth/adapters';
|
||||
import { NextResponse } from 'next/server';
|
||||
@@ -11,7 +12,6 @@ import {
|
||||
nextauthVerificationTokens,
|
||||
users,
|
||||
} from '@/database/schemas';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { pino } from '@/libs/logger';
|
||||
import { merge } from '@/utils/merge';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { UserJSON } from '@clerk/backend';
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { UserModel } from '@/database/models/user';
|
||||
import { UserItem } from '@/database/schemas';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { pino } from '@/libs/logger';
|
||||
import { AgentService } from '@/server/services/agent';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { UserJSON } from '@clerk/backend';
|
||||
import { LobeChatDatabase } from '@lobechat/database';
|
||||
|
||||
import { UserModel } from '@/database/models/user';
|
||||
import { LobeChatDatabase } from '@/database/type';
|
||||
import { initializeServerAnalytics } from '@/libs/analytics';
|
||||
import { pino } from '@/libs/logger';
|
||||
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { edgeClient } from '@/libs/trpc/client';
|
||||
import { API_ENDPOINTS } from '@/services/_url';
|
||||
import { clientS3Storage } from '@/services/file/ClientS3';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Mock, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { FileModel } from '@/database/_deprecated/models/file';
|
||||
import { DB_File } from '@/database/_deprecated/schemas/files';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { clientS3Storage } from '@/services/file/ClientS3';
|
||||
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
||||
import { createServerConfigStore } from '@/store/serverConfig/store';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { uuid } from '@lobechat/utils';
|
||||
import dayjs from 'dayjs';
|
||||
import { sha256 } from 'js-sha256';
|
||||
|
||||
import { fileEnv } from '@/config/file';
|
||||
import { fileEnv } from '@/envs/file';
|
||||
import { edgeClient } from '@/libs/trpc/client';
|
||||
import { API_ENDPOINTS } from '@/services/_url';
|
||||
import { clientS3Storage } from '@/services/file/ClientS3';
|
||||
|
||||
Reference in New Issue
Block a user