@lobehub/lobehub 2.0.0-next.195 → 2.0.0-next.196
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/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/locales/ar/setting.json +0 -3
- package/locales/bg-BG/setting.json +0 -3
- package/locales/de-DE/setting.json +0 -3
- package/locales/en-US/setting.json +0 -3
- package/locales/es-ES/setting.json +0 -3
- package/locales/fa-IR/setting.json +0 -3
- package/locales/fr-FR/setting.json +0 -3
- package/locales/it-IT/setting.json +0 -3
- package/locales/ja-JP/setting.json +0 -3
- package/locales/ko-KR/setting.json +0 -3
- package/locales/nl-NL/setting.json +0 -3
- package/locales/pl-PL/setting.json +0 -3
- package/locales/pt-BR/setting.json +0 -3
- package/locales/ru-RU/setting.json +0 -3
- package/locales/tr-TR/setting.json +0 -3
- package/locales/vi-VN/setting.json +0 -3
- package/locales/zh-CN/setting.json +0 -3
- package/locales/zh-TW/setting.json +0 -3
- package/package.json +1 -1
- package/packages/const/src/fetch.ts +1 -4
- package/packages/types/src/auth.ts +0 -4
- package/packages/utils/src/server/xor.test.ts +1 -2
- package/src/app/(backend)/_deprecated/createBizOpenAI/auth.test.ts +7 -41
- package/src/app/(backend)/_deprecated/createBizOpenAI/auth.ts +1 -15
- package/src/app/(backend)/_deprecated/createBizOpenAI/index.ts +2 -9
- package/src/app/(backend)/middleware/auth/index.ts +0 -1
- package/src/app/(backend)/middleware/auth/utils.test.ts +2 -42
- package/src/app/(backend)/middleware/auth/utils.ts +3 -17
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +0 -5
- package/src/app/(backend)/webapi/models/[provider]/route.test.ts +0 -6
- package/src/app/(backend)/webapi/plugin/gateway/route.ts +2 -32
- package/src/app/[variants]/(main)/settings/common/features/Common/Common.tsx +1 -16
- package/src/locales/default/setting.ts +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.196](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.195...v2.0.0-next.196)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-03**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor to remove access code.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor to remove access code, closes [#11120](https://github.com/lobehub/lobe-chat/issues/11120) ([0e9f98c](https://github.com/lobehub/lobe-chat/commit/0e9f98c))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.195](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.194...v2.0.0-next.195)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-03**</sup>
|
package/changelog/v1.json
CHANGED
package/locales/ar/setting.json
CHANGED
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "الأسئلة الافتتاحية",
|
|
385
385
|
"settingOpening.title": "إعدادات البداية",
|
|
386
386
|
"settingPlugin.title": "قائمة المهارات",
|
|
387
|
-
"settingSystem.accessCode.desc": "تم تفعيل الوصول المشفر من قبل المسؤول",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "أدخل كلمة مرور الوصول",
|
|
389
|
-
"settingSystem.accessCode.title": "كلمة مرور الوصول",
|
|
390
387
|
"settingSystem.oauth.info.desc": "تم تسجيل الدخول",
|
|
391
388
|
"settingSystem.oauth.info.title": "معلومات الحساب",
|
|
392
389
|
"settingSystem.oauth.signin.action": "تسجيل الدخول",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Начални въпроси",
|
|
385
385
|
"settingOpening.title": "Настройки за начало",
|
|
386
386
|
"settingPlugin.title": "Списък с умения",
|
|
387
|
-
"settingSystem.accessCode.desc": "Достъпът с шифроване е активиран от администратора",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Въведете парола за достъп",
|
|
389
|
-
"settingSystem.accessCode.title": "Парола за достъп",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Вход изпълнен",
|
|
391
388
|
"settingSystem.oauth.info.title": "Информация за акаунта",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Вход",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Einstiegsfragen",
|
|
385
385
|
"settingOpening.title": "Begrüßungseinstellungen",
|
|
386
386
|
"settingPlugin.title": "Fähigkeitenliste",
|
|
387
|
-
"settingSystem.accessCode.desc": "Zugriff mit Verschlüsselung ist vom Administrator aktiviert",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Zugangspasswort eingeben",
|
|
389
|
-
"settingSystem.accessCode.title": "Zugangspasswort",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Angemeldet",
|
|
391
388
|
"settingSystem.oauth.info.title": "Kontoinformationen",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Anmelden",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Opening Questions",
|
|
385
385
|
"settingOpening.title": "Opening Settings",
|
|
386
386
|
"settingPlugin.title": "Skill List",
|
|
387
|
-
"settingSystem.accessCode.desc": "Encryption access is enabled by the administrator",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Enter access password",
|
|
389
|
-
"settingSystem.accessCode.title": "Access Password",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Logged in",
|
|
391
388
|
"settingSystem.oauth.info.title": "Account Information",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Sign In",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Preguntas de Apertura",
|
|
385
385
|
"settingOpening.title": "Configuración de Apertura",
|
|
386
386
|
"settingPlugin.title": "Lista de Habilidades",
|
|
387
|
-
"settingSystem.accessCode.desc": "El administrador ha habilitado el acceso cifrado",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Introduce la contraseña de acceso",
|
|
389
|
-
"settingSystem.accessCode.title": "Contraseña de Acceso",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Sesión iniciada",
|
|
391
388
|
"settingSystem.oauth.info.title": "Información de la Cuenta",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Iniciar Sesión",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "سؤالات آغازین",
|
|
385
385
|
"settingOpening.title": "تنظیمات آغازین",
|
|
386
386
|
"settingPlugin.title": "فهرست مهارتها",
|
|
387
|
-
"settingSystem.accessCode.desc": "دسترسی رمزگذاریشده توسط مدیر فعال شده است",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "رمز عبور دسترسی را وارد کنید",
|
|
389
|
-
"settingSystem.accessCode.title": "رمز عبور دسترسی",
|
|
390
387
|
"settingSystem.oauth.info.desc": "وارد شدهاید",
|
|
391
388
|
"settingSystem.oauth.info.title": "اطلاعات حساب",
|
|
392
389
|
"settingSystem.oauth.signin.action": "ورود",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Questions d’accueil",
|
|
385
385
|
"settingOpening.title": "Paramètres d’accueil",
|
|
386
386
|
"settingPlugin.title": "Liste des compétences",
|
|
387
|
-
"settingSystem.accessCode.desc": "L’accès chiffré est activé par l’administrateur",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Saisir le mot de passe d’accès",
|
|
389
|
-
"settingSystem.accessCode.title": "Mot de passe d’accès",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Connecté",
|
|
391
388
|
"settingSystem.oauth.info.title": "Informations du compte",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Se connecter",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Domande di Apertura",
|
|
385
385
|
"settingOpening.title": "Impostazioni di Apertura",
|
|
386
386
|
"settingPlugin.title": "Elenco Competenze",
|
|
387
|
-
"settingSystem.accessCode.desc": "L'accesso crittografato è abilitato dall'amministratore",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Inserisci la password di accesso",
|
|
389
|
-
"settingSystem.accessCode.title": "Password di Accesso",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Accesso effettuato",
|
|
391
388
|
"settingSystem.oauth.info.title": "Informazioni Account",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Accedi",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "オープニング質問",
|
|
385
385
|
"settingOpening.title": "オープニング設定",
|
|
386
386
|
"settingPlugin.title": "スキルリスト",
|
|
387
|
-
"settingSystem.accessCode.desc": "管理者が暗号化アクセスを有効にしています",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "アクセスコードを入力してください",
|
|
389
|
-
"settingSystem.accessCode.title": "アクセスコード",
|
|
390
387
|
"settingSystem.oauth.info.desc": "ログイン済み",
|
|
391
388
|
"settingSystem.oauth.info.title": "アカウント情報",
|
|
392
389
|
"settingSystem.oauth.signin.action": "ログイン",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "오프닝 질문",
|
|
385
385
|
"settingOpening.title": "오프닝 설정",
|
|
386
386
|
"settingPlugin.title": "기능 목록",
|
|
387
|
-
"settingSystem.accessCode.desc": "관리자가 암호화된 액세스를 활성화했습니다",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "액세스 암호를 입력하세요",
|
|
389
|
-
"settingSystem.accessCode.title": "액세스 암호",
|
|
390
387
|
"settingSystem.oauth.info.desc": "로그인됨",
|
|
391
388
|
"settingSystem.oauth.info.title": "계정 정보",
|
|
392
389
|
"settingSystem.oauth.signin.action": "로그인",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Openingsvragen",
|
|
385
385
|
"settingOpening.title": "Openingsinstellingen",
|
|
386
386
|
"settingPlugin.title": "Vaardighedenlijst",
|
|
387
|
-
"settingSystem.accessCode.desc": "Versleutelde toegang is ingeschakeld door de beheerder",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Voer toegangswachtwoord in",
|
|
389
|
-
"settingSystem.accessCode.title": "Toegangswachtwoord",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Ingelogd",
|
|
391
388
|
"settingSystem.oauth.info.title": "Accountinformatie",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Inloggen",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Pytania wprowadzające",
|
|
385
385
|
"settingOpening.title": "Ustawienia powitania",
|
|
386
386
|
"settingPlugin.title": "Lista umiejętności",
|
|
387
|
-
"settingSystem.accessCode.desc": "Dostęp szyfrowany został włączony przez administratora",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Wprowadź hasło dostępu",
|
|
389
|
-
"settingSystem.accessCode.title": "Hasło dostępu",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Zalogowano",
|
|
391
388
|
"settingSystem.oauth.info.title": "Informacje o koncie",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Zaloguj się",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Perguntas de Abertura",
|
|
385
385
|
"settingOpening.title": "Configurações de Abertura",
|
|
386
386
|
"settingPlugin.title": "Lista de Habilidades",
|
|
387
|
-
"settingSystem.accessCode.desc": "O acesso criptografado foi ativado pelo administrador",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Digite a senha de acesso",
|
|
389
|
-
"settingSystem.accessCode.title": "Senha de Acesso",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Conectado",
|
|
391
388
|
"settingSystem.oauth.info.title": "Informações da Conta",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Entrar",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Вводные вопросы",
|
|
385
385
|
"settingOpening.title": "Настройки приветствия",
|
|
386
386
|
"settingPlugin.title": "Список навыков",
|
|
387
|
-
"settingSystem.accessCode.desc": "Доступ по паролю включён администратором",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Введите пароль доступа",
|
|
389
|
-
"settingSystem.accessCode.title": "Пароль доступа",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Вы вошли в систему",
|
|
391
388
|
"settingSystem.oauth.info.title": "Информация об аккаунте",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Войти",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Açılış Soruları",
|
|
385
385
|
"settingOpening.title": "Açılış Ayarları",
|
|
386
386
|
"settingPlugin.title": "Yetenek Listesi",
|
|
387
|
-
"settingSystem.accessCode.desc": "Şifreli erişim, yönetici tarafından etkinleştirilmiştir",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Erişim şifresini girin",
|
|
389
|
-
"settingSystem.accessCode.title": "Erişim Şifresi",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Giriş yapıldı",
|
|
391
388
|
"settingSystem.oauth.info.title": "Hesap Bilgileri",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Giriş Yap",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "Câu Hỏi Mở Đầu",
|
|
385
385
|
"settingOpening.title": "Cài Đặt Mở Đầu",
|
|
386
386
|
"settingPlugin.title": "Danh Sách Kỹ Năng",
|
|
387
|
-
"settingSystem.accessCode.desc": "Mã truy cập được mã hóa do quản trị viên bật",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "Nhập mật khẩu truy cập",
|
|
389
|
-
"settingSystem.accessCode.title": "Mật Khẩu Truy Cập",
|
|
390
387
|
"settingSystem.oauth.info.desc": "Đã đăng nhập",
|
|
391
388
|
"settingSystem.oauth.info.title": "Thông Tin Tài Khoản",
|
|
392
389
|
"settingSystem.oauth.signin.action": "Đăng Nhập",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "开场问题",
|
|
385
385
|
"settingOpening.title": "开场设置",
|
|
386
386
|
"settingPlugin.title": "技能列表",
|
|
387
|
-
"settingSystem.accessCode.desc": "管理员已开启加密访问",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "请输入访问密码",
|
|
389
|
-
"settingSystem.accessCode.title": "访问密码",
|
|
390
387
|
"settingSystem.oauth.info.desc": "已登录",
|
|
391
388
|
"settingSystem.oauth.info.title": "账户信息",
|
|
392
389
|
"settingSystem.oauth.signin.action": "登录",
|
|
@@ -384,9 +384,6 @@
|
|
|
384
384
|
"settingOpening.openingQuestions.title": "開場問題",
|
|
385
385
|
"settingOpening.title": "開場設定",
|
|
386
386
|
"settingPlugin.title": "插件列表",
|
|
387
|
-
"settingSystem.accessCode.desc": "管理員已開啟加密訪問",
|
|
388
|
-
"settingSystem.accessCode.placeholder": "請輸入訪問密碼",
|
|
389
|
-
"settingSystem.accessCode.title": "訪問密碼",
|
|
390
387
|
"settingSystem.oauth.info.desc": "已登錄",
|
|
391
388
|
"settingSystem.oauth.info.title": "帳戶資訊",
|
|
392
389
|
"settingSystem.oauth.signin.action": "登錄",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.196",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -6,8 +6,6 @@ export const USE_AZURE_OPENAI = 'X-use-azure-openai';
|
|
|
6
6
|
|
|
7
7
|
export const AZURE_OPENAI_API_VERSION = 'X-azure-openai-api-version';
|
|
8
8
|
|
|
9
|
-
export const LOBE_CHAT_ACCESS_CODE = 'X-lobe-chat-access-code';
|
|
10
|
-
|
|
11
9
|
export const OAUTH_AUTHORIZED = 'X-oauth-authorized';
|
|
12
10
|
|
|
13
11
|
/**
|
|
@@ -16,7 +14,6 @@ export const OAUTH_AUTHORIZED = 'X-oauth-authorized';
|
|
|
16
14
|
export const getOpenAIAuthFromRequest = (req: Request) => {
|
|
17
15
|
const apiKey = req.headers.get(OPENAI_API_KEY_HEADER_KEY);
|
|
18
16
|
const endpoint = req.headers.get(OPENAI_END_POINT);
|
|
19
|
-
const accessCode = req.headers.get(LOBE_CHAT_ACCESS_CODE);
|
|
20
17
|
const useAzureStr = req.headers.get(USE_AZURE_OPENAI);
|
|
21
18
|
const apiVersion = req.headers.get(AZURE_OPENAI_API_VERSION);
|
|
22
19
|
const oauthAuthorizedStr = req.headers.get(OAUTH_AUTHORIZED);
|
|
@@ -25,5 +22,5 @@ export const getOpenAIAuthFromRequest = (req: Request) => {
|
|
|
25
22
|
const oauthAuthorized = !!oauthAuthorizedStr;
|
|
26
23
|
const useAzure = !!useAzureStr;
|
|
27
24
|
|
|
28
|
-
return {
|
|
25
|
+
return { apiKey, apiVersion, endpoint, oauthAuthorized, useAzure, userId };
|
|
29
26
|
};
|
|
@@ -7,7 +7,6 @@ describe('getXorPayload', () => {
|
|
|
7
7
|
it('should correctly decode XOR obfuscated payload with user data', () => {
|
|
8
8
|
const originalPayload = {
|
|
9
9
|
userId: '001362c3-48c5-4635-bd3b-837bfff58fc0',
|
|
10
|
-
accessCode: 'test-access-code',
|
|
11
10
|
apiKey: 'test-api-key',
|
|
12
11
|
baseURL: 'https://api.example.com',
|
|
13
12
|
};
|
|
@@ -86,7 +85,7 @@ describe('getXorPayload', () => {
|
|
|
86
85
|
it('should handle payload with undefined values', () => {
|
|
87
86
|
const originalPayload = {
|
|
88
87
|
userId: 'test-user',
|
|
89
|
-
|
|
88
|
+
baseURL: undefined,
|
|
90
89
|
apiKey: 'test-key',
|
|
91
90
|
};
|
|
92
91
|
|
|
@@ -1,53 +1,19 @@
|
|
|
1
1
|
// @vitest-environment node
|
|
2
2
|
import { checkAuth } from './auth';
|
|
3
3
|
|
|
4
|
-
describe('
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
auth = false;
|
|
9
|
-
process.env.ACCESS_CODE = undefined;
|
|
10
|
-
// Reset environment variables before each test case
|
|
11
|
-
vi.restoreAllMocks();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('set multiple access codes', () => {
|
|
15
|
-
process.env.ACCESS_CODE = ',code1,code2,code3';
|
|
16
|
-
({ auth } = checkAuth({ accessCode: 'code1' }));
|
|
17
|
-
expect(auth).toBe(true);
|
|
18
|
-
({ auth } = checkAuth({ accessCode: 'code2' }));
|
|
19
|
-
expect(auth).toBe(true);
|
|
20
|
-
({ auth } = checkAuth({ accessCode: 'code1,code2' }));
|
|
21
|
-
expect(auth).toBe(false);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('set individual access code', () => {
|
|
25
|
-
process.env.ACCESS_CODE = 'code1';
|
|
26
|
-
({ auth } = checkAuth({ accessCode: 'code1' }));
|
|
4
|
+
describe('checkAuth', () => {
|
|
5
|
+
it('should pass with oauth authorized', () => {
|
|
6
|
+
const { auth } = checkAuth({ oauthAuthorized: true });
|
|
27
7
|
expect(auth).toBe(true);
|
|
28
|
-
({ auth } = checkAuth({ accessCode: 'code2' }));
|
|
29
|
-
expect(auth).toBe(false);
|
|
30
8
|
});
|
|
31
9
|
|
|
32
|
-
it('
|
|
33
|
-
|
|
34
|
-
({ auth } = checkAuth({ accessCode: 'code1' }));
|
|
35
|
-
expect(auth).toBe(true);
|
|
36
|
-
({ auth } = checkAuth({}));
|
|
10
|
+
it('should pass with api key', () => {
|
|
11
|
+
const { auth } = checkAuth({ apiKey: 'test-api-key' });
|
|
37
12
|
expect(auth).toBe(true);
|
|
38
13
|
});
|
|
39
14
|
|
|
40
|
-
it('
|
|
41
|
-
|
|
42
|
-
({ auth } = checkAuth({ accessCode: 'code1' }));
|
|
43
|
-
expect(auth).toBe(true);
|
|
44
|
-
({ auth } = checkAuth({}));
|
|
45
|
-
expect(auth).toBe(true);
|
|
46
|
-
|
|
47
|
-
process.env.ACCESS_CODE = ',,';
|
|
48
|
-
({ auth } = checkAuth({ accessCode: 'code1' }));
|
|
49
|
-
expect(auth).toBe(true);
|
|
50
|
-
({ auth } = checkAuth({}));
|
|
15
|
+
it('should pass with no params', () => {
|
|
16
|
+
const { auth } = checkAuth({});
|
|
51
17
|
expect(auth).toBe(true);
|
|
52
18
|
});
|
|
53
19
|
});
|
|
@@ -1,32 +1,18 @@
|
|
|
1
|
-
import { ChatErrorType } from '@lobechat/types';
|
|
2
|
-
|
|
3
|
-
import { getAppConfig } from '@/envs/app';
|
|
4
|
-
|
|
5
1
|
interface AuthConfig {
|
|
6
|
-
accessCode?: string | null;
|
|
7
2
|
apiKey?: string | null;
|
|
8
3
|
oauthAuthorized?: boolean;
|
|
9
4
|
}
|
|
10
5
|
|
|
11
|
-
export const checkAuth = ({ apiKey,
|
|
6
|
+
export const checkAuth = ({ apiKey, oauthAuthorized }: AuthConfig) => {
|
|
12
7
|
// If authorized by oauth
|
|
13
8
|
if (oauthAuthorized) {
|
|
14
9
|
return { auth: true };
|
|
15
10
|
}
|
|
16
11
|
|
|
17
|
-
const { ACCESS_CODES } = getAppConfig();
|
|
18
|
-
|
|
19
12
|
// if apiKey exist
|
|
20
13
|
if (apiKey) {
|
|
21
14
|
return { auth: true };
|
|
22
15
|
}
|
|
23
16
|
|
|
24
|
-
// if accessCode doesn't exist
|
|
25
|
-
if (!ACCESS_CODES.length) return { auth: true };
|
|
26
|
-
|
|
27
|
-
if (!accessCode || !ACCESS_CODES.includes(accessCode)) {
|
|
28
|
-
return { auth: false, error: ChatErrorType.InvalidAccessCode };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
17
|
return { auth: true };
|
|
32
18
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { ChatErrorType
|
|
1
|
+
import { ChatErrorType } from '@lobechat/types';
|
|
2
2
|
import type OpenAI from 'openai';
|
|
3
3
|
|
|
4
4
|
import { getOpenAIAuthFromRequest } from '@/const/fetch';
|
|
5
5
|
import { createErrorResponse } from '@/utils/errorResponse';
|
|
6
6
|
|
|
7
|
-
import { checkAuth } from './auth';
|
|
8
7
|
import { createOpenai } from './createOpenai';
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -13,13 +12,7 @@ import { createOpenai } from './createOpenai';
|
|
|
13
12
|
* if auth not pass ,just return error response
|
|
14
13
|
*/
|
|
15
14
|
export const createBizOpenAI = (req: Request): Response | OpenAI => {
|
|
16
|
-
const { apiKey,
|
|
17
|
-
|
|
18
|
-
const result = checkAuth({ accessCode, apiKey, oauthAuthorized });
|
|
19
|
-
|
|
20
|
-
if (!result.auth) {
|
|
21
|
-
return createErrorResponse(result.error as ErrorType);
|
|
22
|
-
}
|
|
15
|
+
const { apiKey, endpoint } = getOpenAIAuthFromRequest(req);
|
|
23
16
|
|
|
24
17
|
let openai: OpenAI;
|
|
25
18
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { type AuthObject } from '@clerk/backend';
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
|
4
|
-
import { getAppConfig } from '@/envs/app';
|
|
5
|
-
|
|
6
4
|
import { checkAuthMethod } from './utils';
|
|
7
5
|
|
|
8
6
|
let enableClerkMock = false;
|
|
@@ -26,15 +24,9 @@ vi.mock('@/const/auth', async (importOriginal) => {
|
|
|
26
24
|
};
|
|
27
25
|
});
|
|
28
26
|
|
|
29
|
-
vi.mock('@/envs/app', () => ({
|
|
30
|
-
getAppConfig: vi.fn(),
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
27
|
describe('checkAuthMethod', () => {
|
|
34
28
|
beforeEach(() => {
|
|
35
|
-
vi.
|
|
36
|
-
ACCESS_CODES: ['validAccessCode'],
|
|
37
|
-
} as any);
|
|
29
|
+
vi.clearAllMocks();
|
|
38
30
|
});
|
|
39
31
|
|
|
40
32
|
it('should pass with valid Clerk auth', () => {
|
|
@@ -91,39 +83,7 @@ describe('checkAuthMethod', () => {
|
|
|
91
83
|
).not.toThrow();
|
|
92
84
|
});
|
|
93
85
|
|
|
94
|
-
it('should pass with no
|
|
95
|
-
vi.mocked(getAppConfig).mockReturnValueOnce({
|
|
96
|
-
ACCESS_CODES: [],
|
|
97
|
-
} as any);
|
|
98
|
-
|
|
86
|
+
it('should pass with no auth params', () => {
|
|
99
87
|
expect(() => checkAuthMethod({})).not.toThrow();
|
|
100
88
|
});
|
|
101
|
-
|
|
102
|
-
it('should pass with valid access code', () => {
|
|
103
|
-
expect(() =>
|
|
104
|
-
checkAuthMethod({
|
|
105
|
-
accessCode: 'validAccessCode',
|
|
106
|
-
}),
|
|
107
|
-
).not.toThrow();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should throw error with invalid access code', () => {
|
|
111
|
-
try {
|
|
112
|
-
checkAuthMethod({
|
|
113
|
-
accessCode: 'invalidAccessCode',
|
|
114
|
-
});
|
|
115
|
-
} catch (e) {
|
|
116
|
-
expect(e).toEqual({
|
|
117
|
-
errorType: 'InvalidAccessCode',
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
checkAuthMethod({});
|
|
123
|
-
} catch (e) {
|
|
124
|
-
expect(e).toEqual({
|
|
125
|
-
errorType: 'InvalidAccessCode',
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
89
|
});
|
|
@@ -3,28 +3,25 @@ import { AgentRuntimeError } from '@lobechat/model-runtime';
|
|
|
3
3
|
import { ChatErrorType } from '@lobechat/types';
|
|
4
4
|
|
|
5
5
|
import { enableBetterAuth, enableClerk, enableNextAuth } from '@/const/auth';
|
|
6
|
-
import { getAppConfig } from '@/envs/app';
|
|
7
6
|
|
|
8
7
|
interface CheckAuthParams {
|
|
9
|
-
accessCode?: string;
|
|
10
8
|
apiKey?: string;
|
|
11
9
|
betterAuthAuthorized?: boolean;
|
|
12
10
|
clerkAuth?: AuthObject;
|
|
13
11
|
nextAuthAuthorized?: boolean;
|
|
14
12
|
}
|
|
15
13
|
/**
|
|
16
|
-
* Check if
|
|
14
|
+
* Check if authentication is valid based on various auth methods.
|
|
17
15
|
*
|
|
18
16
|
* @param {CheckAuthParams} params - Authentication parameters extracted from headers.
|
|
19
|
-
* @param {string} [params.accessCode] - The access code to check.
|
|
20
17
|
* @param {string} [params.apiKey] - The user API key.
|
|
21
18
|
* @param {boolean} [params.betterAuthAuthorized] - Whether the Better Auth session exists.
|
|
22
19
|
* @param {AuthObject} [params.clerkAuth] - Clerk authentication payload from middleware.
|
|
23
20
|
* @param {boolean} [params.nextAuthAuthorized] - Whether the OAuth 2 header is provided.
|
|
24
|
-
* @throws {AgentRuntimeError} If
|
|
21
|
+
* @throws {AgentRuntimeError} If authentication fails.
|
|
25
22
|
*/
|
|
26
23
|
export const checkAuthMethod = (params: CheckAuthParams) => {
|
|
27
|
-
const { apiKey, betterAuthAuthorized, nextAuthAuthorized,
|
|
24
|
+
const { apiKey, betterAuthAuthorized, nextAuthAuthorized, clerkAuth } = params;
|
|
28
25
|
// clerk auth handler
|
|
29
26
|
if (enableClerk) {
|
|
30
27
|
// if there is no userId, means the use is not login, just throw error
|
|
@@ -42,15 +39,4 @@ export const checkAuthMethod = (params: CheckAuthParams) => {
|
|
|
42
39
|
|
|
43
40
|
// if apiKey exist
|
|
44
41
|
if (apiKey) return;
|
|
45
|
-
|
|
46
|
-
const { ACCESS_CODES } = getAppConfig();
|
|
47
|
-
|
|
48
|
-
// if accessCode doesn't exist
|
|
49
|
-
if (!ACCESS_CODES.length) return;
|
|
50
|
-
|
|
51
|
-
if (!accessCode || !ACCESS_CODES.includes(accessCode)) {
|
|
52
|
-
// Avoid logging user-provided credentials (access code) for security reasons
|
|
53
|
-
console.warn('Invalid access code provided');
|
|
54
|
-
throw AgentRuntimeError.createError(ChatErrorType.InvalidAccessCode);
|
|
55
|
-
}
|
|
56
42
|
};
|
|
@@ -62,7 +62,6 @@ describe('POST handler', () => {
|
|
|
62
62
|
|
|
63
63
|
// 设置 getJWTPayload 和 initModelRuntimeWithUserPayload 的模拟返回值
|
|
64
64
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
65
|
-
accessCode: 'test-access-code',
|
|
66
65
|
apiKey: 'test-api-key',
|
|
67
66
|
azureApiVersion: 'v1',
|
|
68
67
|
});
|
|
@@ -105,7 +104,6 @@ describe('POST handler', () => {
|
|
|
105
104
|
mockState.enableClerk = true;
|
|
106
105
|
|
|
107
106
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
108
|
-
accessCode: 'test-access-code',
|
|
109
107
|
apiKey: 'test-api-key',
|
|
110
108
|
azureApiVersion: 'v1',
|
|
111
109
|
});
|
|
@@ -133,7 +131,6 @@ describe('POST handler', () => {
|
|
|
133
131
|
await POST(request, { params: mockParams });
|
|
134
132
|
|
|
135
133
|
expect(checkAuthMethod).toBeCalledWith({
|
|
136
|
-
accessCode: 'test-access-code',
|
|
137
134
|
apiKey: 'test-api-key',
|
|
138
135
|
betterAuthAuthorized: false,
|
|
139
136
|
clerkAuth: {},
|
|
@@ -163,7 +160,6 @@ describe('POST handler', () => {
|
|
|
163
160
|
describe('chat', () => {
|
|
164
161
|
it('should correctly handle chat completion with valid payload', async () => {
|
|
165
162
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
166
|
-
accessCode: 'test-access-code',
|
|
167
163
|
apiKey: 'test-api-key',
|
|
168
164
|
azureApiVersion: 'v1',
|
|
169
165
|
userId: 'abc',
|
|
@@ -193,7 +189,6 @@ describe('POST handler', () => {
|
|
|
193
189
|
it('should return an error response when chat completion fails', async () => {
|
|
194
190
|
// 设置 getJWTPayload 和 initAgentRuntimeWithUserPayload 的模拟返回值
|
|
195
191
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
196
|
-
accessCode: 'test-access-code',
|
|
197
192
|
apiKey: 'test-api-key',
|
|
198
193
|
azureApiVersion: 'v1',
|
|
199
194
|
});
|
|
@@ -41,7 +41,6 @@ describe('GET handler', () => {
|
|
|
41
41
|
const mockParams = Promise.resolve({ provider: 'google' });
|
|
42
42
|
|
|
43
43
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
44
|
-
accessCode: 'test-access-code',
|
|
45
44
|
apiKey: 'test-api-key',
|
|
46
45
|
});
|
|
47
46
|
|
|
@@ -73,7 +72,6 @@ describe('GET handler', () => {
|
|
|
73
72
|
const mockParams = Promise.resolve({ provider: 'google' });
|
|
74
73
|
|
|
75
74
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
76
|
-
accessCode: 'test-access-code',
|
|
77
75
|
apiKey: 'test-api-key',
|
|
78
76
|
});
|
|
79
77
|
|
|
@@ -103,7 +101,6 @@ describe('GET handler', () => {
|
|
|
103
101
|
const mockParams = Promise.resolve({ provider: 'google' });
|
|
104
102
|
|
|
105
103
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
106
|
-
accessCode: 'test-access-code',
|
|
107
104
|
apiKey: 'test-api-key',
|
|
108
105
|
});
|
|
109
106
|
|
|
@@ -128,7 +125,6 @@ describe('GET handler', () => {
|
|
|
128
125
|
const mockParams = Promise.resolve({ provider: 'google' });
|
|
129
126
|
|
|
130
127
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
131
|
-
accessCode: 'test-access-code',
|
|
132
128
|
apiKey: 'test-api-key',
|
|
133
129
|
});
|
|
134
130
|
|
|
@@ -145,7 +141,6 @@ describe('GET handler', () => {
|
|
|
145
141
|
const mockParams = Promise.resolve({ provider: 'openai' });
|
|
146
142
|
|
|
147
143
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
148
|
-
accessCode: 'test-access-code',
|
|
149
144
|
apiKey: 'test-api-key',
|
|
150
145
|
});
|
|
151
146
|
|
|
@@ -165,7 +160,6 @@ describe('GET handler', () => {
|
|
|
165
160
|
const mockParams = Promise.resolve({ provider: 'openai' });
|
|
166
161
|
|
|
167
162
|
vi.mocked(getXorPayload).mockReturnValueOnce({
|
|
168
|
-
accessCode: 'test-access-code',
|
|
169
163
|
apiKey: 'test-api-key',
|
|
170
164
|
});
|
|
171
165
|
|
|
@@ -1,36 +1,15 @@
|
|
|
1
1
|
import { AgentRuntimeError } from '@lobechat/model-runtime';
|
|
2
|
-
import { ChatErrorType,
|
|
3
|
-
import { getXorPayload } from '@lobechat/utils/server';
|
|
2
|
+
import { ChatErrorType, TraceNameMap } from '@lobechat/types';
|
|
4
3
|
import type { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
|
|
5
4
|
import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway';
|
|
6
5
|
|
|
7
|
-
import { LOBE_CHAT_AUTH_HEADER
|
|
6
|
+
import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
|
8
7
|
import { LOBE_CHAT_TRACE_ID } from '@/const/trace';
|
|
9
8
|
import { getAppConfig } from '@/envs/app';
|
|
10
9
|
import { TraceClient } from '@/libs/traces';
|
|
11
10
|
import { parserPluginSettings } from '@/server/services/pluginGateway/settings';
|
|
12
|
-
import { createErrorResponse } from '@/utils/errorResponse';
|
|
13
11
|
import { getTracePayload } from '@/utils/trace';
|
|
14
12
|
|
|
15
|
-
const checkAuth = (accessCode: string | null, oauthAuthorized: boolean | null) => {
|
|
16
|
-
const { ACCESS_CODES, PLUGIN_SETTINGS } = getAppConfig();
|
|
17
|
-
|
|
18
|
-
// if there is no plugin settings, just skip the auth
|
|
19
|
-
if (!PLUGIN_SETTINGS) return { auth: true };
|
|
20
|
-
|
|
21
|
-
// If authorized by oauth
|
|
22
|
-
if (oauthAuthorized && enableNextAuth) return { auth: true };
|
|
23
|
-
|
|
24
|
-
// if accessCode doesn't exist
|
|
25
|
-
if (!ACCESS_CODES.length) return { auth: true };
|
|
26
|
-
|
|
27
|
-
if (!accessCode || !ACCESS_CODES.includes(accessCode)) {
|
|
28
|
-
return { auth: false, error: ChatErrorType.InvalidAccessCode };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return { auth: true };
|
|
32
|
-
};
|
|
33
|
-
|
|
34
13
|
const { PLUGINS_INDEX_URL: pluginsIndexUrl, PLUGIN_SETTINGS } = getAppConfig();
|
|
35
14
|
|
|
36
15
|
const defaultPluginSettings = parserPluginSettings(PLUGIN_SETTINGS);
|
|
@@ -42,15 +21,6 @@ export const POST = async (req: Request) => {
|
|
|
42
21
|
const authorization = req.headers.get(LOBE_CHAT_AUTH_HEADER);
|
|
43
22
|
if (!authorization) throw AgentRuntimeError.createError(ChatErrorType.Unauthorized);
|
|
44
23
|
|
|
45
|
-
const oauthAuthorized = !!req.headers.get(OAUTH_AUTHORIZED);
|
|
46
|
-
const payload = getXorPayload(authorization);
|
|
47
|
-
|
|
48
|
-
const result = checkAuth(payload.accessCode!, oauthAuthorized);
|
|
49
|
-
|
|
50
|
-
if (!result.auth) {
|
|
51
|
-
return createErrorResponse(result.error as ErrorType);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
24
|
// TODO: need to be replace by better telemetry system
|
|
55
25
|
// add trace
|
|
56
26
|
const tracePayload = getTracePayload(req);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Form, type FormGroupItemType, Icon, ImageSelect
|
|
3
|
+
import { Form, type FormGroupItemType, Icon, ImageSelect } from '@lobehub/ui';
|
|
4
4
|
import { Select, Skeleton } from '@lobehub/ui';
|
|
5
5
|
import { Segmented, Switch } from 'antd';
|
|
6
6
|
import isEqual from 'fast-deep-equal';
|
|
@@ -14,8 +14,6 @@ import { isDesktop } from '@/const/version';
|
|
|
14
14
|
import { localeOptions } from '@/locales/resources';
|
|
15
15
|
import { useGlobalStore } from '@/store/global';
|
|
16
16
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
17
|
-
import { useServerConfigStore } from '@/store/serverConfig';
|
|
18
|
-
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
19
17
|
import { useUserStore } from '@/store/user';
|
|
20
18
|
import { settingsSelectors } from '@/store/user/selectors';
|
|
21
19
|
import { type LocaleMode } from '@/types/locale';
|
|
@@ -23,7 +21,6 @@ import { type LocaleMode } from '@/types/locale';
|
|
|
23
21
|
const Common = memo(() => {
|
|
24
22
|
const { t } = useTranslation('setting');
|
|
25
23
|
|
|
26
|
-
const showAccessCodeConfig = useServerConfigStore(serverConfigSelectors.enabledAccessCode);
|
|
27
24
|
const general = useUserStore((s) => settingsSelectors.currentSettings(s).general, isEqual);
|
|
28
25
|
const themeMode = useGlobalStore(systemStatusSelectors.themeMode);
|
|
29
26
|
const language = useGlobalStore(systemStatusSelectors.language);
|
|
@@ -137,18 +134,6 @@ const Common = memo(() => {
|
|
|
137
134
|
name: 'contextMenuMode',
|
|
138
135
|
},
|
|
139
136
|
|
|
140
|
-
{
|
|
141
|
-
children: (
|
|
142
|
-
<InputPassword
|
|
143
|
-
autoComplete={'new-password'}
|
|
144
|
-
placeholder={t('settingSystem.accessCode.placeholder')}
|
|
145
|
-
/>
|
|
146
|
-
),
|
|
147
|
-
desc: t('settingSystem.accessCode.desc'),
|
|
148
|
-
hidden: !showAccessCodeConfig,
|
|
149
|
-
label: t('settingSystem.accessCode.title'),
|
|
150
|
-
name: 'password',
|
|
151
|
-
},
|
|
152
137
|
{
|
|
153
138
|
children: (
|
|
154
139
|
<Select
|
|
@@ -442,9 +442,6 @@ export default {
|
|
|
442
442
|
'settingOpening.openingQuestions.title': 'Opening Questions',
|
|
443
443
|
'settingOpening.title': 'Opening Settings',
|
|
444
444
|
'settingPlugin.title': 'Skill List',
|
|
445
|
-
'settingSystem.accessCode.desc': 'Encryption access is enabled by the administrator',
|
|
446
|
-
'settingSystem.accessCode.placeholder': 'Enter access password',
|
|
447
|
-
'settingSystem.accessCode.title': 'Access Password',
|
|
448
445
|
'settingSystem.oauth.info.desc': 'Logged in',
|
|
449
446
|
'settingSystem.oauth.info.title': 'Account Information',
|
|
450
447
|
'settingSystem.oauth.signin.action': 'Sign In',
|