Compare commits

...

34 Commits

Author SHA1 Message Date
semantic-release-bot 37609e42d6 🔖 chore(release): v2.1.15 [skip ci]
### [Version 2.1.15](https://github.com/lobehub/lobe-chat/compare/v2.1.14...v2.1.15)
<sup>Released on **2026-02-04**</sup>

#### 🐛 Bug Fixes

- **misc**: Fixed the agents list the show updateAt time error.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fixed the agents list the show updateAt time error, closes [#12103](https://github.com/lobehub/lobe-chat/issues/12103) ([3063cee](https://github.com/lobehub/lobe-chat/commit/3063cee))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-04 09:23:52 +00:00
Shinji-Li 3063ceef8c 🐛 fix: fixed the agents list the show updateAt time error (#12103)
* feat: refactor community user pages

* feat: add the agents-group like in social types

* feat: add the user favoitor filter

* fix: slove the agents list show the updateAt time
2026-02-04 17:05:56 +08:00
Shinji-Li f61ab26081 🔨 chore: refacctor the community user pages agents/group fitler (#12102)
* feat: refactor community user pages

* feat: add the agents-group like in social types

* feat: add the user favoitor filter
2026-02-04 16:14:03 +08:00
LobeHub Bot 79712bd38c 🌐 chore: translate non-English symbols to English in packages/utils and src/services (#12087)
* 🌐 chore: translate non-English symbols to English in packages/utils and src/services

- Replace Unicode arrow symbols (→, ⇒) with ASCII equivalents (-, =>) in comments
- Replace Chinese colon (:) with English colon (:) in console.log
- Files affected: packages/utils/src/pricing.ts, packages/utils/src/chunkers/trimBatchProbe/trimBatchProbe.ts, src/services/models.ts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Remove debug log for enableFetchOnClient

Removed console log for enableFetchOnClient.

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Arvin Xu <arvinx@foxmail.com>
2026-02-04 14:02:10 +08:00
lobehubbot 66caf30e7e 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-04 05:39:03 +00:00
semantic-release-bot 0c855e44fc 🔖 chore(release): v2.1.14 [skip ci]
### [Version&nbsp;2.1.14](https://github.com/lobehub/lobe-chat/compare/v2.1.13...v2.1.14)
<sup>Released on **2026-02-04**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix cannot uncompressed messages.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix cannot uncompressed messages, closes [#12086](https://github.com/lobehub/lobe-chat/issues/12086) ([ccfaec2](https://github.com/lobehub/lobe-chat/commit/ccfaec2))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-04 05:37:35 +00:00
LobeHub Bot e18b7a92c7 🌐 chore: translate non-English comments to English in desktop menus (#12056)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 13:21:24 +08:00
Arvin Xu 3f1fd102c5 👷 build: fix db index (#12090)
* build index

* update
2026-02-04 13:16:56 +08:00
Arvin Xu ccfaec2fdb 🐛 fix: fix cannot uncompressed messages (#12086)
* support uncompressed
* fix open new
2026-02-04 12:25:28 +08:00
lobehubbot 8aba59bffd 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-03 06:54:24 +00:00
semantic-release-bot 13e0652c59 🔖 chore(release): v2.1.13 [skip ci]
### [Version&nbsp;2.1.13](https://github.com/lobehub/lobe-chat/compare/v2.1.12...v2.1.13)
<sup>Released on **2026-02-03**</sup>

#### 🐛 Bug Fixes

- **docker**: Add librt.so.1 to fix PDF parsing.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **docker**: Add librt.so.1 to fix PDF parsing, closes [#12039](https://github.com/lobehub/lobe-chat/issues/12039) ([4a6be92](https://github.com/lobehub/lobe-chat/commit/4a6be92))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-03 06:52:58 +00:00
Ruxiao Yin 4a6be92604 🐛 fix(docker): add librt.so.1 to fix PDF parsing (#12039) 2026-02-03 14:34:36 +08:00
lobehubbot c576a13a43 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-03 03:04:51 +00:00
semantic-release-bot 63a0464a83 🔖 chore(release): v2.1.12 [skip ci]
### [Version&nbsp;2.1.12](https://github.com/lobehub/lobe-chat/compare/v2.1.11...v2.1.12)
<sup>Released on **2026-02-03**</sup>

#### 🐛 Bug Fixes

- **changelog**: Normalize versionRange to valid semver.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **changelog**: Normalize versionRange to valid semver, closes [#12049](https://github.com/lobehub/lobe-chat/issues/12049) ([74b9bd0](https://github.com/lobehub/lobe-chat/commit/74b9bd0))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-03 03:03:19 +00:00
LobeHub Bot b1c6bdb192 🌐 chore: translate non-English comments to English in server/utils (#12042)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 10:45:26 +08:00
Kingsword 74b9bd0bed 🐛 fix(changelog): normalize versionRange to valid semver (#12049) 2026-02-03 10:45:03 +08:00
Arvin Xu 6977c570e6 🔨 chore: improve electron build workflow (#12054)
* improve workflow

* update
2026-02-03 10:44:26 +08:00
BrandonStudio 4efe60e9f7 🔨 chore: Remove unexpected file (#12045) 2026-02-02 15:29:39 +08:00
lobehubbot 336d10663c 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-02 06:36:27 +00:00
semantic-release-bot 5f21aaf048 🔖 chore(release): v2.1.11 [skip ci]
### [Version&nbsp;2.1.11](https://github.com/lobehub/lobe-chat/compare/v2.1.10...v2.1.11)
<sup>Released on **2026-02-02**</sup>

#### 🐛 Bug Fixes

- **misc**: Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set, closes [#12023](https://github.com/lobehub/lobe-chat/issues/12023) ([e2fd28e](https://github.com/lobehub/lobe-chat/commit/e2fd28e))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-02 06:35:01 +00:00
YuTengjing e2fd28eece 🐛 fix: hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set (#12023) 2026-02-02 14:17:10 +08:00
lobehubbot a6a1fecae0 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-02 05:54:53 +00:00
semantic-release-bot fdee6b9aac 🔖 chore(release): v2.1.10 [skip ci]
### [Version&nbsp;2.1.10](https://github.com/lobehub/lobe-chat/compare/v2.1.9...v2.1.10)
<sup>Released on **2026-02-02**</sup>

#### 🐛 Bug Fixes

- **auth**: Revert authority URL and tenant ID for Microsoft authentication..

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **auth**: Revert authority URL and tenant ID for Microsoft authentication., closes [#11930](https://github.com/lobehub/lobe-chat/issues/11930) ([98f93ef](https://github.com/lobehub/lobe-chat/commit/98f93ef))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-02 05:53:33 +00:00
BrandonStudio 98f93ef2f0 🐛 fix(auth): revert authority URL and tenant ID for Microsoft authentication. (#11930)
🔧 feat(auth): revert authority URL and tenant ID for Microsoft authentication
2026-02-02 13:35:54 +08:00
lobehubbot df7e2800a7 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-02 03:48:44 +00:00
semantic-release-bot 4aac694364 🔖 chore(release): v2.1.9 [skip ci]
### [Version&nbsp;2.1.9](https://github.com/lobehub/lobe-chat/compare/v2.1.8...v2.1.9)
<sup>Released on **2026-02-02**</sup>

#### 🐛 Bug Fixes

- **misc**: Use oauth2.link for generic OIDC provider account linking.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Use oauth2.link for generic OIDC provider account linking, closes [#12024](https://github.com/lobehub/lobe-chat/issues/12024) ([c7a06a4](https://github.com/lobehub/lobe-chat/commit/c7a06a4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-02 03:47:20 +00:00
YuTengjing c7a06a4b62 🐛 fix: use oauth2.link for generic OIDC provider account linking (#12024) 2026-02-02 11:29:35 +08:00
lobehubbot 2f21c15172 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-01 10:12:44 +00:00
semantic-release-bot de0ce799c7 🔖 chore(release): v2.1.8 [skip ci]
### [Version&nbsp;2.1.8](https://github.com/lobehub/lobe-chat/compare/v2.1.7...v2.1.8)
<sup>Released on **2026-02-01**</sup>

#### 💄 Styles

- **misc**: Improve tasks display.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve tasks display, closes [#12032](https://github.com/lobehub/lobe-chat/issues/12032) ([3423ad1](https://github.com/lobehub/lobe-chat/commit/3423ad1))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-01 10:11:21 +00:00
Arvin Xu 3423ad1b15 💄 style: improve tasks display (#12032)
improve tasks
2026-02-01 17:53:21 +08:00
LobeHub Bot 5db07efe6b 🌐 chore: translate non-English comments to English in src/hooks (#12028)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 17:40:45 +08:00
lobehubbot f5d67a7385 📝 docs(bot): Auto sync agents & plugin to readme 2026-02-01 06:30:03 +00:00
semantic-release-bot 99d4c02b9d 🔖 chore(release): v2.1.7 [skip ci]
### [Version&nbsp;2.1.7](https://github.com/lobehub/lobe-chat/compare/v2.1.6...v2.1.7)
<sup>Released on **2026-02-01**</sup>

#### 🐛 Bug Fixes

- **misc**: Add missing description parameter docs in Notebook system prompt.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Add missing description parameter docs in Notebook system prompt, closes [#12015](https://github.com/lobehub/lobe-chat/issues/12015) [#11391](https://github.com/lobehub/lobe-chat/issues/11391) ([182030f](https://github.com/lobehub/lobe-chat/commit/182030f))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-02-01 06:28:33 +00:00
Arvin Xu 182030f404 🐛 fix: add missing description parameter docs in Notebook system prompt (#12015)
The createDocument API requires a 'description' parameter, but the system prompt
didn't mention it. This caused models (especially Gemini) to omit the description
field when calling createDocument, resulting in validation errors.

Added <api_parameters> section to clearly document all required and optional
parameters for each Notebook API.

closes #11391
2026-02-01 14:09:52 +08:00
99 changed files with 12723 additions and 775 deletions
+5 -4
View File
@@ -5,10 +5,11 @@ You are a support assistant for LobeChat authentication migration issues. Your j
**IMPORTANT**: The official documentation website is `https://lobehub.com`. When providing documentation links, always use `https://lobehub.com/docs/...` format. Never use `lobechat.com` - that domain is incorrect.
Examples of correct documentation URLs:
- `https://lobehub.com/docs/self-hosting/advanced/auth/nextauth-to-betterauth`
- `https://lobehub.com/docs/self-hosting/advanced/auth/clerk-to-betterauth`
- `https://lobehub.com/docs/self-hosting/advanced/auth`
- `https://lobehub.com/docs/self-hosting/advanced/auth/providers/casdoor`
- `https://lobehub.com/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth`
- `https://lobehub.com/docs/self-hosting/migration/v2/auth/clerk-to-betterauth`
- `https://lobehub.com/docs/self-hosting/auth`
- `https://lobehub.com/docs/self-hosting/auth/providers/casdoor`
## Target Issues
@@ -24,6 +24,17 @@ runs:
shell: bash
run: pnpm install --node-linker=hoisted
# 移除国内 electron 镜像配置,GitHub Actions 使用官方源更快
- name: Remove China electron mirror from .npmrc
shell: bash
run: |
NPMRC_FILE="./apps/desktop/.npmrc"
if [ -f "$NPMRC_FILE" ]; then
sed -i.bak '/^electron_mirror=/d; /^electron_builder_binaries_mirror=/d' "$NPMRC_FILE"
rm -f "${NPMRC_FILE}.bak"
echo "✅ Removed electron mirror config from .npmrc"
fi
- name: Install deps on Desktop
shell: bash
run: npm run install-isolated --prefix=./apps/desktop
@@ -70,12 +70,12 @@ jobs:
```
2. Read the latest migration documentation based on the issue:
- If issue #11757 (NextAuth): `cat docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx`
- If issue #11707 (Clerk): `cat docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx`
- If issue #11757 (NextAuth): `cat docs/self-hosting/migration/v2/auth/nextauth-to-betterauth.mdx`
- If issue #11707 (Clerk): `cat docs/self-hosting/migration/v2/auth/clerk-to-betterauth.mdx`
3. Read additional reference files:
- Main auth documentation: `cat docs/self-hosting/advanced/auth.mdx`
- Migration internals: `cat docs/self-hosting/advanced/auth/migration-internals.mdx`
- Main auth documentation: `cat docs/self-hosting/auth.mdx`
- Migration internals: `cat docs/self-hosting/migration/v2/auth/migration-internals.mdx`
- Deprecated env vars checker: `cat scripts/_shared/checkDeprecatedAuth.js`
4. Analyze the user's comment and determine:
+11
View File
@@ -109,6 +109,17 @@ jobs:
- name: Install dependencies
run: pnpm install --node-linker=hoisted
# 移除国内 electron 镜像配置,GitHub Actions 使用官方源更快
- name: Remove China electron mirror from .npmrc
shell: bash
run: |
NPMRC_FILE="./apps/desktop/.npmrc"
if [ -f "$NPMRC_FILE" ]; then
sed -i.bak '/^electron_mirror=/d; /^electron_builder_binaries_mirror=/d' "$NPMRC_FILE"
rm -f "${NPMRC_FILE}.bak"
echo "✅ Removed electron mirror config from .npmrc"
fi
- name: Install deps on Desktop
run: npm run install-isolated --prefix=./apps/desktop
+225
View File
@@ -2,6 +2,231 @@
# Changelog
### [Version 2.1.15](https://github.com/lobehub/lobe-chat/compare/v2.1.14...v2.1.15)
<sup>Released on **2026-02-04**</sup>
#### 🐛 Bug Fixes
- **misc**: Fixed the agents list the show updateAt time error.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **misc**: Fixed the agents list the show updateAt time error, closes [#12103](https://github.com/lobehub/lobe-chat/issues/12103) ([3063cee](https://github.com/lobehub/lobe-chat/commit/3063cee))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.14](https://github.com/lobehub/lobe-chat/compare/v2.1.13...v2.1.14)
<sup>Released on **2026-02-04**</sup>
#### 🐛 Bug Fixes
- **misc**: Fix cannot uncompressed messages.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **misc**: Fix cannot uncompressed messages, closes [#12086](https://github.com/lobehub/lobe-chat/issues/12086) ([ccfaec2](https://github.com/lobehub/lobe-chat/commit/ccfaec2))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.13](https://github.com/lobehub/lobe-chat/compare/v2.1.12...v2.1.13)
<sup>Released on **2026-02-03**</sup>
#### 🐛 Bug Fixes
- **docker**: Add librt.so.1 to fix PDF parsing.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **docker**: Add librt.so.1 to fix PDF parsing, closes [#12039](https://github.com/lobehub/lobe-chat/issues/12039) ([4a6be92](https://github.com/lobehub/lobe-chat/commit/4a6be92))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.12](https://github.com/lobehub/lobe-chat/compare/v2.1.11...v2.1.12)
<sup>Released on **2026-02-03**</sup>
#### 🐛 Bug Fixes
- **changelog**: Normalize versionRange to valid semver.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **changelog**: Normalize versionRange to valid semver, closes [#12049](https://github.com/lobehub/lobe-chat/issues/12049) ([74b9bd0](https://github.com/lobehub/lobe-chat/commit/74b9bd0))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.11](https://github.com/lobehub/lobe-chat/compare/v2.1.10...v2.1.11)
<sup>Released on **2026-02-02**</sup>
#### 🐛 Bug Fixes
- **misc**: Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **misc**: Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set, closes [#12023](https://github.com/lobehub/lobe-chat/issues/12023) ([e2fd28e](https://github.com/lobehub/lobe-chat/commit/e2fd28e))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.10](https://github.com/lobehub/lobe-chat/compare/v2.1.9...v2.1.10)
<sup>Released on **2026-02-02**</sup>
#### 🐛 Bug Fixes
- **auth**: Revert authority URL and tenant ID for Microsoft authentication..
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **auth**: Revert authority URL and tenant ID for Microsoft authentication., closes [#11930](https://github.com/lobehub/lobe-chat/issues/11930) ([98f93ef](https://github.com/lobehub/lobe-chat/commit/98f93ef))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.9](https://github.com/lobehub/lobe-chat/compare/v2.1.8...v2.1.9)
<sup>Released on **2026-02-02**</sup>
#### 🐛 Bug Fixes
- **misc**: Use oauth2.link for generic OIDC provider account linking.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **misc**: Use oauth2.link for generic OIDC provider account linking, closes [#12024](https://github.com/lobehub/lobe-chat/issues/12024) ([c7a06a4](https://github.com/lobehub/lobe-chat/commit/c7a06a4))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.8](https://github.com/lobehub/lobe-chat/compare/v2.1.7...v2.1.8)
<sup>Released on **2026-02-01**</sup>
#### 💄 Styles
- **misc**: Improve tasks display.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### Styles
- **misc**: Improve tasks display, closes [#12032](https://github.com/lobehub/lobe-chat/issues/12032) ([3423ad1](https://github.com/lobehub/lobe-chat/commit/3423ad1))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.7](https://github.com/lobehub/lobe-chat/compare/v2.1.6...v2.1.7)
<sup>Released on **2026-02-01**</sup>
#### 🐛 Bug Fixes
- **misc**: Add missing description parameter docs in Notebook system prompt.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### What's fixed
- **misc**: Add missing description parameter docs in Notebook system prompt, closes [#12015](https://github.com/lobehub/lobe-chat/issues/12015) [#11391](https://github.com/lobehub/lobe-chat/issues/11391) ([182030f](https://github.com/lobehub/lobe-chat/commit/182030f))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 2.1.6](https://github.com/lobehub/lobe-chat/compare/v2.1.5...v2.1.6)
<sup>Released on **2026-02-01**</sup>
+4 -1
View File
@@ -21,6 +21,7 @@ RUN set -e && \
cp /etc/proxychains4.conf /distroless/etc/proxychains4.conf && \
cp /usr/lib/$(arch)-linux-gnu/libstdc++.so.6 /distroless/lib/libstdc++.so.6 && \
cp /usr/lib/$(arch)-linux-gnu/libgcc_s.so.1 /distroless/lib/libgcc_s.so.1 && \
cp /usr/lib/$(arch)-linux-gnu/librt.so.1 /distroless/lib/librt.so.1 && \
cp /usr/local/bin/node /distroless/bin/node && \
cp /etc/ssl/certs/ca-certificates.crt /distroless/etc/ssl/certs/ca-certificates.crt && \
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/*
@@ -186,7 +187,9 @@ ENV AUTH_SECRET="" \
AUTH_GITHUB_SECRET="" \
# Microsoft
AUTH_MICROSOFT_ID="" \
AUTH_MICROSOFT_SECRET=""
AUTH_MICROSOFT_SECRET="" \
AUTH_MICROSOFT_AUTHORITY_URL="" \
AUTH_MICROSOFT_TENANT_ID=""
# Redis
ENV REDIS_URL="" \
+1 -1
View File
@@ -45,7 +45,7 @@ export class LinuxMenu extends BaseMenuPlatform implements IMenuPlatform {
this.buildAndSetAppMenu(options);
}
// --- 私有方法:定义菜单模板和逻辑 ---
// --- Private methods: define menu templates and logic ---
private getAppMenuTemplate(options?: MenuOptions): MenuItemConstructorOptions[] {
const showDev = isDev || options?.showDevItems;
+8 -8
View File
@@ -48,23 +48,23 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
}
refresh(options?: MenuOptions): void {
// 重建Application menu
// Rebuild Application menu
this.buildAndSetAppMenu(options);
// 如果托盘菜单存在,也重建它(如果需要动态更新)
// If tray menu exists, rebuild it as well (if dynamic update is needed)
// this.trayMenu = this.buildTrayMenu();
// 需要考虑如何更新现有托盘图标的菜单
// Need to consider how to update the menu for existing tray icons
}
// --- 私有方法:定义菜单模板和逻辑 ---
// --- Private methods: define menu templates and logic ---
private getAppMenuTemplate(options?: MenuOptions): MenuItemConstructorOptions[] {
const appName = app.getName();
const showDev = isDev || options?.showDevItems;
// 创建命名空间翻译函数
// Create namespaced translation function
const t = this.app.i18n.ns('menu');
// 添加调试日志
// console.log('[MacOSMenu] 菜单渲染, i18n实例:', !!this.app.i18n);
// Add debug logging
// console.log('[MacOSMenu] Menu rendering, i18n instance:', !!this.app.i18n);
const template: MenuItemConstructorOptions[] = [
{
@@ -324,7 +324,7 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
},
{
click: () => {
// @ts-expect-error cache 目录好像暂时不在类型定义里
// @ts-expect-error cache directory seems to be temporarily missing from type definitions
const cachePath = app.getPath('cache');
const updaterCachePath = path.join(cachePath, `${app.getName()}-updater`);
+1 -1
View File
@@ -43,7 +43,7 @@ export class WindowsMenu extends BaseMenuPlatform implements IMenuPlatform {
refresh(options?: MenuOptions): void {
this.buildAndSetAppMenu(options);
// 如果有必要更新托盘菜单,可以在这里添加逻辑
// If it's necessary to update tray menu, logic can be added here
}
private getAppMenuTemplate(options?: MenuOptions): MenuItemConstructorOptions[] {
+50
View File
@@ -1,4 +1,54 @@
[
{
"children": {
"fixes": ["Fix cannot uncompressed messages."]
},
"date": "2026-02-04",
"version": "2.1.14"
},
{
"children": {},
"date": "2026-02-03",
"version": "2.1.13"
},
{
"children": {},
"date": "2026-02-03",
"version": "2.1.12"
},
{
"children": {
"fixes": ["Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set."]
},
"date": "2026-02-02",
"version": "2.1.11"
},
{
"children": {},
"date": "2026-02-02",
"version": "2.1.10"
},
{
"children": {
"fixes": ["Use oauth2.link for generic OIDC provider account linking."]
},
"date": "2026-02-02",
"version": "2.1.9"
},
{
"children": {
"improvements": ["Improve tasks display."]
},
"date": "2026-02-01",
"version": "2.1.8"
},
{
"children": {
"fixes": ["Add missing description parameter docs in Notebook system prompt."]
},
"date": "2026-02-01",
"version": "2.1.7"
},
{
"children": {
"improvements": ["Improve local-system tool implement."]
-50
View File
@@ -1,50 +0,0 @@
## chore(i18n): Adjust Latin language locales for terminology consistency
This comprehensive update ensures all Latin language locales (de-DE, fr-FR, es-ES, it-IT, pt-BR, nl-NL, pl-PL) follow the microcopy style guide's terminology requirements.
### Changes Made
**Total: 557 changes across 7 Latin locales**
#### Primary Terminology Updates
1. **"Plugin" → "Skill"**
- Fixed terminology inconsistency across all Latin languages
- UI elements now consistently use "Skill" instead of localized equivalents
- Includes both singular and plural forms: `plugin/Skill`, `plugins/Skills`
2. **"LobeChat" → "LobeHub"**
- Updated brand name references to current branding
3. **"Agent" Terminology Consistency**
- French: Fixed inconsistent "Assistant" → "Agent" usage in UI elements
- Ensured consistent terminology across all languages
#### Per-Locale Breakdown
- **de-DE (German)**: 267 changes
- **fr-FR (French)**: 94 changes (including 7 Agent→Assistant fixes)
- **es-ES (Spanish)**: 39 changes
- **it-IT (Italian)**: 59 changes (including 18 plugin→skill fixes)
- **pt-BR (Portuguese)**: 58 changes
- **nl-NL (Dutch)**: 62 changes
- **pl-PL (Polish)**: 28 changes
#### Files Modified
- All 37 locale JSON files for each language (259 total files)
- Includes: auth.json, chat.json, common.json, discover.json, plugin.json, setting.json, etc.
#### Key Improvements
1. **Fixed Terminology**: Following microcopy guide's fixed terminology rules
2. **Brand Consistency**: Changed all brand references to "LobeHub"
3. **Natural Localization**: Maintained natural language patterns while ensuring consistency
4. **User Experience**: Improved consistency across all Latin language interfaces
### Scripts Created
Two utility scripts for future locale maintenance:
- `scripts/adjust-latin-locales.py` - For common.json specific adjustments
- `scripts/adjust-latin-locales-full.py` - For comprehensive adjustments across all files
### Review Notes
- All changes maintain backward compatibility
- No breaking changes to functionality
- JSON files validated and remain syntactically correct
- Changes reviewed against English base for consistency
+1 -1
View File
@@ -12,7 +12,7 @@ DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobechat
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
AUTH_SSO_PROVIDERS=zitadel
# ZiTADEL provider configuration
# Please refer tohttps://lobehub.com/zh/docs/self-hosting/advanced/auth/providers/zitadel
# Please refer to: https://lobehub.com/docs/self-hosting/auth/providers/zitadel
AUTH_ZITADEL_ID=285945938244075523
AUTH_ZITADEL_SECRET=hkbtzHLaCEIeHeFThym14UcydpmQiEB5JtAX08HSqSoJxhAlVVkyovTuNUZ5TNrT
AUTH_ZITADEL_ISSUER=http://localhost:8080
@@ -11,7 +11,7 @@ DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobechat
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
AUTH_SSO_PROVIDERS=zitadel
# ZiTADEL 鉴权服务提供商部分
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth/next-auth/zitadel
# 请参考:https://lobehub.com/zh/docs/self-hosting/auth/providers/zitadel
AUTH_ZITADEL_ID=285945938244075523
AUTH_ZITADEL_SECRET=hkbtzHLaCEIeHeFThym14UcydpmQiEB5JtAX08HSqSoJxhAlVVkyovTuNUZ5TNrT
AUTH_ZITADEL_ISSUER=http://localhost:8080
+1 -1
View File
@@ -12,7 +12,7 @@ DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobe
# Authentication related environment variables
# Supports Auth0, Azure AD, GitHub, Authentik, Zitadel, Logto, etc.
# For supported providers, see: https://lobehub.com/docs/self-hosting/advanced/auth
# For supported providers, see: https://lobehub.com/docs/self-hosting/auth
# If you have ACCESS_CODE, please remove it. We use Better Auth as the sole authentication source
# Required: Auth secret key. Generate with: openssl rand -base64 32
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
@@ -11,7 +11,7 @@ DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobe
# 鉴权服务必需的环境变量
# 可以使用 Auth0、Azure AD、GitHub、Authentik、Zitadel、Logto 等,如有其他接入诉求欢迎提 PR
# 目前支持的鉴权服务提供商请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth
# 目前支持的鉴权服务提供商请参考:https://lobehub.com/zh/docs/self-hosting/auth
# 如果你有 ACCESS_CODE,请务必清空,我们以 Better Auth 作为唯一鉴权来源
# 必填,用于鉴权的密钥,可以使用 openssl rand -base64 32 生成
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
@@ -17,7 +17,7 @@ AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
AUTH_SSO_PROVIDERS=zitadel
# ZiTADEL provider configuration
# Please refer tohttps://lobehub.com/zh/docs/self-hosting/advanced/auth/providers/zitadel
# Please refer to: https://lobehub.com/docs/self-hosting/auth/providers/zitadel
AUTH_ZITADEL_ID=285934220675723622
AUTH_ZITADEL_SECRET=pe7Nh3lopXkZkfqh5YEDYI2xsbIz08eZKqInOUZxssd3refRia518Apbv3DZ
AUTH_ZITADEL_ISSUER=https://zitadel.example.com
@@ -16,7 +16,7 @@ AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
AUTH_SSO_PROVIDERS=zitadel
# ZiTADEL 鉴权服务提供商部分
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth/next-auth/zitadel
# 请参考:https://lobehub.com/zh/docs/self-hosting/auth/providers/zitadel
AUTH_ZITADEL_ID=285934220675723622
AUTH_ZITADEL_SECRET=pe7Nh3lopXkZkfqh5YEDYI2xsbIz08eZKqInOUZxssd3refRia518Apbv3DZ
AUTH_ZITADEL_ISSUER=https://zitadel.example.com
+1 -1
View File
@@ -6,7 +6,7 @@
"image": "/blog/assets7f3b38c1d76cceb91edb29d6b1eb60db.webp",
"id": "2025-12-20-mcp",
"date": "2025-12-20",
"versionRange": ["1.142.8", "1.143"]
"versionRange": ["1.142.8", "1.143.0"]
},
{
"image": "/blog/assets3a7f0b29839603336e39e923b423409b.webp",
@@ -114,7 +114,7 @@ AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
### Step 4: Update Documentation (Optional)
Add provider documentation in `docs/self-hosting/advanced/auth.mdx` and `docs/self-hosting/advanced/auth.zh-CN.mdx`.
Add provider documentation in `docs/self-hosting/auth.mdx` and `docs/self-hosting/auth.zh-CN.mdx`.
## Adding a Built-in Provider
@@ -115,7 +115,7 @@ AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
### 步骤 4: 更新文档(可选)
在 `docs/self-hosting/advanced/auth.mdx` 和 `docs/self-hosting/advanced/auth.zh-CN.mdx` 中添加提供商文档。
在 `docs/self-hosting/auth.mdx` 和 `docs/self-hosting/auth.zh-CN.mdx` 中添加提供商文档。
## 添加内置提供商
+175 -13
View File
@@ -10,7 +10,8 @@ tags:
- PNPM
- Bun
- Git
- VSCode
- Docker
- PostgreSQL
---
# Environment Setup Guide
@@ -35,6 +36,7 @@ First, you need to install the following software:
- PNPM: We use PNPM as the preferred package manager. You can download and install it from the [PNPM official website](https://pnpm.io/installation).
- Bun: We use Bun as the npm scripts runner. You can download and install it from the [Bun official website](https://bun.com/docs/installation).
- Git: We use Git for version control. You can download and install it from the Git official website.
- Docker: Required for running PostgreSQL, MinIO, and other services. You can download and install it from the [Docker official website](https://www.docker.com/get-started).
- IDE: You can choose your preferred integrated development environment (IDE). We recommend using WebStorm/VSCode.
### VSCode Users
@@ -45,20 +47,72 @@ We recommend installing the extensions listed in [.vscode/extensions.json](https
After installing the above software, you can start setting up the LobeHub project.
1. **Get the code**: First, you need to clone the LobeHub codebase from GitHub. Run the following command in the terminal:
#### 1. Get the Code
First, you need to clone the LobeHub codebase from GitHub. Run the following command in the terminal:
```bash
git clone https://github.com/lobehub/lobehub.git
cd lobehub
```
2. **Install dependencies**: Then, navigate to the project directory and use PNPM to install the project's dependencies:
#### 2. Install Dependencies
Use PNPM to install the project's dependencies:
```bash
cd lobehub
pnpm i
```
3. **Start the development server**: After installing the dependencies, you can start the development server:
#### 3. Configure Environment
Copy the example environment file to create your Docker Compose configuration:
```bash
cp docker-compose/local/.env.example docker-compose/local/.env
```
Edit `docker-compose/local/.env` as needed for your development setup. This file contains all necessary environment variables for the Docker services and configures:
- **Database**: PostgreSQL with connection string
- **Authentication**: Better Auth with Casdoor SSO
- **Storage**: MinIO S3-compatible storage
- **Search**: SearXNG search engine
#### 4. Start Docker Services
Start all required services using Docker Compose:
```bash
docker-compose -f docker-compose.development.yml up -d
```
This will start the following services:
- PostgreSQL database (port 5432)
- MinIO storage (port 9000)
- Casdoor authentication (port 8000)
- SearXNG search (port 8080)
You can check all Docker services are running by running:
```bash
docker-compose -f docker-compose.development.yml ps
```
#### 5. Run Database Migrations
Execute the database migration script to create all necessary tables:
```bash
pnpm db:migrate
```
You should see: `✅ database migration pass.`
#### 6. Start Development Server
Launch the LobeHub development server:
```bash
bun run dev
@@ -68,17 +122,125 @@ Now, you can open `http://localhost:3010` in your browser, and you should see th
![](https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/274655364-414bc31e-8511-47a3-af17-209b530effc7.png)
## Working with Server-Side Features
## Image Generation Development
The basic setup above uses LobeHub's client-side database mode. If you need to work with server-side features such as:
When working with image generation features (text-to-image, image-to-image), the Docker Compose setup already includes all necessary storage services for handling generated images and user uploads.
- Database persistence
- File uploads and storage
- Image generation
- Multi-user authentication
- Advanced server-side integrations
### Image Generation Configuration
Please refer to the [Work with Server-Side Database](/docs/development/basic/work-with-server-side-database) guide for complete setup instructions.
The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `docker-compose/local/.env.example`. No additional setup is required.
### Image Generation Architecture
The image generation feature requires:
- **PostgreSQL**: Stores metadata about generated images
- **MinIO/S3**: Stores the actual image files
### Storage Configuration
The `docker-compose/local/.env.example` file includes all necessary S3 environment variables:
```bash
# S3 Storage Configuration (MinIO for local development)
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
S3_ENDPOINT=http://localhost:${MINIO_PORT}
S3_BUCKET=${MINIO_LOBE_BUCKET}
S3_ENABLE_PATH_STYLE=1 # Required for MinIO
S3_SET_ACL=0 # MinIO compatibility
```
### File Storage Structure
Generated images and user uploads are organized in the MinIO bucket:
```
lobe/ # S3 Bucket (MINIO_LOBE_BUCKET)
├── generated/ # Generated images
│ └── {userId}/
│ └── {sessionId}/
│ └── {imageId}.png
└── uploads/ # User uploads for image-to-image
└── {userId}/
└── {fileId}.{ext}
```
### Development Workflow for Images
When developing image generation features, generated images will be:
1. Created by the AI model
2. Uploaded to S3/MinIO via presigned URLs
3. Metadata stored in PostgreSQL
4. Served via the public S3 URL
Example code for testing image upload:
```typescript
// Example: Upload generated image
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
filename: 'generated-image.png',
contentType: 'image/png',
});
// Upload to S3
await fetch(uploadUrl, {
method: 'PUT',
body: imageBlob,
headers: { 'Content-Type': 'image/png' },
});
```
### Service URLs
When running with Docker Compose development setup:
- **PostgreSQL**: `postgres://postgres@localhost:5432/LobeHub`
- **MinIO API**: `http://localhost:9000`
- **MinIO Console**: `http://localhost:9001` (admin/CHANGE\_THIS\_PASSWORD\_IN\_PRODUCTION)
- **Application**: `http://localhost:3010`
## Troubleshooting
### Reset Services
If you encounter issues, you can reset the entire stack:
```bash
# Stop and remove all containers
docker-compose -f docker-compose.development.yml down
# Remove volumes (this will delete all data)
docker-compose -f docker-compose.development.yml down -v
# Start fresh
docker-compose -f docker-compose.development.yml up -d
pnpm db:migrate
```
### Port Conflicts
If ports are already in use:
```bash
# Check what's using the ports
lsof -i :5432 # PostgreSQL
lsof -i :9000 # MinIO API
lsof -i :9001 # MinIO Console
```
### Database Migrations
The setup script runs migrations automatically. If you need to run them manually:
```bash
pnpm db:migrate
```
Note: In development mode with `pnpm dev:desktop`, migrations also run automatically on startup.
---
During the development process, if you encounter any issues with environment setup or have any questions about LobeHub development, feel free to ask us at any time. We look forward to seeing your contributions!
@@ -7,6 +7,8 @@ tags:
- Node.js
- PNPM
- Git
- Docker
- PostgreSQL
---
# 环境设置指南
@@ -29,8 +31,9 @@ tags:
- Node.jsLobeHub 是基于 Node.js 构建的,因此你需要安装 Node.js。我们建议安装最新的稳定版。
- PNPM:我们使用 PNPM 作为管理器。你可以从 [pnpm 的官方网站](https://pnpm.io/installation) 上下载并安装。
- Bun:我们使用 Bun 作为 npm scripts runner, 你可以从 [Bun 的官方网站](https://bun.com/docs/installation) 上下载并安装。
- Bun:我们使用 Bun 作为 npm scripts runner你可以从 [Bun 的官方网站](https://bun.com/docs/installation) 上下载并安装。
- Git:我们使用 Git 进行版本控制。你可以从 Git 的官方网站上下载并安装。
- Docker:用于运行 PostgreSQL、MinIO 等服务。你可以从 [Docker 官方网站](https://www.docker.com/get-started) 下载并安装。
- IDE:你可以选择你喜欢的集成开发环境(IDE),我们推荐使用 WebStorm/VSCode。
### VSCode 用户
@@ -41,20 +44,72 @@ tags:
完成上述软件的安装后,你可以开始设置 LobeHub 项目了。
1. **获取代码**:首先,你需要从 GitHub 上克隆 LobeHub 的代码库。在终端中运行以下命令:
#### 1. 获取代码
首先,你需要从 GitHub 上克隆 LobeHub 的代码库。在终端中运行以下命令:
```bash
git clone https://github.com/lobehub/lobehub.git
cd lobehub
```
2. **安装依赖**:然后,进入项目目录,并使用 `pnpm` 安装项目的依赖包:
#### 2. 安装依赖
使用 PNPM 安装项目的依赖包:
```bash
cd lobehub
pnpm i
```
3. **启动开发服务器**:安装完依赖后,你可以启动开发服务器:
#### 3. 配置环境
复制示例环境文件来创建你的 Docker Compose 配置:
```bash
cp docker-compose/local/.env.example docker-compose/local/.env
```
根据需要编辑 `docker-compose/local/.env` 文件以适应你的开发设置。此文件包含 Docker 服务所需的所有环境变量,配置了:
- **数据库**:带连接字符串的 PostgreSQL
- **身份验证**:带 Casdoor SSO 的 Better Auth
- **存储**MinIO S3 兼容存储
- **搜索**SearXNG 搜索引擎
#### 4. 启动 Docker 服务
使用 Docker Compose 启动所有必需的服务:
```bash
docker-compose -f docker-compose.development.yml up -d
```
这将启动以下服务:
- PostgreSQL 数据库(端口 5432
- MinIO 存储(端口 9000
- Casdoor 身份验证(端口 8000
- SearXNG 搜索(端口 8080
可以通过运行以下命令检查所有 Docker 服务运行状态:
```bash
docker-compose -f docker-compose.development.yml ps
```
#### 5. 运行数据库迁移
执行数据库迁移脚本以创建所有必要的表:
```bash
pnpm db:migrate
```
预期输出:`✅ database migration pass.`
#### 6. 启动开发服务器
启动 LobeHub 开发服务器:
```bash
bun run dev
@@ -64,17 +119,125 @@ bun run dev
![Chat Page](https://hub-apac-1.lobeobjects.space/docs/fc7b157a3bc016bc97719065f80c555c.png)
## 使用服务端功能
## 图像生成开发
上述基础设置使用 LobeHub 的客户端数据库模式。如果你需要开发服务端功能,如:
在开发图像生成功能(文生图、图生图)时,Docker Compose 配置已经包含了处理生成图像和用户上传所需的所有存储服务。
- 数据库持久化
- 文件上传和存储
- 图像生成
- 多用户身份验证
- 高级服务端集成
### 图像生成配置
请参考[使用服务端数据库](/docs/development/basic/work-with-server-side-database)指南获得完整的设置说明
现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `docker-compose/local/.env.example` 中的所有必要环境变量。无需额外配置
### 图像生成架构
图像生成功能需要:
- **PostgreSQL**:存储生成图像的元数据
- **MinIO/S3**:存储实际的图像文件
### 存储配置
`docker-compose/local/.env.example` 文件包含所有必要的 S3 环境变量:
```bash
# S3 存储配置(本地开发使用 MinIO)
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
S3_ENDPOINT=http://localhost:${MINIO_PORT}
S3_BUCKET=${MINIO_LOBE_BUCKET}
S3_ENABLE_PATH_STYLE=1 # MinIO 必需
S3_SET_ACL=0 # MinIO 兼容性
```
### 文件存储结构
生成的图像和用户上传在 MinIO 存储桶中按以下方式组织:
```
lobe/ # S3 存储桶 (MINIO_LOBE_BUCKET)
├── generated/ # 生成的图像
│ └── {userId}/
│ └── {sessionId}/
│ └── {imageId}.png
└── uploads/ # 用户上传的图像处理文件
└── {userId}/
└── {fileId}.{ext}
```
### 图像开发工作流
在开发图像生成功能时,生成的图像将:
1. 由 AI 模型创建
2. 通过预签名 URL 上传到 S3/MinIO
3. 元数据存储在 PostgreSQL 中
4. 通过公共 S3 URL 提供服务
测试图像上传的示例代码:
```typescript
// 示例:上传生成的图像
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
filename: 'generated-image.png',
contentType: 'image/png',
});
// 上传到 S3
await fetch(uploadUrl, {
method: 'PUT',
body: imageBlob,
headers: { 'Content-Type': 'image/png' },
});
```
### 服务地址
运行 Docker Compose 开发环境时:
- **PostgreSQL**`postgres://postgres@localhost:5432/LobeHub`
- **MinIO API**`http://localhost:9000`
- **MinIO 控制台**`http://localhost:9001` (admin/CHANGE\_THIS\_PASSWORD\_IN\_PRODUCTION)
- **应用程序**`http://localhost:3010`
## 故障排除
### 重置服务
如遇到问题,可以重置整个服务堆栈:
```bash
# 停止并删除所有容器
docker-compose -f docker-compose.development.yml down
# 删除卷(这将删除所有数据)
docker-compose -f docker-compose.development.yml down -v
# 重新启动
docker-compose -f docker-compose.development.yml up -d
pnpm db:migrate
```
### 端口冲突
如果端口已被占用:
```bash
# 检查端口使用情况
lsof -i :5432 # PostgreSQL
lsof -i :9000 # MinIO API
lsof -i :9001 # MinIO 控制台
```
### 数据库迁移
配置脚本会自动运行迁移。如需手动运行:
```bash
pnpm db:migrate
```
注意:在使用 `pnpm dev:desktop` 的开发模式下,迁移也会在启动时自动运行。
---
在开发过程中,如果你在环境设置上遇到任何问题,或者有任何关于 LobeHub 开发的问题,欢迎随时向我们提问。我们期待看到你的贡献!
@@ -1,195 +0,0 @@
---
title: Work with Server-Side Database
description: Learn how to set up a server-side database for LobeHub with Docker.
tags:
- LobeHub
- Server-Side Database
- Docker
- PostgreSQL
- MinIO
---
# Work with Server-Side Database
LobeHub provides a battery-included experience with its client-side database.
While some features you really care about is only available at a server-side development.
In order to work with the aspect of server-side database,
you can setup all the prerequisites by following the [Deploying Server-Side Database](https://lobehub.com/docs/self-hosting/server-database) story.
But here is the easier approach that can reduce your pain.
## Quick Setup
### Environment Configuration
First, copy the example environment file to create your Docker Compose configuration:
```bash
cp docker-compose/local/.env.example docker-compose/local/.env
```
Edit `docker-compose/local/.env` as needed for your development setup. This file contains all necessary environment variables for the Docker services and configures:
- **Database**: PostgreSQL with connection string
- **Authentication**: NextAuth with Casdoor SSO
- **Storage**: MinIO S3-compatible storage
- **Search**: SearXNG search engine
### Start Docker Services
Start all required services using Docker Compose:
```bash
docker-compose -f docker-compose.development.yml up -d
```
This will start the following services:
- PostgreSQL database (port 5432)
- MinIO storage (port 9000)
- Casdoor authentication (port 8000)
- SearXNG search (port 8080)
### Run Database Migrations
Execute the database migration script to create all necessary tables:
```bash
pnpm db:migrate
```
You should see: `✅ database migration pass.`
### Start Development Server
Launch the LobeHub development server:
```bash
pnpm dev
```
The server will start on `http://localhost:3010`
And you can check all Docker services are running by running:
```bash
docker-compose -f docker-compose.development.yml ps
```
## Image Generation Development
When working with image generation features (text-to-image, image-to-image), the Docker Compose setup already includes all necessary storage services for handling generated images and user uploads.
### Image Generation Configuration
The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `docker-compose/local/.env.example`. No additional setup is required.
### Image Generation Architecture
The image generation feature requires:
- **PostgreSQL**: Stores metadata about generated images
- **MinIO/S3**: Stores the actual image files
### Storage Configuration
The `docker-compose/local/.env.example` file includes all necessary S3 environment variables:
```bash
# S3 Storage Configuration (MinIO for local development)
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
S3_ENDPOINT=http://localhost:${MINIO_PORT}
S3_BUCKET=${MINIO_LOBE_BUCKET}
S3_ENABLE_PATH_STYLE=1 # Required for MinIO
S3_SET_ACL=0 # MinIO compatibility
```
### File Storage Structure
Generated images and user uploads are organized in the MinIO bucket:
```
lobe/ # S3 Bucket (MINIO_LOBE_BUCKET)
├── generated/ # Generated images
│ └── {userId}/
│ └── {sessionId}/
│ └── {imageId}.png
└── uploads/ # User uploads for image-to-image
└── {userId}/
└── {fileId}.{ext}
```
### Development Workflow for Images
When developing image generation features, generated images will be:
1. Created by the AI model
2. Uploaded to S3/MinIO via presigned URLs
3. Metadata stored in PostgreSQL
4. Served via the public S3 URL
Example code for testing image upload:
```typescript
// Example: Upload generated image
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
filename: 'generated-image.png',
contentType: 'image/png',
});
// Upload to S3
await fetch(uploadUrl, {
method: 'PUT',
body: imageBlob,
headers: { 'Content-Type': 'image/png' },
});
```
### Service URLs
When running with Docker Compose development setup:
- **PostgreSQL**: `postgres://postgres@localhost:5432/LobeHub`
- **MinIO API**: `http://localhost:9000`
- **MinIO Console**: `http://localhost:9001` (admin/CHANGE\_THIS\_PASSWORD\_IN\_PRODUCTION)
- **Application**: `http://localhost:3010`
### Reset Services
If you encounter issues, you can reset the entire stack:
```bash
# Stop and remove all containers
docker-compose -f docker-compose.development.yml down
# Remove volumes (this will delete all data)
docker-compose -f docker-compose.development.yml down -v
# Start fresh
docker-compose -f docker-compose.development.yml up -d
pnpm db:migrate
```
### Troubleshooting
#### Port Conflicts
If ports are already in use:
```bash
# Check what's using the ports
lsof -i :5432 # PostgreSQL
lsof -i :9000 # MinIO API
lsof -i :9001 # MinIO Console
```
#### Database Migrations
The setup script runs migrations automatically. If you need to run them manually:
```bash
pnpm db:migrate
```
Note: In development mode with `pnpm dev:desktop`, migrations also run automatically on startup.
@@ -1,195 +0,0 @@
---
title: 使用服务端数据库
description: 快速设置 LobeHub 服务端数据库,支持 Docker 和图像生成。
tags:
- 服务端数据库
- LobeHub
- Docker
- 图像生成
- PostgreSQL
---
# 使用服务端数据库
LobeHub 提供了内置的客户端数据库体验。
但某些重要功能仅在服务端开发中可用。
为了使用服务端数据库功能,
需要参考 [部署服务端数据库](https://lobehub.com/docs/self-hosting/server-database) 的说明来配置所有前置条件。
本文档提供了一个更简化的配置方法,能够在本地开发时快速启动简化的服务端环境。
## 快速设置
### 环境配置
首先,复制示例环境文件来创建你的 Docker Compose 配置:
```bash
cp docker-compose/local/.env.example docker-compose/local/.env
```
根据需要编辑 `docker-compose/local/.env` 文件以适应你的开发设置。此文件包含 Docker 服务所需的所有环境变量,配置了:
- **数据库**: 带连接字符串的 PostgreSQL
- **身份验证**: 带 Casdoor SSO 的 NextAuth
- **存储**: MinIO S3 兼容存储
- **搜索**: SearXNG 搜索引擎
### 启动 Docker 服务
使用 Docker Compose 启动所有必需的服务:
```bash
docker-compose -f docker-compose.development.yml up -d
```
这将启动以下服务:
- PostgreSQL 数据库(端口 5432
- MinIO 存储(端口 9000
- Casdoor 身份验证(端口 8000
- SearXNG 搜索(端口 8080
### 运行数据库迁移
执行数据库迁移脚本以创建所有必要的表:
```bash
pnpm db:migrate
```
预期输出:`✅ database migration pass.`
### 启动开发服务器
启动 LobeHub 开发服务器:
```bash
pnpm dev
```
服务器将在 `http://localhost:3010` 上启动
可以通过运行以下命令检查所有 Docker 服务运行状态:
```bash
docker-compose -f docker-compose.development.yml ps
```
## 图像生成开发
在开发图像生成功能(文生图、图生图)时,Docker Compose 配置已经包含了处理生成图像和用户上传所需的所有存储服务。
### 图像生成配置
现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `docker-compose/local/.env.example` 中的所有必要环境变量。无需额外配置。
### 图像生成架构
图像生成功能需要:
- **PostgreSQL**:存储生成图像的元数据
- **MinIO/S3**:存储实际的图像文件
### 存储配置
`docker-compose/local/.env.example` 文件包含所有必要的 S3 环境变量:
```bash
# S3 存储配置(本地开发使用 MinIO)
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
S3_ENDPOINT=http://localhost:${MINIO_PORT}
S3_BUCKET=${MINIO_LOBE_BUCKET}
S3_ENABLE_PATH_STYLE=1 # MinIO 必需
S3_SET_ACL=0 # MinIO 兼容性
```
### 文件存储结构
生成的图像和用户上传在 MinIO 存储桶中按以下方式组织:
```
lobe/ # S3 存储桶 (MINIO_LOBE_BUCKET)
├── generated/ # 生成的图像
│ └── {userId}/
│ └── {sessionId}/
│ └── {imageId}.png
└── uploads/ # 用户上传的图像处理文件
└── {userId}/
└── {fileId}.{ext}
```
### 图像开发工作流
在开发图像生成功能时,生成的图像将:
1. 由 AI 模型创建
2. 通过预签名 URL 上传到 S3/MinIO
3. 元数据存储在 PostgreSQL 中
4. 通过公共 S3 URL 提供服务
测试图像上传的示例代码:
```typescript
// 示例:上传生成的图像
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
filename: 'generated-image.png',
contentType: 'image/png',
});
// 上传到 S3
await fetch(uploadUrl, {
method: 'PUT',
body: imageBlob,
headers: { 'Content-Type': 'image/png' },
});
```
### 服务地址
运行 Docker Compose 开发环境时:
- **PostgreSQL**`postgres://postgres@localhost:5432/LobeHub`
- **MinIO API**`http://localhost:9000`
- **MinIO 控制台**`http://localhost:9001` (admin/CHANGE\_THIS\_PASSWORD\_IN\_PRODUCTION)
- **应用程序**`http://localhost:3010`
### 重置服务
如遇到问题,可以重置整个服务堆栈:
```bash
# 停止并删除所有容器
docker-compose -f docker-compose.development.yml down
# 删除卷(这将删除所有数据)
docker-compose -f docker-compose.development.yml down -v
# 重新启动
docker-compose -f docker-compose.development.yml up -d
pnpm db:migrate
```
### 故障排除
#### 端口冲突
如果端口已被占用:
```bash
# 检查端口使用情况
lsof -i :5432 # PostgreSQL
lsof -i :9000 # MinIO API
lsof -i :9001 # MinIO 控制台
```
#### 数据库迁移
配置脚本会自动运行迁移。如需手动运行:
```bash
pnpm db:migrate
```
注意:在使用 `pnpm dev:desktop` 的开发模式下,迁移也会在启动时自动运行。
+1
View File
@@ -643,6 +643,7 @@ table messages {
thread_id [name: 'messages_thread_id_idx']
agent_id [name: 'messages_agent_id_idx']
group_id [name: 'messages_group_id_idx']
message_group_id [name: 'messages_message_group_id_idx']
}
}
+19 -19
View File
@@ -42,7 +42,7 @@ To enable Better Auth in LobeHub, set the following environment variables:
| --------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Google | `google` | `AUTH_GOOGLE_ID`, `AUTH_GOOGLE_SECRET` |
| GitHub | `github` | `AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET` |
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET` |
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET`, `AUTH_MICROSOFT_AUTHORITY_URL`, `AUTH_MICROSOFT_TENANT_ID` |
| Apple | `apple` | `AUTH_APPLE_CLIENT_ID`, `AUTH_APPLE_CLIENT_SECRET` |
| AWS Cognito | `cognito` | `AUTH_COGNITO_ID`, `AUTH_COGNITO_SECRET`, `AUTH_COGNITO_DOMAIN`, `AUTH_COGNITO_REGION`, `AUTH_COGNITO_USERPOOL_ID` |
| Auth0 | `auth0` | `AUTH_AUTH0_ID`, `AUTH_AUTH0_SECRET`, `AUTH_AUTH0_ISSUER` |
@@ -61,41 +61,41 @@ To enable Better Auth in LobeHub, set the following environment variables:
Click on a provider below for detailed configuration guides:
<Cards>
<Card href={'/docs/self-hosting/advanced/auth/providers/password'} title={'Email/Password'} />
<Card href={'/docs/self-hosting/auth/providers/password'} title={'Email/Password'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/github'} title={'GitHub'} />
<Card href={'/docs/self-hosting/auth/providers/github'} title={'GitHub'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/google'} title={'Google'} />
<Card href={'/docs/self-hosting/auth/providers/google'} title={'Google'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/microsoft'} title={'Microsoft'} />
<Card href={'/docs/self-hosting/auth/providers/microsoft'} title={'Microsoft'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/apple'} title={'Apple'} />
<Card href={'/docs/self-hosting/auth/providers/apple'} title={'Apple'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/cognito'} title={'AWS Cognito'} />
<Card href={'/docs/self-hosting/auth/providers/cognito'} title={'AWS Cognito'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/auth0'} title={'Auth0'} />
<Card href={'/docs/self-hosting/auth/providers/auth0'} title={'Auth0'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/authelia'} title={'Authelia'} />
<Card href={'/docs/self-hosting/auth/providers/authelia'} title={'Authelia'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/authentik'} title={'Authentik'} />
<Card href={'/docs/self-hosting/auth/providers/authentik'} title={'Authentik'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/casdoor'} title={'Casdoor'} />
<Card href={'/docs/self-hosting/auth/providers/casdoor'} title={'Casdoor'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/docs/self-hosting/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/keycloak'} title={'Keycloak'} />
<Card href={'/docs/self-hosting/auth/providers/keycloak'} title={'Keycloak'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/logto'} title={'Logto'} />
<Card href={'/docs/self-hosting/auth/providers/logto'} title={'Logto'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/okta'} title={'Okta'} />
<Card href={'/docs/self-hosting/auth/providers/okta'} title={'Okta'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/zitadel'} title={'ZITADEL'} />
<Card href={'/docs/self-hosting/auth/providers/zitadel'} title={'ZITADEL'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/generic-oidc'} title={'Generic OIDC'} />
<Card href={'/docs/self-hosting/auth/providers/generic-oidc'} title={'Generic OIDC'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/feishu'} title={'Feishu'} />
<Card href={'/docs/self-hosting/auth/providers/feishu'} title={'Feishu'} />
<Card href={'/docs/self-hosting/advanced/auth/providers/wechat'} title={'WeChat'} />
<Card href={'/docs/self-hosting/auth/providers/wechat'} title={'WeChat'} />
</Cards>
## Callback URL Format
+19 -19
View File
@@ -42,7 +42,7 @@ LobeHub 支持使用 Better Auth 配置外部身份验证服务,供企业 /
| --------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Google | `google` | `AUTH_GOOGLE_ID`, `AUTH_GOOGLE_SECRET` |
| GitHub | `github` | `AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET` |
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET` |
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET`, `AUTH_MICROSOFT_AUTHORITY_URL`, `AUTH_MICROSOFT_TENANT_ID` |
| Apple | `apple` | `AUTH_APPLE_CLIENT_ID`, `AUTH_APPLE_CLIENT_SECRET` |
| AWS Cognito | `cognito` | `AUTH_COGNITO_ID`, `AUTH_COGNITO_SECRET`, `AUTH_COGNITO_DOMAIN`, `AUTH_COGNITO_REGION`, `AUTH_COGNITO_USERPOOL_ID` |
| Auth0 | `auth0` | `AUTH_AUTH0_ID`, `AUTH_AUTH0_SECRET`, `AUTH_AUTH0_ISSUER` |
@@ -61,41 +61,41 @@ LobeHub 支持使用 Better Auth 配置外部身份验证服务,供企业 /
点击下方提供商查看详细配置指南:
<Cards>
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/password'} title={'邮箱密码'} />
<Card href={'/zh/docs/self-hosting/auth/providers/password'} title={'邮箱密码'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/github'} title={'GitHub'} />
<Card href={'/zh/docs/self-hosting/auth/providers/github'} title={'GitHub'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/google'} title={'Google'} />
<Card href={'/zh/docs/self-hosting/auth/providers/google'} title={'Google'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/microsoft'} title={'Microsoft'} />
<Card href={'/zh/docs/self-hosting/auth/providers/microsoft'} title={'Microsoft'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/apple'} title={'Apple'} />
<Card href={'/zh/docs/self-hosting/auth/providers/apple'} title={'Apple'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/cognito'} title={'AWS Cognito'} />
<Card href={'/zh/docs/self-hosting/auth/providers/cognito'} title={'AWS Cognito'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/auth0'} title={'Auth0'} />
<Card href={'/zh/docs/self-hosting/auth/providers/auth0'} title={'Auth0'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/authelia'} title={'Authelia'} />
<Card href={'/zh/docs/self-hosting/auth/providers/authelia'} title={'Authelia'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/authentik'} title={'Authentik'} />
<Card href={'/zh/docs/self-hosting/auth/providers/authentik'} title={'Authentik'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/casdoor'} title={'Casdoor'} />
<Card href={'/zh/docs/self-hosting/auth/providers/casdoor'} title={'Casdoor'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/zh/docs/self-hosting/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/keycloak'} title={'Keycloak'} />
<Card href={'/zh/docs/self-hosting/auth/providers/keycloak'} title={'Keycloak'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/logto'} title={'Logto'} />
<Card href={'/zh/docs/self-hosting/auth/providers/logto'} title={'Logto'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/okta'} title={'Okta'} />
<Card href={'/zh/docs/self-hosting/auth/providers/okta'} title={'Okta'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/zitadel'} title={'ZITADEL'} />
<Card href={'/zh/docs/self-hosting/auth/providers/zitadel'} title={'ZITADEL'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/generic-oidc'} title={'Generic OIDC'} />
<Card href={'/zh/docs/self-hosting/auth/providers/generic-oidc'} title={'Generic OIDC'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/feishu'} title={'飞书'} />
<Card href={'/zh/docs/self-hosting/auth/providers/feishu'} title={'飞书'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/wechat'} title={'微信'} />
<Card href={'/zh/docs/self-hosting/auth/providers/wechat'} title={'微信'} />
</Cards>
## 回调 URL 格式
+15 -15
View File
@@ -13,7 +13,7 @@ tags:
# Legacy Authentication
<Callout type={'warning'}>
**Legacy Notice**: NextAuth and Clerk are legacy authentication methods. For new deployments, we strongly recommend using [Better Auth](/docs/self-hosting/advanced/auth) for its simplicity and flexibility.
**Legacy Notice**: NextAuth and Clerk are legacy authentication methods. For new deployments, we strongly recommend using [Better Auth](/docs/self-hosting/auth) for its simplicity and flexibility.
</Callout>
This page documents the legacy authentication methods (NextAuth and Clerk) for users who are still using these services.
@@ -27,17 +27,17 @@ LobeHub has deeply integrated with Clerk to provide users with a secure and conv
By setting the environment variables `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` in LobeHub's environment, you can enable and use Clerk.
<Callout type={'info'}>
For detailed Clerk configuration, see [Clerk Configuration Guide](/docs/self-hosting/advanced/auth/clerk).
For detailed Clerk configuration, see [Clerk Configuration Guide](/docs/self-hosting/auth/clerk).
</Callout>
<Callout type={'tip'}>
To migrate from Clerk to Better Auth, see the [Clerk Migration Guide](/docs/self-hosting/advanced/auth/clerk-to-betterauth).
To migrate from Clerk to Better Auth, see the [Clerk Migration Guide](/docs/self-hosting/migration/v2/auth/clerk-to-betterauth).
</Callout>
## Next Auth
<Callout type={'tip'}>
To migrate from NextAuth to Better Auth, see the [NextAuth Migration Guide](/docs/self-hosting/advanced/auth/nextauth-to-betterauth).
To migrate from NextAuth to Better Auth, see the [NextAuth Migration Guide](/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth).
</Callout>
Before using NextAuth, please set the following variables in LobeHub's environment variables:
@@ -53,27 +53,27 @@ Before using NextAuth, please set the following variables in LobeHub's environme
Currently supported identity verification services include:
<Cards>
<Card href={'/docs/self-hosting/advanced/auth/next-auth/auth0'} title={'Auth0'} />
<Card href={'/docs/self-hosting/auth/next-auth/auth0'} title={'Auth0'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
<Card href={'/docs/self-hosting/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/authentik'} title={'Authentik'} />
<Card href={'/docs/self-hosting/auth/next-auth/authentik'} title={'Authentik'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/github'} title={'Github'} />
<Card href={'/docs/self-hosting/auth/next-auth/github'} title={'Github'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/zitadel'} title={'ZITADEL'} />
<Card href={'/docs/self-hosting/auth/next-auth/zitadel'} title={'ZITADEL'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/docs/self-hosting/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/authelia'} title={'Authelia'} />
<Card href={'/docs/self-hosting/auth/next-auth/authelia'} title={'Authelia'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/logto'} title={'Logto'} />
<Card href={'/docs/self-hosting/auth/next-auth/logto'} title={'Logto'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/keycloak'} title={'Keycloak'} />
<Card href={'/docs/self-hosting/auth/next-auth/keycloak'} title={'Keycloak'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/google'} title={'Google'} />
<Card href={'/docs/self-hosting/auth/next-auth/google'} title={'Google'} />
<Card href={'/docs/self-hosting/advanced/auth/next-auth/okta'} title={'Okta'} />
<Card href={'/docs/self-hosting/auth/next-auth/okta'} title={'Okta'} />
</Cards>
Click on the links to view the corresponding platform's configuration documentation.
+14 -14
View File
@@ -11,7 +11,7 @@ tags:
# 旧版身份验证
<Callout type={'warning'}>
**旧版提示**NextAuth 和 Clerk 是旧版身份验证方案。对于新部署,我们强烈建议使用 [Better Auth](/zh/docs/self-hosting/advanced/auth),它更简洁、更灵活。
**旧版提示**NextAuth 和 Clerk 是旧版身份验证方案。对于新部署,我们强烈建议使用 [Better Auth](/zh/docs/self-hosting/auth),它更简洁、更灵活。
</Callout>
本页面为仍在使用这些服务的用户提供旧版身份验证方案(NextAuth 和 Clerk)的文档。
@@ -25,17 +25,17 @@ LobeHub 与 Clerk 做了深度集成,能够为用户提供安全、便捷的
在 LobeHub 的环境变量中设置 `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` 和 `CLERK_SECRET_KEY`,即可开启和使用 Clerk。
<Callout type={'info'}>
详细的 Clerk 配置请参阅 [Clerk 配置指南](/zh/docs/self-hosting/advanced/auth/clerk)。
详细的 Clerk 配置请参阅 [Clerk 配置指南](/zh/docs/self-hosting/auth/clerk)。
</Callout>
<Callout type={'tip'}>
如需从 Clerk 迁移到 Better Auth,请参阅 [Clerk 迁移指南](/zh/docs/self-hosting/advanced/auth/clerk-to-betterauth)。
如需从 Clerk 迁移到 Better Auth,请参阅 [Clerk 迁移指南](/zh/docs/self-hosting/migration/v2/auth/clerk-to-betterauth)。
</Callout>
## Next Auth
<Callout type={'tip'}>
如需从 NextAuth 迁移到 Better Auth,请参阅 [NextAuth 迁移指南](/zh/docs/self-hosting/advanced/auth/nextauth-to-betterauth)。
如需从 NextAuth 迁移到 Better Auth,请参阅 [NextAuth 迁移指南](/zh/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth)。
</Callout>
在使用 NextAuth 之前,请先在 LobeHub 的环境变量中设置以下变量:
@@ -51,25 +51,25 @@ LobeHub 与 Clerk 做了深度集成,能够为用户提供安全、便捷的
目前支持的身份验证服务有:
<Cards>
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/auth0'} title={'Auth0'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/auth0'} title={'Auth0'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/authentik'} title={'Authentik'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/authentik'} title={'Authentik'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/github'} title={'Github'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/github'} title={'Github'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/zitadel'} title={'ZITADEL'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/zitadel'} title={'ZITADEL'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/authelia'} title={'Authelia'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/authelia'} title={'Authelia'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/logto'} title={'Logto'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/logto'} title={'Logto'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/keycloak'} title={'Keycloak'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/keycloak'} title={'Keycloak'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/okta'} title={'Okta'} />
<Card href={'/zh/docs/self-hosting/auth/next-auth/okta'} title={'Okta'} />
</Cards>
点击即可查看对应平台的配置文档。
+8 -10
View File
@@ -70,12 +70,14 @@ tags:
### Configure Environment Variables
| Environment Variable | Type | Description |
| ----------------------- | -------- | --------------------------------------------------------------- |
| `AUTH_SECRET` | Required | Session encryption key, generate with `openssl rand -base64 32` |
| `AUTH_SSO_PROVIDERS` | Required | Set to `microsoft` |
| `AUTH_MICROSOFT_ID` | Required | Application (client) ID |
| `AUTH_MICROSOFT_SECRET` | Required | Client secret value |
| Environment Variable | Type | Description |
| ------------------------------ | -------- | --------------------------------------------------------------- |
| `AUTH_SECRET` | Required | Session encryption key, generate with `openssl rand -base64 32` |
| `AUTH_SSO_PROVIDERS` | Required | Set to `microsoft` |
| `AUTH_MICROSOFT_ID` | Required | Application (client) ID |
| `AUTH_MICROSOFT_SECRET` | Required | Client secret value |
| `AUTH_MICROSOFT_AUTHORITY_URL` | Optional | Authority URL for Microsoft Entra ID |
| `AUTH_MICROSOFT_TENANT_ID` | Optional | Directory (tenant) ID for single-tenant apps |
<Callout type={'info'}>
**Alternative Environment Variables**: For backward compatibility, these
@@ -99,10 +101,6 @@ tags:
## Common Issues
### Tenant Configuration
By default, LobeHub uses `common` tenant which allows both organizational and personal Microsoft accounts. If you need single-tenant configuration, you may need to customize the tenant settings.
### Client Secret Expiration
Microsoft client secrets have a maximum validity of 24 months. Remember to rotate secrets before they expire.
@@ -68,12 +68,14 @@ tags:
### 配置环境变量
| 环境变量 | 类型 | 描述 |
| ----------------------- | -- | -------------------------------------- |
| `AUTH_SECRET` | 必选 | 会话加密密钥,使用 `openssl rand -base64 32` 生成 |
| `AUTH_SSO_PROVIDERS` | 必选 | 填写 `microsoft` |
| `AUTH_MICROSOFT_ID` | 必选 | Application (client) ID |
| `AUTH_MICROSOFT_SECRET` | 必选 | 客户端密钥值 |
| 环境变量 | 类型 | 描述 |
| ------------------------------ | -- | -------------------------------------- |
| `AUTH_SECRET` | 必选 | 会话加密密钥,使用 `openssl rand -base64 32` 生成 |
| `AUTH_SSO_PROVIDERS` | 必选 | 填写 `microsoft` |
| `AUTH_MICROSOFT_ID` | 必选 | Application (client) ID |
| `AUTH_MICROSOFT_SECRET` | 必选 | 客户端密钥值 |
| `AUTH_MICROSOFT_AUTHORITY_URL` | 可选 | Microsoft Entra ID 的 Authority URL |
| `AUTH_MICROSOFT_TENANT_ID` | 可选 | 单租户应用的 Directory (tenant) ID |
<Callout type={'info'}>
**兼容的环境变量**:为了向后兼容,以下别名也支持:
@@ -95,10 +97,6 @@ tags:
## 常见问题
### 租户配置
默认情况下,LobeHub 使用 `common` 租户,允许组织帐户和个人 Microsoft 帐户登录。如果需要单租户配置,可能需要自定义租户设置。
### 客户端密钥过期
Microsoft 客户端密钥最长有效期为 24 个月。请记得在过期前轮换密钥。
@@ -162,6 +162,20 @@ These settings are required for email verification and password reset features.
- Default: `-`
- Example: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
#### `AUTH_MICROSOFT_AUTHORITY_URL`
- Type: Optional
- Description: Authority URL for the Microsoft Entra ID. This is used to specify the endpoint for authentication requests.
- Default: `https://login.microsoftonline.com`
- Example: `https://login.partner.microsoftonline.cn`
#### `AUTH_MICROSOFT_TENANT_ID`
- Type: Optional
- Description: Directory (tenant) ID for single-tenant Microsoft Entra ID applications.
- Default: `common`
- Example: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
### AWS Cognito
#### `AUTH_COGNITO_ID`
@@ -160,6 +160,20 @@ LobeHub 在部署时提供了完善的身份验证服务能力,以下是相关
- 默认值:`-`
- 示例:`xxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
#### `AUTH_MICROSOFT_AUTHORITY_URL`
- 类型:可选
- 描述:Microsoft Entra ID 的 Authority URL。
- 默认值:`https://login.microsoftonline.com`
- 示例:`https://login.partner.microsoftonline.cn`
#### `AUTH_MICROSOFT_TENANT_ID`
- 类型:可选
- 描述:单租户 Microsoft Entra ID 应用的 Directory (tenant) ID。
- 默认值:`common`
- 示例:`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
### AWS Cognito
#### `AUTH_COGNITO_ID`
@@ -57,7 +57,7 @@ For small self-hosted deployments, the simplest approach is to let users reset t
1. **Configure Email Service**
Set up email service for password reset functionality. See [Email Service Configuration](/docs/self-hosting/advanced/auth#email-service-configuration).
Set up email service for password reset functionality. See [Email Service Configuration](/docs/self-hosting/auth#email-service-configuration).
2. **Update Environment Variables**
@@ -80,7 +80,7 @@ For small self-hosted deployments, the simplest approach is to let users reset t
```
<Callout type={'tip'}>
See [Authentication Service Configuration](/docs/self-hosting/advanced/auth) for complete environment variables and SSO provider setup.
See [Authentication Service Configuration](/docs/self-hosting/auth) for complete environment variables and SSO provider setup.
</Callout>
3. **Redeploy LobeHub**
@@ -289,7 +289,7 @@ npx tsx scripts/clerk-to-betterauth/verify.ts
After migration is complete, follow [Simple Migration - Step 2](#steps) to configure Better Auth environment variables and redeploy.
<Callout type={'tip'}>
For complete Better Auth configuration, see [Authentication Service Configuration](/docs/self-hosting/advanced/auth).
For complete Better Auth configuration, see [Authentication Service Configuration](/docs/self-hosting/auth).
</Callout>
## What Gets Migrated
@@ -339,11 +339,11 @@ This error occurs because the database schema is outdated. Run `pnpm db:migrate`
## Related Reading
<Cards>
<Card href={'/docs/self-hosting/advanced/auth/migration-internals'} title={'Migration Technical Deep Dive'} />
<Card href={'/docs/self-hosting/migration/v2/auth/migration-internals'} title={'Migration Technical Deep Dive'} />
<Card href={'/docs/self-hosting/advanced/auth'} title={'Authentication Service Configuration'} />
<Card href={'/docs/self-hosting/auth'} title={'Authentication Service Configuration'} />
<Card href={'/docs/self-hosting/environment-variables/auth'} title={'Auth Environment Variables'} />
<Card href={'/docs/self-hosting/advanced/auth/legacy'} title={'Legacy Authentication (NextAuth & Clerk)'} />
<Card href={'/docs/self-hosting/auth/legacy'} title={'Legacy Authentication (NextAuth & Clerk)'} />
</Cards>
@@ -55,7 +55,7 @@ tags:
1. **配置邮件服务**
设置邮件服务以支持密码重置功能。参阅 [邮件服务配置](/zh/docs/self-hosting/advanced/auth#邮件服务配置)。
设置邮件服务以支持密码重置功能。参阅 [邮件服务配置](/zh/docs/self-hosting/auth#邮件服务配置)。
2. **更新环境变量**
@@ -78,7 +78,7 @@ tags:
```
<Callout type={'tip'}>
查阅 [身份验证服务配置](/zh/docs/self-hosting/advanced/auth) 了解完整的环境变量和 SSO 提供商配置。
查阅 [身份验证服务配置](/zh/docs/self-hosting/auth) 了解完整的环境变量和 SSO 提供商配置。
</Callout>
3. **重新部署 LobeHub**
@@ -283,7 +283,7 @@ npx tsx scripts/clerk-to-betterauth/verify.ts
迁移完成后,参照 [简单迁移 - 步骤 2](#步骤) 配置 Better Auth 环境变量并重新部署。
<Callout type={'tip'}>
完整的 Better Auth 配置请参阅 [身份验证服务配置](/zh/docs/self-hosting/advanced/auth),包括所有支持的 SSO 提供商和邮件服务配置。
完整的 Better Auth 配置请参阅 [身份验证服务配置](/zh/docs/self-hosting/auth),包括所有支持的 SSO 提供商和邮件服务配置。
</Callout>
## 迁移内容对比
@@ -333,11 +333,11 @@ npx tsx scripts/clerk-to-betterauth/verify.ts
## 相关阅读
<Cards>
<Card href={'/zh/docs/self-hosting/advanced/auth/migration-internals'} title={'迁移技术原理'} />
<Card href={'/zh/docs/self-hosting/migration/v2/auth/migration-internals'} title={'迁移技术原理'} />
<Card href={'/zh/docs/self-hosting/advanced/auth'} title={'身份验证服务配置'} />
<Card href={'/zh/docs/self-hosting/auth'} title={'身份验证服务配置'} />
<Card href={'/zh/docs/self-hosting/environment-variables/auth'} title={'认证相关环境变量'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/legacy'} title={'旧版身份验证(NextAuth 和 Clerk'} />
<Card href={'/zh/docs/self-hosting/auth/legacy'} title={'旧版身份验证(NextAuth 和 Clerk'} />
</Cards>
@@ -15,7 +15,7 @@ tags:
This document explains the technical principles behind authentication migration in LobeHub. It's intended for users with database and development experience who want to understand how migration works under the hood.
<Callout type={'info'}>
For step-by-step migration instructions, see [NextAuth Migration](/docs/self-hosting/advanced/auth/nextauth-to-betterauth) or [Clerk Migration](/docs/self-hosting/advanced/auth/clerk-to-betterauth).
For step-by-step migration instructions, see [NextAuth Migration](/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth) or [Clerk Migration](/docs/self-hosting/migration/v2/auth/clerk-to-betterauth).
</Callout>
## Core Database Schema
@@ -193,9 +193,9 @@ This typically happens with simple migration when logging in with a secondary em
## Related Reading
<Cards>
<Card href={'/docs/self-hosting/advanced/auth/nextauth-to-betterauth'} title={'NextAuth Migration Guide'} />
<Card href={'/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth'} title={'NextAuth Migration Guide'} />
<Card href={'/docs/self-hosting/advanced/auth/clerk-to-betterauth'} title={'Clerk Migration Guide'} />
<Card href={'/docs/self-hosting/migration/v2/auth/clerk-to-betterauth'} title={'Clerk Migration Guide'} />
<Card href={'/docs/self-hosting/advanced/auth'} title={'Authentication Configuration'} />
<Card href={'/docs/self-hosting/auth'} title={'Authentication Configuration'} />
</Cards>
@@ -14,7 +14,7 @@ tags:
本文档解释 LobeHub 认证迁移的技术原理,适合有数据库和开发经验的用户,帮助理解迁移的底层逻辑。
<Callout type={'info'}>
如需分步迁移指南,请参阅 [NextAuth 迁移](/docs/self-hosting/advanced/auth/nextauth-to-betterauth) 或 [Clerk 迁移](/docs/self-hosting/advanced/auth/clerk-to-betterauth)。
如需分步迁移指南,请参阅 [NextAuth 迁移](/zh/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth) 或 [Clerk 迁移](/zh/docs/self-hosting/migration/v2/auth/clerk-to-betterauth)。
</Callout>
## 核心数据库 Schema
@@ -192,9 +192,9 @@ tags:
## 相关阅读
<Cards>
<Card href={'/docs/self-hosting/advanced/auth/nextauth-to-betterauth'} title={'NextAuth 迁移指南'} />
<Card href={'/zh/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth'} title={'NextAuth 迁移指南'} />
<Card href={'/docs/self-hosting/advanced/auth/clerk-to-betterauth'} title={'Clerk 迁移指南'} />
<Card href={'/zh/docs/self-hosting/migration/v2/auth/clerk-to-betterauth'} title={'Clerk 迁移指南'} />
<Card href={'/docs/self-hosting/advanced/auth'} title={'认证服务配置'} />
<Card href={'/zh/docs/self-hosting/auth'} title={'认证服务配置'} />
</Cards>
@@ -54,23 +54,23 @@ This guide helps you migrate your existing NextAuth-based LobeHub deployment to
SSO provider environment variables follow the same format: `AUTH_<PROVIDER>_ID` and `AUTH_<PROVIDER>_SECRET`.
| NextAuth (Old) | Better Auth (New) | Notes |
| ----------------------------------- | ----------------------- | ------------------- |
| `AUTH_GITHUB_ID` | `AUTH_GITHUB_ID` | ✅ Unchanged |
| `AUTH_GITHUB_SECRET` | `AUTH_GITHUB_SECRET` | ✅ Unchanged |
| `AUTH_GOOGLE_ID` | `AUTH_GOOGLE_ID` | ✅ Unchanged |
| `AUTH_GOOGLE_SECRET` | `AUTH_GOOGLE_SECRET` | ✅ Unchanged |
| `AUTH_AUTH0_ID` | `AUTH_AUTH0_ID` | ✅ Unchanged |
| `AUTH_AUTH0_SECRET` | `AUTH_AUTH0_SECRET` | ✅ Unchanged |
| `AUTH_AUTH0_ISSUER` | `AUTH_AUTH0_ISSUER` | ✅ Unchanged |
| `AUTH_AUTHENTIK_ID` | `AUTH_AUTHENTIK_ID` | ✅ Unchanged |
| `AUTH_AUTHENTIK_SECRET` | `AUTH_AUTHENTIK_SECRET` | ✅ Unchanged |
| `AUTH_AUTHENTIK_ISSUER` | `AUTH_AUTHENTIK_ISSUER` | ✅ Unchanged |
| `microsoft-entra-id` | `microsoft` | ⚠️ Provider renamed |
| `AUTH_MICROSOFT_ENTRA_ID_ID` | `AUTH_MICROSOFT_ID` | ⚠️ Variable renamed |
| `AUTH_MICROSOFT_ENTRA_ID_SECRET` | `AUTH_MICROSOFT_SECRET` | ⚠️ Variable renamed |
| `AUTH_MICROSOFT_ENTRA_ID_TENANT_ID` | - | ❌ No longer needed |
| `AUTH_MICROSOFT_ENTRA_ID_BASE_URL` | - | ❌ No longer needed |
| NextAuth (Old) | Better Auth (New) | Notes |
| ----------------------------------- | ------------------------------ | ------------------- |
| `AUTH_GITHUB_ID` | `AUTH_GITHUB_ID` | ✅ Unchanged |
| `AUTH_GITHUB_SECRET` | `AUTH_GITHUB_SECRET` | ✅ Unchanged |
| `AUTH_GOOGLE_ID` | `AUTH_GOOGLE_ID` | ✅ Unchanged |
| `AUTH_GOOGLE_SECRET` | `AUTH_GOOGLE_SECRET` | ✅ Unchanged |
| `AUTH_AUTH0_ID` | `AUTH_AUTH0_ID` | ✅ Unchanged |
| `AUTH_AUTH0_SECRET` | `AUTH_AUTH0_SECRET` | ✅ Unchanged |
| `AUTH_AUTH0_ISSUER` | `AUTH_AUTH0_ISSUER` | ✅ Unchanged |
| `AUTH_AUTHENTIK_ID` | `AUTH_AUTHENTIK_ID` | ✅ Unchanged |
| `AUTH_AUTHENTIK_SECRET` | `AUTH_AUTHENTIK_SECRET` | ✅ Unchanged |
| `AUTH_AUTHENTIK_ISSUER` | `AUTH_AUTHENTIK_ISSUER` | ✅ Unchanged |
| `microsoft-entra-id` | `microsoft` | ⚠️ Provider renamed |
| `AUTH_MICROSOFT_ENTRA_ID_ID` | `AUTH_MICROSOFT_ID` | ⚠️ Variable renamed |
| `AUTH_MICROSOFT_ENTRA_ID_SECRET` | `AUTH_MICROSOFT_SECRET` | ⚠️ Variable renamed |
| `AUTH_MICROSOFT_ENTRA_ID_TENANT_ID` | `AUTH_MICROSOFT_TENANT_ID` | ⚠️ Variable renamed |
| `AUTH_MICROSOFT_ENTRA_ID_BASE_URL` | `AUTH_MICROSOFT_AUTHORITY_URL` | ⚠️ Variable renamed |
<Callout type={'warning'}>
**Note**: Microsoft Entra ID provider name changed from `microsoft-entra-id` to `microsoft`, and the environment variable prefix changed from `AUTH_MICROSOFT_ENTRA_ID_` to `AUTH_MICROSOFT_`.
@@ -133,7 +133,7 @@ For small self-hosted deployments, the simplest approach is to let users re-logi
```
<Callout type={'tip'}>
See [Authentication Service Configuration](/docs/self-hosting/advanced/auth) for complete environment variables and SSO provider setup.
See [Authentication Service Configuration](/docs/self-hosting/auth) for complete environment variables and SSO provider setup.
</Callout>
2. **Redeploy LobeHub**
@@ -286,7 +286,7 @@ npx tsx scripts/nextauth-to-betterauth/verify.ts
After migration is complete, follow [Simple Migration - Step 1](#steps) to configure Better Auth environment variables and redeploy.
<Callout type={'tip'}>
For complete Better Auth configuration, see [Authentication Service Configuration](/docs/self-hosting/advanced/auth), including all supported SSO providers and email service configuration.
For complete Better Auth configuration, see [Authentication Service Configuration](/docs/self-hosting/auth), including all supported SSO providers and email service configuration.
</Callout>
## What Gets Migrated
@@ -360,19 +360,19 @@ For identity providers like Casdoor or Logto, users may not have an email config
Solution:
1. First configure the Webhook in LobeHub to sync user data from the identity provider:
- [Casdoor Webhook Configuration](/docs/self-hosting/advanced/auth/providers/casdoor)
- [Logto Webhook Configuration](/docs/self-hosting/advanced/auth/providers/logto)
- [Casdoor Webhook Configuration](/docs/self-hosting/auth/providers/casdoor)
- [Logto Webhook Configuration](/docs/self-hosting/auth/providers/logto)
2. Then configure the user's email in the identity provider's admin console
3. The user data will be synced to LobeHub via Webhook, and the user can then log in
## Related Reading
<Cards>
<Card href={'/docs/self-hosting/advanced/auth/migration-internals'} title={'Migration Technical Deep Dive'} />
<Card href={'/docs/self-hosting/migration/v2/auth/migration-internals'} title={'Migration Technical Deep Dive'} />
<Card href={'/docs/self-hosting/advanced/auth'} title={'Authentication Service Configuration'} />
<Card href={'/docs/self-hosting/auth'} title={'Authentication Service Configuration'} />
<Card href={'/docs/self-hosting/environment-variables/auth'} title={'Auth Environment Variables'} />
<Card href={'/docs/self-hosting/advanced/auth/legacy'} title={'Legacy Authentication (NextAuth & Clerk)'} />
<Card href={'/docs/self-hosting/auth/legacy'} title={'Legacy Authentication (NextAuth & Clerk)'} />
</Cards>
@@ -52,21 +52,23 @@ tags:
SSO 提供商的环境变量格式保持一致:`AUTH_<PROVIDER>_ID` 和 `AUTH_<PROVIDER>_SECRET`。
| NextAuth (旧) | Better Auth (新) | 说明 |
| -------------------------------- | ----------------------- | ---------------- |
| `AUTH_GITHUB_ID` | `AUTH_GITHUB_ID` | ✅ 保持不变 |
| `AUTH_GITHUB_SECRET` | `AUTH_GITHUB_SECRET` | ✅ 保持不变 |
| `AUTH_GOOGLE_ID` | `AUTH_GOOGLE_ID` | ✅ 保持不变 |
| `AUTH_GOOGLE_SECRET` | `AUTH_GOOGLE_SECRET` | ✅ 保持不变 |
| `AUTH_AUTH0_ID` | `AUTH_AUTH0_ID` | ✅ 保持不变 |
| `AUTH_AUTH0_SECRET` | `AUTH_AUTH0_SECRET` | ✅ 保持不变 |
| `AUTH_AUTH0_ISSUER` | `AUTH_AUTH0_ISSUER` | ✅ 保持不变 |
| `AUTH_AUTHENTIK_ID` | `AUTH_AUTHENTIK_ID` | ✅ 保持不变 |
| `AUTH_AUTHENTIK_SECRET` | `AUTH_AUTHENTIK_SECRET` | ✅ 保持不变 |
| `AUTH_AUTHENTIK_ISSUER` | `AUTH_AUTHENTIK_ISSUER` | ✅ 保持不变 |
| `microsoft-entra-id` | `microsoft` | ⚠️ provider 名称变更 |
| `AUTH_MICROSOFT_ENTRA_ID_ID` | `AUTH_MICROSOFT_ID` | ⚠️ 变量名变更 |
| `AUTH_MICROSOFT_ENTRA_ID_SECRET` | `AUTH_MICROSOFT_SECRET` | ⚠️ 变量名变更 |
| NextAuth (旧) | Better Auth (新) | 说明 |
| ----------------------------------- | ------------------------------ | ---------------- |
| `AUTH_GITHUB_ID` | `AUTH_GITHUB_ID` | ✅ 保持不变 |
| `AUTH_GITHUB_SECRET` | `AUTH_GITHUB_SECRET` | ✅ 保持不变 |
| `AUTH_GOOGLE_ID` | `AUTH_GOOGLE_ID` | ✅ 保持不变 |
| `AUTH_GOOGLE_SECRET` | `AUTH_GOOGLE_SECRET` | ✅ 保持不变 |
| `AUTH_AUTH0_ID` | `AUTH_AUTH0_ID` | ✅ 保持不变 |
| `AUTH_AUTH0_SECRET` | `AUTH_AUTH0_SECRET` | ✅ 保持不变 |
| `AUTH_AUTH0_ISSUER` | `AUTH_AUTH0_ISSUER` | ✅ 保持不变 |
| `AUTH_AUTHENTIK_ID` | `AUTH_AUTHENTIK_ID` | ✅ 保持不变 |
| `AUTH_AUTHENTIK_SECRET` | `AUTH_AUTHENTIK_SECRET` | ✅ 保持不变 |
| `AUTH_AUTHENTIK_ISSUER` | `AUTH_AUTHENTIK_ISSUER` | ✅ 保持不变 |
| `microsoft-entra-id` | `microsoft` | ⚠️ provider 名称变更 |
| `AUTH_MICROSOFT_ENTRA_ID_ID` | `AUTH_MICROSOFT_ID` | ⚠️ 变量名变更 |
| `AUTH_MICROSOFT_ENTRA_ID_SECRET` | `AUTH_MICROSOFT_SECRET` | ⚠️ 变量名变更 |
| `AUTH_MICROSOFT_ENTRA_ID_TENANT_ID` | `AUTH_MICROSOFT_TENANT_ID` | ⚠️ 变量名变更 |
| `AUTH_MICROSOFT_ENTRA_ID_BASE_URL` | `AUTH_MICROSOFT_AUTHORITY_URL` | ⚠️ 变量名变更 |
<Callout type={'warning'}>
**注意**Microsoft Entra ID 的 provider 名称从 `microsoft-entra-id` 改为 `microsoft`,相应的环境变量前缀也从 `AUTH_MICROSOFT_ENTRA_ID_` 改为 `AUTH_MICROSOFT_`。
@@ -128,7 +130,7 @@ Better Auth 支持更多功能,以下是新增的环境变量:
```
<Callout type={'tip'}>
查阅 [身份验证服务配置](/zh/docs/self-hosting/advanced/auth) 了解完整的环境变量和 SSO 提供商配置。
查阅 [身份验证服务配置](/zh/docs/self-hosting/auth) 了解完整的环境变量和 SSO 提供商配置。
</Callout>
2. **重新部署 LobeHub**
@@ -280,7 +282,7 @@ npx tsx scripts/nextauth-to-betterauth/verify.ts
迁移完成后,参照 [简单迁移 - 步骤 1](#步骤) 配置 Better Auth 环境变量并重新部署。
<Callout type={'tip'}>
完整的 Better Auth 配置请参阅 [身份验证服务配置](/zh/docs/self-hosting/advanced/auth),包括所有支持的 SSO 提供商和邮件服务配置。
完整的 Better Auth 配置请参阅 [身份验证服务配置](/zh/docs/self-hosting/auth),包括所有支持的 SSO 提供商和邮件服务配置。
</Callout>
## 迁移内容对比
@@ -354,19 +356,19 @@ npx tsx scripts/nextauth-to-betterauth/verify.ts
解决方案:
1. 先在 LobeHub 中配置身份提供商的 Webhook 以同步用户数据:
- [Casdoor Webhook 配置](/zh/docs/self-hosting/advanced/auth/providers/casdoor)
- [Logto Webhook 配置](/zh/docs/self-hosting/advanced/auth/providers/logto)
- [Casdoor Webhook 配置](/zh/docs/self-hosting/auth/providers/casdoor)
- [Logto Webhook 配置](/zh/docs/self-hosting/auth/providers/logto)
2. 然后在身份提供商的管理后台为用户配置邮箱
3. 用户数据通过 Webhook 同步到 LobeHub 后即可正常登录
## 相关阅读
<Cards>
<Card href={'/zh/docs/self-hosting/advanced/auth/migration-internals'} title={'迁移技术原理'} />
<Card href={'/zh/docs/self-hosting/migration/v2/auth/migration-internals'} title={'迁移技术原理'} />
<Card href={'/zh/docs/self-hosting/advanced/auth'} title={'身份验证服务配置'} />
<Card href={'/zh/docs/self-hosting/auth'} title={'身份验证服务配置'} />
<Card href={'/zh/docs/self-hosting/environment-variables/auth'} title={'认证相关环境变量'} />
<Card href={'/zh/docs/self-hosting/advanced/auth/legacy'} title={'旧版身份验证(NextAuth 和 Clerk'} />
<Card href={'/zh/docs/self-hosting/auth/legacy'} title={'旧版身份验证(NextAuth 和 Clerk'} />
</Cards>
@@ -60,11 +60,11 @@ LobeHub 2.0 only supports Better Auth authentication system. NextAuth and Clerk
### Migrating from NextAuth
See the [NextAuth Migration Guide](/docs/self-hosting/advanced/auth/nextauth-to-betterauth).
See the [NextAuth Migration Guide](/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth).
### Migrating from Clerk
See the [Clerk Migration Guide](/docs/self-hosting/advanced/auth/clerk-to-betterauth).
See the [Clerk Migration Guide](/docs/self-hosting/migration/v2/auth/clerk-to-betterauth).
## Database Mode Changes
@@ -58,11 +58,11 @@ LobeHub 2.0 仅支持 Better Auth 认证系统,不再支持 NextAuth 和 Clerk
### 从 NextAuth 迁移
请参阅 [NextAuth 迁移指南](/zh/docs/self-hosting/advanced/auth/nextauth-to-betterauth)。
请参阅 [NextAuth 迁移指南](/zh/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth)。
### 从 Clerk 迁移
请参阅 [Clerk 迁移指南](/zh/docs/self-hosting/advanced/auth/clerk-to-betterauth)。
请参阅 [Clerk 迁移指南](/zh/docs/self-hosting/migration/v2/auth/clerk-to-betterauth)。
## 数据库模式变更
@@ -265,7 +265,7 @@ Generally, to fully run the LobeHub database version, you will need at least the
These services can be combined through self-hosting or online cloud services to meet various deployment needs. In this article, we provide a Docker Compose configuration entirely based on open-source self-hosted services, which can be used directly to start the LobeHub database version or modified to suit your requirements.
We use [RustFS](https://github.com/rustfs/rustfs) as the local S3 object storage service by default. To configure SSO authentication services, please refer to the [Authentication Services](/docs/self-hosting/advanced/auth) documentation.
We use [RustFS](https://github.com/rustfs/rustfs) as the local S3 object storage service by default. To configure SSO authentication services, please refer to the [Authentication Services](/docs/self-hosting/auth) documentation.
<Callout type="warning">
If your network topology is complex, please make sure these services can communicate properly
@@ -350,7 +350,7 @@ If `INTERNAL_APP_URL` is not set, it defaults to `APP_URL`.
## Configuring Authentication
To configure SSO authentication services (such as Casdoor, Logto, etc.), please refer to the [Authentication Services](/docs/self-hosting/advanced/auth) documentation.
To configure SSO authentication services (such as Casdoor, Logto, etc.), please refer to the [Authentication Services](/docs/self-hosting/auth) documentation.
[docker-pulls-link]: https://hub.docker.com/r/lobehub/lobehub
[docker-pulls-shield]: https://img.shields.io/docker/pulls/lobehub/lobehub?color=45cc11&labelColor=black&style=flat-square
@@ -262,7 +262,7 @@ mv .env.zh-CN.example .env
这些服务可以通过自建或者在线云服务组合搭配,以满足不同层次的部署需求。本文中,我们提供了完全基于开源自建服务的 Docker Compose 配置,你可以直接使用这份配置文件来启动 LobeHub,也可以对之进行修改以适应你的需求。
我们默认使用 [RustFS](https://github.com/rustfs/rustfs) 作为本地 S3 对象存储服务。如需配置 SSO 登录鉴权服务,请参考 [身份验证服务](/zh/docs/self-hosting/advanced/auth) 文档。
我们默认使用 [RustFS](https://github.com/rustfs/rustfs) 作为本地 S3 对象存储服务。如需配置 SSO 登录鉴权服务,请参考 [身份验证服务](/zh/docs/self-hosting/auth) 文档。
<Callout type="warning">
如果你的网络拓扑较为复杂,请先确保在你的网络环境中这些服务能够正常通讯。
@@ -346,7 +346,7 @@ environment:
## 配置身份验证
如需配置 SSO 登录鉴权服务(如 Casdoor、Logto 等),请参考 [身份验证服务](/zh/docs/self-hosting/advanced/auth) 文档。
如需配置 SSO 登录鉴权服务(如 Casdoor、Logto 等),请参考 [身份验证服务](/zh/docs/self-hosting/auth) 文档。
[docker-pulls-link]: https://hub.docker.com/r/lobehub/lobehub
[docker-pulls-shield]: https://img.shields.io/docker/pulls/lobehub/lobehub?color=45cc11&labelColor=black&style=flat-square
+1 -1
View File
@@ -61,7 +61,7 @@ You also need to configure the `JWKS_KEY` environment variable for signing and v
<GenerateJWKSKey />
<Callout type={'info'}>
For advanced features like SSO providers, magic link login, and email verification, see [Authentication Service](/docs/self-hosting/advanced/auth).
For advanced features like SSO providers, magic link login, and email verification, see [Authentication Service](/docs/self-hosting/auth).
</Callout>
## 2. Deploying the database on Dokploy
+1 -1
View File
@@ -62,7 +62,7 @@ S3_ENABLE_PATH_STYLE=
<GenerateJWKSKey />
<Callout type={'info'}>
如需 SSO 登录、魔法链接登录、邮箱验证等高级功能,请参阅 [身份验证服务](/zh/docs/self-hosting/advanced/auth)。
如需 SSO 登录、魔法链接登录、邮箱验证等高级功能,请参阅 [身份验证服务](/zh/docs/self-hosting/auth)。
</Callout>
## 二、在 Dokploy 上部署数据库
+1 -1
View File
@@ -112,7 +112,7 @@ The server-side database needs to be paired with a user authentication service t
With these variables, users can register and login with email and password.
<Callout type={'info'}>
For advanced features like SSO providers, magic link login, and email verification, see [Authentication Service](/docs/self-hosting/advanced/auth).
For advanced features like SSO providers, magic link login, and email verification, see [Authentication Service](/docs/self-hosting/auth).
</Callout>
</Steps>
+1 -1
View File
@@ -112,7 +112,7 @@ tags:
配置这些变量后,用户即可使用邮箱和密码注册登录。
<Callout type={'info'}>
如需 SSO 登录、魔法链接登录、邮箱验证等高级功能,请参阅 [身份验证服务](/zh/docs/self-hosting/advanced/auth)。
如需 SSO 登录、魔法链接登录、邮箱验证等高级功能,请参阅 [身份验证服务](/zh/docs/self-hosting/auth)。
</Callout>
</Steps>
+1 -1
View File
@@ -66,7 +66,7 @@ Here is the process for deploying the LobeHub server database version on Zeabur:
Fill in those variables into your LobeHub service on Zeabur, here is a more detailed guide for [editing environment variables on Zeabur](https://zeabur.com/docs/deploy/variables).
For detailed configuration of Logto, refer to [this document](/docs/self-hosting/advanced/auth/providers/logto).
For detailed configuration of Logto, refer to [this document](/docs/self-hosting/auth/providers/logto).
### Access your LobeHub Instance
+1 -1
View File
@@ -57,7 +57,7 @@ tags:
使用你刚绑定的域名来访问你的 Logto 控制台,创建一个新项目以获得对应的客户端 ID 与密钥,将它们填入你的 LobeHub 服务的变量中。关于如何填入变量,可以参照 [Zeabur 的官方文档](https://zeabur.com/docs/deploy/variables)。
Logto 的详细配置可以参考[这篇文档](/zh/docs/self-hosting/advanced/auth/providers/logto)。
Logto 的详细配置可以参考[这篇文档](/zh/docs/self-hosting/auth/providers/logto)。
### 访问你的 LobeHub
+2
View File
@@ -32,6 +32,8 @@
"chatList.longMessageDetail": "View Details",
"clearCurrentMessages": "Clear current session messages",
"compressedHistory": "Compressed History",
"compression.cancel": "Uncompress",
"compression.cancelConfirm": "Are you sure you want to uncompress? This will restore the original messages.",
"compression.history": "History",
"compression.summary": "Summary",
"confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be retrieved. Please confirm your action.",
+9
View File
@@ -490,6 +490,15 @@
"user.noForkedAgentGroups": "No forked Agent Groups yet",
"user.noForkedAgents": "No forked Agents yet",
"user.publishedAgents": "Created Agents",
"user.publishedGroups": "Created Groups",
"user.searchPlaceholder": "Search by name or description...",
"user.statusFilter.all": "All",
"user.statusFilter.archived": "Archived",
"user.statusFilter.deprecated": "Deprecated",
"user.statusFilter.favorite": "Favorite",
"user.statusFilter.forked": "Forked",
"user.statusFilter.published": "Published",
"user.statusFilter.unpublished": "Under Review",
"user.tabs.favorites": "Favorites",
"user.tabs.forkedAgents": "Forked",
"user.tabs.publishedAgents": "Created",
+2
View File
@@ -32,6 +32,8 @@
"chatList.longMessageDetail": "查看详情",
"clearCurrentMessages": "清空当前会话消息",
"compressedHistory": "压缩历史",
"compression.cancel": "取消压缩",
"compression.cancelConfirm": "确定要取消压缩吗?这将恢复原始消息。",
"compression.history": "历史记录",
"compression.summary": "摘要",
"confirmClearCurrentMessages": "确认清空当前会话消息吗?清空后无法恢复",
+9
View File
@@ -490,6 +490,15 @@
"user.noForkedAgentGroups": "尚无已派生的代理组",
"user.noForkedAgents": "尚无已派生的代理",
"user.publishedAgents": "创作的助理",
"user.publishedGroups": "创作的群组",
"user.searchPlaceholder": "搜索名称或描述...",
"user.statusFilter.all": "全部",
"user.statusFilter.archived": "已归档",
"user.statusFilter.deprecated": "已废弃",
"user.statusFilter.favorite": "已收藏",
"user.statusFilter.forked": "已派生",
"user.statusFilter.published": "已发布",
"user.statusFilter.unpublished": "审核中",
"user.tabs.favorites": "收藏",
"user.tabs.forkedAgents": "已派生",
"user.tabs.publishedAgents": "创作",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lobehub/lobehub",
"version": "2.1.6",
"version": "2.1.15",
"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.",
"keywords": [
"framework",
@@ -10,6 +10,26 @@ export const systemPrompt = `You have access to the Notebook tool for creating a
Note: The list of existing documents is automatically provided in the context, so you don't need to query for it.
</tool_overview>
<api_parameters>
**createDocument** - All three parameters are required:
- title (required): A descriptive title for the document
- description (required): A brief summary of the document (1-2 sentences), shown in document lists
- content (required): The document content in Markdown format
- type (optional): "markdown" (default), "note", "report", or "article"
**updateDocument**:
- id (required): The document ID to update
- title (optional): New title
- content (optional): New content
- append (optional): If true, append to existing content instead of replacing
**getDocument**:
- id (required): The document ID to retrieve
**deleteDocument**:
- id (required): The document ID to delete
</api_parameters>
<when_to_use>
**Save to Notebook when**:
- User explicitly asks to "save", "write down", or "document" something
@@ -0,0 +1 @@
CREATE INDEX IF NOT EXISTS "messages_message_group_id_idx" ON "messages" USING btree ("message_group_id");
File diff suppressed because it is too large Load Diff
@@ -532,7 +532,14 @@
"when": 1769362978088,
"tag": "0075_add_user_memory_persona",
"breakpoints": true
},
{
"idx": 76,
"version": "7",
"when": 1770179814971,
"tag": "0076_add_message_group_index",
"breakpoints": true
}
],
"version": "6"
}
}
+1
View File
@@ -149,6 +149,7 @@ export const messages = pgTable(
index('messages_thread_id_idx').on(table.threadId),
index('messages_agent_id_idx').on(table.agentId),
index('messages_group_id_idx').on(table.groupId),
index('messages_message_group_id_idx').on(table.messageGroupId),
],
);
@@ -62,6 +62,7 @@ export interface DiscoverAssistantItem extends Omit<LobeAgentSettings, 'meta'>,
status?: AgentStatus;
tokenUsage: number;
type?: AgentType;
updatedAt?: string;
userName?: string;
}
+8
View File
@@ -67,6 +67,14 @@ export interface DiscoverUserInfo {
export interface DiscoverUserProfile {
agentGroups?: DiscoverGroupAgentItem[];
agents: DiscoverAssistantItem[];
/**
* Agent groups favorited by the user
*/
favoriteAgentGroups?: DiscoverGroupAgentItem[];
/**
* Agents favorited by the user
*/
favoriteAgents?: DiscoverAssistantItem[];
/**
* Agent groups forked by the user
*/
@@ -202,7 +202,7 @@ export const handleSingle = async (
* to avoid breaking structured XML/JSON-like content mid-way.
*
* Bisection example (8 segments, keep newest):
* try 4 (fits?) yes try 6 no try 5 yes best=5
* try 4 (fits?) -> yes -> try 6 -> no -> try 5 -> yes => best=5
* if compact retry needed, repeat with build(true) and pick the better fit.
*
* This minimizes structural breakage by preferring whole built segments and only truncating the last one as a last resort.
+6 -6
View File
@@ -2,9 +2,9 @@ import { Pricing, PricingUnit, PricingUnitName } from 'model-bank';
/**
* Internal helper to extract the displayed unit rate from a pricing unit by strategy
* - fixed rate
* - tiered tiers[0].rate
* - lookup first price value
* - fixed: rate
* - tiered: tiers[0].rate
* - lookup: first price value
*/
const getRateFromUnit = (unit: PricingUnit): number | undefined => {
switch (unit.strategy) {
@@ -39,9 +39,9 @@ export const getUnitRateByName = (
/**
* Get text input unit rate from pricing
* - fixed rate
* - tiered tiers[0].rate
* - lookup Object.values(lookup.prices)[0]
* - fixed: rate
* - tiered: tiers[0].rate
* - lookup: Object.values(lookup.prices)[0]
*/
export function getTextInputUnitRate(pricing?: Pricing): number | undefined {
return getUnitRateByName(pricing, 'textInput');
+27 -11
View File
@@ -5,7 +5,7 @@
* IMPORTANT: Keep this file as CommonJS (.js) for compatibility with startServer.js
*/
const MIGRATION_DOC_BASE = 'https://lobehub.com/docs/self-hosting/advanced/auth';
const MIGRATION_DOC_BASE = 'https://lobehub.com/docs/self-hosting/migration/v2/auth';
/**
* Deprecated environment variable checks configuration
@@ -86,10 +86,10 @@ const DEPRECATED_CHECKS = [
const mapping = {
AUTH_AZURE_AD_ID: 'AUTH_MICROSOFT_ID',
AUTH_AZURE_AD_SECRET: 'AUTH_MICROSOFT_SECRET',
AUTH_AZURE_AD_TENANT_ID: 'No longer needed',
AUTH_AZURE_AD_TENANT_ID: 'AUTH_MICROSOFT_TENANT_ID',
AZURE_AD_CLIENT_ID: 'AUTH_MICROSOFT_ID',
AZURE_AD_CLIENT_SECRET: 'AUTH_MICROSOFT_SECRET',
AZURE_AD_TENANT_ID: 'No longer needed',
AZURE_AD_TENANT_ID: 'AUTH_MICROSOFT_TENANT_ID',
};
return `${envVar}${mapping[envVar]}`;
},
@@ -167,10 +167,10 @@ const DEPRECATED_CHECKS = [
docUrl: `${MIGRATION_DOC_BASE}/nextauth-to-betterauth`,
formatVar: (envVar) => {
const mapping = {
AUTH_MICROSOFT_ENTRA_ID_BASE_URL: 'No longer needed',
AUTH_MICROSOFT_ENTRA_ID_BASE_URL: 'AUTH_MICROSOFT_AUTHORITY_URL',
AUTH_MICROSOFT_ENTRA_ID_ID: 'AUTH_MICROSOFT_ID',
AUTH_MICROSOFT_ENTRA_ID_SECRET: 'AUTH_MICROSOFT_SECRET',
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID: 'No longer needed',
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID: 'AUTH_MICROSOFT_TENANT_ID',
};
return `${envVar}${mapping[envVar]}`;
},
@@ -213,7 +213,11 @@ function printIssueBlock(name, vars, message, docUrl, formatVar, severity = 'err
log(`\n${icon} ${name}`);
log('─'.repeat(50));
log(isWarning ? 'Missing recommended environment variables:' : 'Detected deprecated environment variables:');
log(
isWarning
? 'Missing recommended environment variables:'
: 'Detected deprecated environment variables:',
);
for (const envVar of vars) {
log(`${formatVar ? formatVar(envVar) : envVar}`);
}
@@ -253,7 +257,14 @@ function checkDeprecatedAuth(options = {}) {
console.warn('═'.repeat(70));
for (const issue of warnings) {
printIssueBlock(issue.name, issue.foundVars, issue.message, issue.docUrl, issue.formatVar, 'warning');
printIssueBlock(
issue.name,
issue.foundVars,
issue.message,
issue.docUrl,
issue.formatVar,
'warning',
);
}
console.warn('\n' + '═'.repeat(70));
@@ -264,13 +275,18 @@ function checkDeprecatedAuth(options = {}) {
// Print errors and exit (blocking)
if (errors.length > 0) {
console.error('\n' + '═'.repeat(70));
console.error(
`❌ ERROR: Found ${errors.length} deprecated environment variable issue(s)!`,
);
console.error(`❌ ERROR: Found ${errors.length} deprecated environment variable issue(s)!`);
console.error('═'.repeat(70));
for (const issue of errors) {
printIssueBlock(issue.name, issue.foundVars, issue.message, issue.docUrl, issue.formatVar, 'error');
printIssueBlock(
issue.name,
issue.foundVars,
issue.message,
issue.docUrl,
issue.formatVar,
'error',
);
}
console.error('\n' + '═'.repeat(70));
@@ -0,0 +1,14 @@
import { redirect } from 'next/navigation';
import { type PropsWithChildren } from 'react';
import { authEnv } from '@/envs/auth';
const ResetPasswordLayout = ({ children }: PropsWithChildren) => {
if (authEnv.AUTH_DISABLE_EMAIL_PASSWORD) {
redirect('/signin');
}
return children;
};
export default ResetPasswordLayout;
@@ -33,7 +33,7 @@ const Nav = memo(() => {
const switchTopic = useChatStore((s) => s.switchTopic);
const [openNewTopicOrSaveTopic] = useChatStore((s) => [s.openNewTopicOrSaveTopic]);
const { mutate, isValidating } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
const { mutate } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
const handleNewTopic = () => {
// If in agent sub-route, navigate back to agent chat first
if (isProfileActive && agentId) {
@@ -46,7 +46,6 @@ const Nav = memo(() => {
<Flexbox gap={1} paddingInline={4}>
<NavItem
icon={MessageSquarePlusIcon}
loading={isValidating}
onClick={handleNewTopic}
title={tTopic('actions.addNewTopic')}
/>
@@ -75,7 +75,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
// Fetch favorite status
const { data: favoriteStatus, mutate: mutateFavorite } = useSWR(
identifier && isAuthenticated ? ['favorite-status', 'agent', identifier] : null,
() => socialService.checkFavoriteStatus('agent', identifier!),
() => socialService.checkFavoriteStatus('agent-group', identifier!),
{ revalidateOnFocus: false },
);
@@ -100,10 +100,10 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
setFavoriteLoading(true);
try {
if (isFavorited) {
await socialService.removeFavorite('agent', identifier);
await socialService.removeFavorite('agent-group', identifier);
message.success(t('assistant.unfavoriteSuccess'));
} else {
await socialService.addFavorite('agent', identifier);
await socialService.addFavorite('agent-group', identifier);
message.success(t('assistant.favoriteSuccess'));
}
await mutateFavorite();
@@ -13,6 +13,8 @@ export interface UserDetailContextConfig {
agentCount: number;
agentGroups?: DiscoverGroupAgentItem[];
agents: DiscoverAssistantItem[];
favoriteAgentGroups?: DiscoverGroupAgentItem[];
favoriteAgents?: DiscoverAssistantItem[];
forkedAgentGroups?: DiscoverGroupAgentItem[];
forkedAgents?: DiscoverAssistantItem[];
groupCount: number;
@@ -0,0 +1,36 @@
'use client';
import { Select } from 'antd';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
export type StatusFilterValue = 'published' | 'unpublished' | 'deprecated' | 'archived' | 'forked' | 'favorite';
interface StatusFilterProps {
onChange: (value: StatusFilterValue) => void;
value: StatusFilterValue;
}
const StatusFilter = memo<StatusFilterProps>(({ value, onChange }) => {
const { t } = useTranslation('discover');
const options = [
{ label: t('user.statusFilter.published'), value: 'published' as const },
{ label: t('user.statusFilter.unpublished'), value: 'unpublished' as const },
{ label: t('user.statusFilter.deprecated'), value: 'deprecated' as const },
{ label: t('user.statusFilter.archived'), value: 'archived' as const },
{ label: t('user.statusFilter.forked'), value: 'forked' as const },
{ label: t('user.statusFilter.favorite'), value: 'favorite' as const },
];
return (
<Select
onChange={onChange}
options={options}
style={{ minWidth: 120 }}
value={value}
/>
);
});
export default StatusFilter;
@@ -1,12 +1,13 @@
'use client';
import { Flexbox, Grid, Tag, Text } from '@lobehub/ui';
import { Pagination } from 'antd';
import { Input, Pagination } from 'antd';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import AssistantEmpty from '../../../features/AssistantEmpty';
import { useUserDetailContext } from './DetailProvider';
import StatusFilter, { type StatusFilterValue } from './StatusFilter';
import UserAgentCard from './UserAgentCard';
interface UserAgentListProps {
@@ -14,27 +15,82 @@ interface UserAgentListProps {
rows?: number;
}
const UserAgentList = memo<UserAgentListProps>(({ rows = 4, pageSize = 10 }) => {
const UserAgentList = memo<UserAgentListProps>(({ rows = 4, pageSize = 8 }) => {
const { t } = useTranslation('discover');
const { agents, agentCount } = useUserDetailContext();
const { agents, agentCount, forkedAgents = [], favoriteAgents = [], isOwner } = useUserDetailContext();
const [currentPage, setCurrentPage] = useState(1);
const [statusFilter, setStatusFilter] = useState<StatusFilterValue>('published');
const [searchQuery, setSearchQuery] = useState('');
// Combine agents and forked agents, then filter based on status and search
const filteredAgents = useMemo(() => {
let allAgents = [...agents];
if (statusFilter === 'forked') {
// Show only forked agents (those with forkedFromAgentId)
allAgents = forkedAgents;
} else if (statusFilter === 'favorite') {
// Show only favorited agents
allAgents = favoriteAgents;
} else {
// Filter by status for non-forked agents
allAgents = allAgents.filter((agent) => {
return agent.status === statusFilter;
});
}
// Apply search filter
if (searchQuery.trim()) {
const query = searchQuery.toLowerCase();
allAgents = allAgents.filter((agent) => {
console.log('agent', agent);
const name = agent?.title?.toLowerCase() || '';
const description = agent?.description?.toLowerCase() || '';
return name.includes(query) || description.includes(query);
});
}
return allAgents;
}, [agents, forkedAgents, statusFilter, searchQuery]);
const paginatedAgents = useMemo(() => {
const startIndex = (currentPage - 1) * pageSize;
return agents.slice(startIndex, startIndex + pageSize);
}, [agents, currentPage, pageSize]);
return filteredAgents.slice(startIndex, startIndex + pageSize);
}, [filteredAgents, currentPage, pageSize]);
if (agents.length === 0) return <AssistantEmpty />;
// Reset to page 1 when filter or search changes
useMemo(() => {
setCurrentPage(1);
}, [statusFilter, searchQuery]);
const showPagination = agents.length > pageSize;
if (agents.length === 0 && forkedAgents.length === 0) return <AssistantEmpty />;
const showPagination = filteredAgents.length > pageSize;
return (
<Flexbox gap={16}>
<Flexbox align={'center'} gap={8} horizontal>
<Text fontSize={16} weight={500}>
{t('user.publishedAgents')}
</Text>
{agentCount > 0 && <Tag>{agentCount}</Tag>}
<Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
<Flexbox align={'center'} gap={8} horizontal>
<Text fontSize={16} weight={500}>
{t('user.publishedAgents')}
</Text>
{agentCount > 0 && <Tag>{filteredAgents.length}</Tag>}
</Flexbox>
{isOwner && (
<Flexbox align={'center'} gap={8} horizontal>
<Input.Search
allowClear
onChange={(e) => setSearchQuery(e.target.value)}
placeholder={t('user.searchPlaceholder')}
style={{ width: 200 }}
value={searchQuery}
/>
<StatusFilter
onChange={(value) => setStatusFilter(value)}
value={statusFilter}
/>
</Flexbox>
)}
</Flexbox>
<Grid rows={rows} width={'100%'}>
{paginatedAgents.map((item, index) => (
@@ -48,7 +104,7 @@ const UserAgentList = memo<UserAgentListProps>(({ rows = 4, pageSize = 10 }) =>
onChange={(page) => setCurrentPage(page)}
pageSize={pageSize}
showSizeChanger={false}
total={agents.length}
total={filteredAgents.length}
/>
</Flexbox>
)}
@@ -3,25 +3,14 @@
import { Flexbox } from '@lobehub/ui';
import { memo } from 'react';
import { useUserDetailContext } from './DetailProvider';
import UserAgentList from './UserAgentList';
import UserFavoriteAgents from './UserFavoriteAgents';
import UserFavoritePlugins from './UserFavoritePlugins';
import UserForkedAgentGroups from './UserForkedAgentGroups';
import UserForkedAgents from './UserForkedAgents';
import UserGroupList from './UserGroupList';
const UserContent = memo(() => {
const { forkedAgents, forkedAgentGroups } = useUserDetailContext();
return (
<Flexbox gap={32}>
<UserAgentList />
<UserGroupList />
<UserForkedAgents agents={forkedAgents} />
<UserForkedAgentGroups agentGroups={forkedAgentGroups} />
<UserFavoriteAgents />
<UserFavoritePlugins />
</Flexbox>
);
});
@@ -1,11 +1,12 @@
'use client';
import { Flexbox, Grid, Tag, Text } from '@lobehub/ui';
import { Pagination } from 'antd';
import { Input, Pagination } from 'antd';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useUserDetailContext } from './DetailProvider';
import StatusFilter, { type StatusFilterValue } from './StatusFilter';
import UserGroupCard from './UserGroupCard';
interface UserGroupListProps {
@@ -13,28 +14,81 @@ interface UserGroupListProps {
rows?: number;
}
const UserGroupList = memo<UserGroupListProps>(({ rows = 4, pageSize = 10 }) => {
const UserGroupList = memo<UserGroupListProps>(({ rows = 4, pageSize = 8 }) => {
const { t } = useTranslation('discover');
const { agentGroups, groupCount } = useUserDetailContext();
const { agentGroups = [], groupCount, forkedAgentGroups = [], favoriteAgentGroups = [], isOwner } = useUserDetailContext();
const [currentPage, setCurrentPage] = useState(1);
const [statusFilter, setStatusFilter] = useState<StatusFilterValue>('published');
const [searchQuery, setSearchQuery] = useState('');
// Combine groups and forked groups, then filter based on status and search
const filteredGroups = useMemo(() => {
let allGroups = [...agentGroups];
if (statusFilter === 'forked') {
// Show only forked groups (those with forkedFromAgentId)
allGroups = forkedAgentGroups;
} else if (statusFilter === 'favorite') {
// Show only favorited groups
allGroups = favoriteAgentGroups;
} else {
// Filter by status for non-forked groups
allGroups = allGroups.filter((group) => {
return group.status === statusFilter;
});
}
// Apply search filter
if (searchQuery.trim()) {
const query = searchQuery.toLowerCase();
allGroups = allGroups.filter((group) => {
const name = group?.title?.toLowerCase() || '';
const description = group?.description?.toLowerCase() || '';
return name.includes(query) || description.includes(query);
});
}
return allGroups;
}, [agentGroups, forkedAgentGroups, statusFilter, searchQuery]);
const paginatedGroups = useMemo(() => {
if (!agentGroups) return [];
const startIndex = (currentPage - 1) * pageSize;
return agentGroups.slice(startIndex, startIndex + pageSize);
}, [agentGroups, currentPage, pageSize]);
return filteredGroups.slice(startIndex, startIndex + pageSize);
}, [filteredGroups, currentPage, pageSize]);
if (!agentGroups || agentGroups.length === 0) return null;
// Reset to page 1 when filter or search changes
useMemo(() => {
setCurrentPage(1);
}, [statusFilter, searchQuery]);
const showPagination = agentGroups.length > pageSize;
if (agentGroups.length === 0 && forkedAgentGroups.length === 0) return null;
const showPagination = filteredGroups.length > pageSize;
return (
<Flexbox gap={16}>
<Flexbox align={'center'} gap={8} horizontal>
<Text fontSize={16} weight={500}>
{t('user.publishedGroups', { defaultValue: '创作的群组' })}
</Text>
{groupCount > 0 && <Tag>{groupCount}</Tag>}
<Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
<Flexbox align={'center'} gap={8} horizontal>
<Text fontSize={16} weight={500}>
{t('user.publishedGroups', { defaultValue: '创作的群组' })}
</Text>
{groupCount > 0 && <Tag>{filteredGroups.length}</Tag>}
</Flexbox>
{isOwner && (
<Flexbox align={'center'} gap={8} horizontal>
<Input.Search
allowClear
onChange={(e) => setSearchQuery(e.target.value)}
placeholder={t('user.searchPlaceholder')}
style={{ width: 200 }}
value={searchQuery}
/>
<StatusFilter
onChange={(value) => setStatusFilter(value)}
value={statusFilter}
/>
</Flexbox>
)}
</Flexbox>
<Grid rows={rows} width={'100%'}>
{paginatedGroups.map((item, index) => (
@@ -48,7 +102,7 @@ const UserGroupList = memo<UserGroupListProps>(({ rows = 4, pageSize = 10 }) =>
onChange={(page) => setCurrentPage(page)}
pageSize={pageSize}
showSizeChanger={false}
total={agentGroups.length}
total={filteredGroups.length}
/>
</Flexbox>
)}
@@ -61,12 +61,14 @@ const UserDetailPage = memo<UserDetailPageProps>(({ mobile }) => {
const contextConfig = useMemo(() => {
if (!data || !data.user) return null;
const { user, agents, agentGroups, forkedAgents, forkedAgentGroups } = data;
const { user, agents, agentGroups, forkedAgents, forkedAgentGroups, favoriteAgents, favoriteAgentGroups } = data;
const totalInstalls = agents.reduce((sum, agent) => sum + (agent.installCount || 0), 0);
return {
agentCount: agents.length,
agentGroups: agentGroups || [],
agents,
favoriteAgentGroups: favoriteAgentGroups || [],
favoriteAgents: favoriteAgents || [],
forkedAgentGroups: forkedAgentGroups || [],
forkedAgents: forkedAgents || [],
groupCount: agentGroups?.length || 0,
@@ -56,6 +56,7 @@ const styles = createStaticStyles(({ css, cssVar }) => {
const AssistantItem = memo<DiscoverAssistantItem>(
({
createdAt,
updatedAt,
author,
avatar,
title,
@@ -225,7 +226,7 @@ const AssistantItem = memo<DiscoverAssistantItem>(
<Icon icon={ClockIcon} size={14} />
<PublishedTime
className={styles.secondaryDesc}
date={createdAt}
date={updatedAt || createdAt}
template={'MMM DD, YYYY'}
/>
</Flexbox>
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
import { modal, notification } from '@/components/AntdStaticMethods';
import AuthIcons from '@/components/AuthIcons';
import { isBuiltinProvider, normalizeProviderId } from '@/libs/better-auth/utils/client';
import { useServerConfigStore } from '@/store/serverConfig';
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
import { useUserStore } from '@/store/user';
@@ -33,8 +34,11 @@ export const SSOProvidersList = memo(() => {
}, [providers]);
// Get available providers for linking (filter out already linked)
// Normalize provider IDs when comparing to handle aliases (e.g. microsoft-entra-id → microsoft)
const availableProviders = useMemo(() => {
return (oAuthSSOProviders || []).filter((provider) => !linkedProviderIds.has(provider));
return (oAuthSSOProviders || []).filter(
(provider) => !linkedProviderIds.has(normalizeProviderId(provider)),
);
}, [oAuthSSOProviders, linkedProviderIds]);
const handleUnlinkSSO = async (provider: string) => {
@@ -63,14 +67,24 @@ export const SSOProvidersList = memo(() => {
};
const handleLinkSSO = async (provider: string) => {
if (enableAuthActions) {
// Use better-auth native linkSocial API
const { linkSocial } = await import('@/libs/better-auth/auth-client');
if (!enableAuthActions) return;
const normalizedProvider = normalizeProviderId(provider);
const { linkSocial, oauth2 } = await import('@/libs/better-auth/auth-client');
if (isBuiltinProvider(normalizedProvider)) {
// Use better-auth native linkSocial API for built-in providers
await linkSocial({
callbackURL: '/profile',
provider: provider as any,
provider: normalizedProvider as any,
});
return;
}
await oauth2.link({
callbackURL: '/profile',
providerId: normalizedProvider,
});
};
// Dropdown menu items for linking new providers
@@ -59,6 +59,7 @@ const ProfileSetting = ({ mobile }: ProfileSettingProps) => {
const isLoadedAuthProviders = useUserStore(authSelectors.isLoadedAuthProviders);
const fetchAuthProviders = useUserStore((s) => s.fetchAuthProviders);
const enableKlavis = useServerConfigStore(serverConfigSelectors.enableKlavis);
const disableEmailPassword = useServerConfigStore(serverConfigSelectors.disableEmailPassword);
const [servers, isServersInit, useFetchUserKlavisServers] = useToolStore((s) => [
s.servers,
s.isServersInit,
@@ -113,7 +114,7 @@ const ProfileSetting = ({ mobile }: ProfileSettingProps) => {
<InterestsRow mobile={mobile} />
{/* Password Row - For logged in users to change or set password */}
{!isDesktop && isLogin && (
{!isDesktop && isLogin && !disableEmailPassword && (
<>
<Divider style={{ margin: 0 }} />
<PasswordRow mobile={mobile} />
+6
View File
@@ -33,8 +33,10 @@ declare global {
AUTH_COGNITO_REGION?: string;
AUTH_COGNITO_USERPOOL_ID?: string;
AUTH_MICROSOFT_AUTHORITY_URL?: string;
AUTH_MICROSOFT_ID?: string;
AUTH_MICROSOFT_SECRET?: string;
AUTH_MICROSOFT_TENANT_ID?: string;
AUTH_AUTH0_ID?: string;
AUTH_AUTH0_SECRET?: string;
@@ -132,8 +134,10 @@ export const getAuthConfig = () => {
AUTH_COGNITO_REGION: z.string().optional(),
AUTH_COGNITO_USERPOOL_ID: z.string().optional(),
AUTH_MICROSOFT_AUTHORITY_URL: z.string().optional(),
AUTH_MICROSOFT_ID: z.string().optional(),
AUTH_MICROSOFT_SECRET: z.string().optional(),
AUTH_MICROSOFT_TENANT_ID: z.string().optional(),
AUTH_AUTH0_ID: z.string().optional(),
AUTH_AUTH0_SECRET: z.string().optional(),
@@ -219,8 +223,10 @@ export const getAuthConfig = () => {
AUTH_GITHUB_ID: process.env.AUTH_GITHUB_ID,
AUTH_GITHUB_SECRET: process.env.AUTH_GITHUB_SECRET,
AUTH_MICROSOFT_AUTHORITY_URL: process.env.AUTH_MICROSOFT_AUTHORITY_URL,
AUTH_MICROSOFT_ID: process.env.AUTH_MICROSOFT_ID,
AUTH_MICROSOFT_SECRET: process.env.AUTH_MICROSOFT_SECRET,
AUTH_MICROSOFT_TENANT_ID: process.env.AUTH_MICROSOFT_TENANT_ID,
AUTH_COGNITO_ID: process.env.AUTH_COGNITO_ID,
AUTH_COGNITO_SECRET: process.env.AUTH_COGNITO_SECRET,
@@ -20,10 +20,12 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
`,
paramKey: css`
font-family: ${cssVar.fontFamilyCode};
font-size: 12px;
color: ${cssVar.colorTextTertiary};
`,
paramValue: css`
font-family: ${cssVar.fontFamilyCode};
font-size: 12px;
color: ${cssVar.colorTextSecondary};
`,
root: css`
@@ -10,9 +10,10 @@ import {
Tabs,
type TabsProps,
} from '@lobehub/ui';
import { App } from 'antd';
import { createStaticStyles, cx } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { ChevronDown, ChevronUp, History, Sparkles } from 'lucide-react';
import { ChevronDown, ChevronUp, History, Sparkles, Undo2 } from 'lucide-react';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -66,6 +67,7 @@ export interface CompressedGroupMessageProps {
const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
const { t } = useTranslation('chat');
const { modal } = App.useApp();
const [activeTab, setActiveTab] = useState<string>(() => getStoredTab(id));
const handleTabChange = useCallback(
@@ -80,6 +82,16 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
const toggleCompressedGroupExpanded = useConversationStore(
(s) => s.toggleCompressedGroupExpanded,
);
const cancelCompression = useConversationStore((s) => s.cancelCompression);
const handleCancelCompression = useCallback(() => {
modal.confirm({
centered: true,
content: t('compression.cancelConfirm'),
onOk: () => cancelCompression(id),
title: t('compression.cancel'),
});
}, [id, cancelCompression, modal, t]);
const content = message?.content;
const rawCompressedMessages = (message as UIChatMessage)?.compressedMessages;
@@ -145,11 +157,19 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
onChange={handleTabChange}
variant={'rounded'}
/>
<ActionIcon
icon={expanded ? ChevronUp : ChevronDown}
onClick={() => toggleCompressedGroupExpanded(id)}
size={'small'}
/>
<Flexbox gap={4} horizontal>
<ActionIcon
icon={Undo2}
onClick={handleCancelCompression}
size={'small'}
title={t('compression.cancel')}
/>
<ActionIcon
icon={expanded ? ChevronUp : ChevronDown}
onClick={() => toggleCompressedGroupExpanded(id)}
size={'small'}
/>
</Flexbox>
</Flexbox>
)}
{!showContent ? null : activeTab === 'summary' ? (
@@ -327,11 +327,37 @@ const TaskMessages = memo<TaskMessagesProps>(
const assistantGroupMessage = messages.find((item) => item.role === 'assistantGroup');
const userMessage = messages.find((item) => item.role === 'user');
return {
assistantId: assistantGroupMessage?.id ?? '',
blocks: assistantGroupMessage?.children ?? [],
instruction: userMessage?.content,
};
// If assistantGroup exists, use its children as blocks
if (assistantGroupMessage) {
return {
assistantId: assistantGroupMessage.id ?? '',
blocks: assistantGroupMessage.children ?? [],
instruction: userMessage?.content,
};
}
// Fallback: support plain assistant message (without tools)
// This handles cases where SubAgent returns a simple text response
const assistantMessage = messages.find((item) => item.role === 'assistant');
if (assistantMessage) {
// Convert plain assistant message to block format
const block: AssistantContentBlock = {
content: assistantMessage.content || '',
id: assistantMessage.id,
};
// Copy optional fields if they exist
if (assistantMessage.error) block.error = assistantMessage.error;
if (assistantMessage.reasoning) block.reasoning = assistantMessage.reasoning;
return {
assistantId: assistantMessage.id ?? '',
blocks: [block],
instruction: userMessage?.content,
};
}
return { assistantId: '', blocks: [], instruction: undefined };
}, [messages]);
// Calculate total tool calls
@@ -13,6 +13,11 @@ import { dataSelectors } from '../../data/selectors';
* Handles message state operations like loading, collapsed, etc.
*/
export interface MessageStateAction {
/**
* Cancel compression and restore original messages
*/
cancelCompression: (id: string) => Promise<void>;
/**
* Copy message content to clipboard
*/
@@ -50,6 +55,26 @@ export const messageStateSlice: StateCreator<
[],
MessageStateAction
> = (set, get) => ({
cancelCompression: async (id) => {
const message = dataSelectors.getDisplayMessageById(id)(get());
if (!message || message.role !== 'compressedGroup') return;
const { context, replaceMessages } = get();
if (!context.agentId || !context.topicId) return;
// Call service to cancel compression
const { messages } = await messageService.cancelCompression({
agentId: context.agentId,
groupId: context.groupId,
messageGroupId: id,
threadId: context.threadId,
topicId: context.topicId,
});
// Replace messages with restored original messages
replaceMessages(messages);
},
copyMessage: async (id, content) => {
const { hooks } = get();
+1 -1
View File
@@ -12,7 +12,7 @@ export const useFetchAgentList = () => {
const { isValidating, data } = useFetchAgentListHook(isLogin);
// isRevalidating: 有缓存数据,后台正在更新
// isRevalidating: has cached data, updating in background
return {
isRevalidating: isValidating && !!data,
};
+1 -1
View File
@@ -49,7 +49,7 @@ export const useClearCurrentMessagesHotkey = () => {
});
};
// 注册聚合
// Register aggregate
export const useRegisterChatHotkeys = () => {
const { enableScope, disableScope } = useHotkeysContext();
+2 -2
View File
@@ -7,7 +7,7 @@ import { useGlobalStore } from '@/store/global';
import { useHotkeyById } from './useHotkeyById';
// 切换到会话标签(并聚焦到Lobe AI)
// Switch to chat tab (and focus on Lobe AI)
export const useNavigateToChatHotkey = () => {
const navigateToAgent = useNavigateToAgent();
const [, { unpinAgent }] = usePinnedAgentState();
@@ -58,7 +58,7 @@ export const useCommandPaletteHotkey = () => {
};
export const useRegisterGlobalHotkeys = () => {
// 全局自动注册不需要 enableScope
// Global auto-registration doesn't need enableScope
useToggleLeftPanelHotkey();
useToggleRightPanelHotkey();
useNavigateToChatHotkey();
+1 -1
View File
@@ -19,7 +19,7 @@ export const useToggleImageRightPanelHotkey = () => {
);
};
// 注册聚合
// Register aggregate
export const useRegisterImageHotkeys = () => {
const { enableScope, disableScope } = useHotkeysContext();
+1 -1
View File
@@ -19,7 +19,7 @@ export const usePWAInstall = () => {
}, []);
const installCheck = () => {
// 当在 PWA 或不支持 PWA 的环境中时,不显示安装按钮
// Don't show install button when in PWA or environment that doesn't support PWA
if (isPWA || !isSupportInstallPWA) return false;
const pwa: any = document.querySelector(`#${PWA_INSTALL_ID}`);
if (!pwa) return false;
+1
View File
@@ -10,6 +10,7 @@ import type { auth } from '@/auth';
export const {
linkSocial,
oauth2,
accountInfo,
listAccounts,
requestPasswordReset,
+1
View File
@@ -107,6 +107,7 @@ export function defineConfig(customOptions: CustomBetterAuthOptions) {
emailAndPassword: {
autoSignIn: true,
disableSignUp: authEnv.AUTH_DISABLE_EMAIL_PASSWORD,
enabled: !authEnv.AUTH_DISABLE_EMAIL_PASSWORD,
maxPasswordLength: 64,
minPasswordLength: 8,
@@ -3,23 +3,30 @@ import { authEnv } from '@/envs/auth';
import type { BuiltinProviderDefinition } from '../types';
type MicrosoftEnv = {
AUTH_MICROSOFT_ID?: string;
AUTH_MICROSOFT_SECRET?: string;
AUTH_MICROSOFT_AUTHORITY_URL?: string;
AUTH_MICROSOFT_ID: string;
AUTH_MICROSOFT_SECRET: string;
AUTH_MICROSOFT_TENANT_ID?: string;
};
const provider: BuiltinProviderDefinition<MicrosoftEnv, 'microsoft'> = {
aliases: ['microsoft-entra-id'],
build: (env) => ({
clientId: env.AUTH_MICROSOFT_ID!,
clientSecret: env.AUTH_MICROSOFT_SECRET!,
authority: env.AUTH_MICROSOFT_AUTHORITY_URL,
clientId: env.AUTH_MICROSOFT_ID,
clientSecret: env.AUTH_MICROSOFT_SECRET,
tenantId: env.AUTH_MICROSOFT_TENANT_ID,
}),
checkEnvs: () => {
const clientId = authEnv.AUTH_MICROSOFT_ID;
const clientSecret = authEnv.AUTH_MICROSOFT_SECRET;
const tenantId = authEnv.AUTH_MICROSOFT_TENANT_ID;
return !!(clientId && clientSecret)
? {
AUTH_MICROSOFT_AUTHORITY_URL: authEnv.AUTH_MICROSOFT_AUTHORITY_URL,
AUTH_MICROSOFT_ID: clientId,
AUTH_MICROSOFT_SECRET: clientSecret,
AUTH_MICROSOFT_TENANT_ID: tenantId,
}
: false;
},
+3
View File
@@ -41,6 +41,9 @@ export default {
'chatList.longMessageDetail': 'View Details',
'clearCurrentMessages': 'Clear current session messages',
'compressedHistory': 'Compressed History',
'compression.cancel': 'Uncompress',
'compression.cancelConfirm':
'Are you sure you want to uncompress? This will restore the original messages.',
'compression.history': 'History',
'compression.summary': 'Summary',
'confirmClearCurrentMessages':
+12
View File
@@ -891,6 +891,18 @@ export default {
'user.publishedAgents': 'Created Agents',
'user.publishedGroups': 'Created Groups',
'user.searchPlaceholder': 'Search by name or description...',
'user.statusFilter.all': 'All',
'user.statusFilter.archived': 'Archived',
'user.statusFilter.deprecated': 'Deprecated',
'user.statusFilter.favorite': 'Favorite',
'user.statusFilter.forked': 'Forked',
'user.statusFilter.published': 'Published',
'user.statusFilter.unpublished': 'Under Review',
'user.tabs.favorites': 'Favorites',
'user.tabs.forkedAgents': 'Forked',
+1 -1
View File
@@ -17,7 +17,7 @@ const socialPublicProcedure = publicProcedure
.use(marketSDK);
// Schema definitions
const targetTypeSchema = z.enum(['agent', 'plugin']);
const targetTypeSchema = z.enum(['agent', 'plugin', 'agent-group']);
const paginationSchema = z.object({
limit: z.number().optional(),
+37 -6
View File
@@ -48,7 +48,32 @@ export const messageRouter = router({
return ctx.messageService.addFilesToMessage(id, fileIds, resolved);
}),
count: messageProcedure
/**
* Cancel compression by deleting the compression group and restoring original messages
*/
cancelCompression: messageProcedure
.input(
z.object({
agentId: z.string(),
groupId: z.string().nullable().optional(),
messageGroupId: z.string(),
threadId: z.string().nullable().optional(),
topicId: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
const { messageGroupId, agentId, groupId, threadId, topicId } = input;
return ctx.messageService.cancelCompression(messageGroupId, {
agentId,
groupId,
threadId,
topicId,
});
}),
count: messageProcedure
.input(
z
.object({
@@ -62,7 +87,9 @@ export const messageRouter = router({
return ctx.messageModel.count(input);
}),
countWords: messageProcedure
countWords: messageProcedure
.input(
z
.object({
@@ -76,12 +103,13 @@ export const messageRouter = router({
return ctx.messageModel.countWords(input);
}),
/**
/**
* Create a compression group for old messages
* Creates a placeholder group, marks messages as compressed
* Returns messages to summarize for frontend AI generation
*/
createCompressionGroup: messageProcedure
createCompressionGroup: messageProcedure
.input(
z.object({
agentId: z.string(),
@@ -102,7 +130,9 @@ export const messageRouter = router({
});
}),
createMessage: messageProcedure
createMessage: messageProcedure
.input(CreateNewMessageParamsSchema)
.mutation(async ({ input, ctx }) => {
// If there's no agentId but has sessionId, resolve agentId from sessionId
@@ -115,10 +145,11 @@ export const messageRouter = router({
return ctx.messageService.createMessage({ ...input, agentId } as any);
}),
/**
* Finalize compression by updating the group with generated summary
*/
finalizeCompression: messageProcedure
finalizeCompression: messageProcedure
.input(
z.object({
agentId: z.string(),
+57 -2
View File
@@ -745,6 +745,7 @@ export class DiscoverService {
title: item.name || item.identifier,
tokenUsage: item.tokenUsage || 0,
type: item.type,
updatedAt: item.updatedAt,
userName: normalizedAuthor.userName,
};
});
@@ -1710,6 +1711,8 @@ export class DiscoverService {
locale,
})) as UserInfoResponse & {
agentGroups?: any[];
favoriteAgentGroups?: any[];
favoriteAgents?: any[];
forkedAgentGroups?: any[];
forkedAgents?: any[];
};
@@ -1719,7 +1722,7 @@ export class DiscoverService {
return undefined;
}
const { user, agents, agentGroups, forkedAgents, forkedAgentGroups } = response;
const { user, agents, agentGroups, forkedAgents, forkedAgentGroups, favoriteAgents, favoriteAgentGroups } = response;
// Transform agents to DiscoverAssistantItem format
const transformedAgents: DiscoverAssistantItem[] = (agents || []).map((agent: any) => ({
@@ -1811,9 +1814,59 @@ export class DiscoverService {
updatedAt: group.updatedAt,
}));
// Transform favoriteAgents to DiscoverAssistantItem format
const transformedFavoriteAgents: DiscoverAssistantItem[] = (favoriteAgents || []).map(
(agent: any) => ({
author: agent.author || '',
avatar: agent.avatar || '',
category: agent.category as any,
config: {} as any,
createdAt: agent.createdAt,
description: agent.description || '',
forkCount: agent.forkCount || 0,
forkedFromAgentId: agent.forkedFromAgentId || null,
homepage: `https://lobehub.com/discover/assistant/${agent.identifier}`,
identifier: agent.identifier,
installCount: agent.installCount,
isValidated: agent.isValidated,
knowledgeCount: agent.knowledgeCount || 0,
pluginCount: agent.pluginCount || 0,
schemaVersion: 1,
status: agent.status,
tags: agent.tags || [],
title: agent.name || agent.identifier,
tokenUsage: agent.tokenUsage || 0,
}),
);
// Transform favoriteAgentGroups to DiscoverGroupAgentItem format
const transformedFavoriteAgentGroups = (favoriteAgentGroups || []).map((group: any) => ({
author: group.author || '',
avatar: group.avatar || '👥',
category: group.category as any,
createdAt: group.createdAt,
description: group.description || '',
forkCount: group.forkCount || 0,
forkedFromGroupId: group.forkedFromGroupId || null,
homepage: `https://lobehub.com/discover/group_agent/${group.identifier}`,
identifier: group.identifier,
installCount: group.installCount || 0,
isFeatured: group.isFeatured || false,
isOfficial: group.isOfficial || false,
isValidated: group.isValidated,
memberCount: 0, // Will be populated from memberAgents in detail view
schemaVersion: 1,
status: group.status,
tags: group.tags || [],
title: group.name || group.identifier,
updatedAt: group.updatedAt,
}));
const result: DiscoverUserProfile = {
agentGroups: transformedAgentGroups,
agents: transformedAgents,
favoriteAgentGroups: transformedFavoriteAgentGroups,
favoriteAgents: transformedFavoriteAgents,
forkedAgentGroups: transformedForkedAgentGroups,
forkedAgents: transformedForkedAgents,
user: {
@@ -1833,11 +1886,13 @@ export class DiscoverService {
};
log(
'getUserInfo: returning user profile with %d agents, %d groups, %d forked agents, %d forked groups',
'getUserInfo: returning user profile with %d agents, %d groups, %d forked agents, %d forked groups, %d favorite agents, %d favorite groups',
result.agents.length,
result.agentGroups?.length || 0,
result.forkedAgents?.length || 0,
result.forkedAgentGroups?.length || 0,
result.favoriteAgents?.length || 0,
result.favoriteAgentGroups?.length || 0,
);
return result;
} catch (error) {
+19
View File
@@ -359,4 +359,23 @@ export class MessageService {
return { messages };
}
/**
* Cancel compression by deleting the compression group and restoring original messages
*
* @param messageGroupId - The compression group ID to cancel
* @param context - Query options for returning updated messages
*/
async cancelCompression(
messageGroupId: string,
context: QueryOptions,
): Promise<{ messages: UIChatMessage[]; success: boolean }> {
// Delete compression group (this also unmarks messages)
await this.compressionRepository.deleteCompressionGroup(messageGroupId);
// Query updated messages
const messages = await this.messageModel.query(context, this.getQueryOptions());
return { messages, success: true };
}
}
+11 -11
View File
@@ -3,21 +3,21 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
/**
*
* Utility class for safely storing temporary files
*/
export class TempFileManager {
private readonly tempDir: string;
private filePaths: Set<string> = new Set();
constructor(dirname: string) {
// 创建唯一临时目录 (跨平台安全)
// Create unique temporary directory (cross-platform safe)
this.tempDir = mkdtempSync(join(tmpdir(), dirname));
// 注册退出清理钩子
// Register cleanup hook for process exit
this.registerCleanupHook();
}
/**
* Uint8Array
* Write Uint8Array data to a temporary file
*/
async writeTempFile(data: Uint8Array, name: string): Promise<string> {
@@ -28,35 +28,35 @@ export class TempFileManager {
this.filePaths.add(filePath);
return filePath;
} catch (error) {
this.cleanup(); // 写入失败时立即清理
this.cleanup(); // Immediately cleanup on write failure
throw new Error(`Failed to write temp file: ${(error as Error).message}`);
}
}
/**
*
* Safely cleanup temporary resources
*/
cleanup(): void {
if (existsSync(this.tempDir)) {
// 递归删除目录及内容
// Recursively delete directory and its contents
rmSync(this.tempDir, { force: true, recursive: true });
this.filePaths.clear();
}
}
/**
* 退/
* Register automatic cleanup on process exit/exception
*/
private registerCleanupHook(): void {
// 正常退出
// Normal exit
process.on('exit', () => this.cleanup());
// 异常退出
// Exception exit
process.on('uncaughtException', (err) => {
console.error('Uncaught exception, cleaning temp files:', err);
this.cleanup();
process.exit(1);
});
// 信号终止
// Signal termination
['SIGINT', 'SIGTERM'].forEach((signal) => {
process.on(signal, () => {
this.cleanup();
+14
View File
@@ -276,6 +276,20 @@ export class MessageService {
messages: (result.messages || []) as unknown as UIChatMessage[],
};
};
/**
* Cancel compression by deleting the compression group and restoring original messages
*/
cancelCompression = async (params: {
agentId: string;
groupId?: string | null;
messageGroupId: string;
threadId?: string | null;
topicId: string;
}): Promise<{ messages: UIChatMessage[] }> => {
const result = await lambdaClient.message.cancelCompression.mutate(params);
return { messages: (result.messages || []) as unknown as UIChatMessage[] };
};
}
export const messageService = new MessageService();
-1
View File
@@ -75,7 +75,6 @@ export class ModelsService {
const runtimeProvider = resolveRuntimeProvider(provider);
const enableFetchOnClient = isEnableFetchOnClient(provider);
console.log('enableFetchOnClient', enableFetchOnClient);
let res: Response;
if (enableFetchOnClient) {
const agentRuntime = await initializeWithClientStore({
+1 -1
View File
@@ -1,6 +1,6 @@
import { lambdaClient } from '@/libs/trpc/client';
export type SocialTargetType = 'agent' | 'plugin';
export type SocialTargetType = 'agent' | 'plugin' | 'agent-group';
export interface FollowStatus {
isFollowing: boolean;