mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix: docker deploy REDIS_URL check (#11773)
This commit is contained in:
@@ -110,15 +110,15 @@ Used by email verification, password reset, and magic-link delivery. Two provide
|
||||
|
||||
Send emails via SMTP protocol, suitable for users with existing email services. See [Nodemailer SMTP docs](https://nodemailer.com/smtp/).
|
||||
|
||||
| Environment Variable | Type | Description | Example |
|
||||
| ------------------------ | -------- | ----------------------------------------------------------------- | ---------------------- |
|
||||
| `EMAIL_SERVICE_PROVIDER` | Optional | Set to `nodemailer` (default) | `nodemailer` |
|
||||
| `SMTP_HOST` | Required | SMTP server hostname | `smtp.gmail.com` |
|
||||
| `SMTP_PORT` | Required | SMTP server port (`587` for TLS, `465` for SSL) | `587` |
|
||||
| `SMTP_SECURE` | Optional | `true` for SSL (port 465), `false` for TLS (port 587) | `false` |
|
||||
| `SMTP_USER` | Required | SMTP auth username | `user@gmail.com` |
|
||||
| `SMTP_PASS` | Required | SMTP auth password | `your-app-password` |
|
||||
| `SMTP_FROM` | Optional | Sender address (required for AWS SES), defaults to `SMTP_USER` | `noreply@example.com` |
|
||||
| Environment Variable | Type | Description | Example |
|
||||
| ------------------------ | -------- | -------------------------------------------------------------- | --------------------- |
|
||||
| `EMAIL_SERVICE_PROVIDER` | Optional | Set to `nodemailer` (default) | `nodemailer` |
|
||||
| `SMTP_HOST` | Required | SMTP server hostname | `smtp.gmail.com` |
|
||||
| `SMTP_PORT` | Required | SMTP server port (`587` for TLS, `465` for SSL) | `587` |
|
||||
| `SMTP_SECURE` | Optional | `true` for SSL (port 465), `false` for TLS (port 587) | `false` |
|
||||
| `SMTP_USER` | Required | SMTP auth username | `user@gmail.com` |
|
||||
| `SMTP_PASS` | Required | SMTP auth password | `your-app-password` |
|
||||
| `SMTP_FROM` | Optional | Sender address (required for AWS SES), defaults to `SMTP_USER` | `noreply@example.com` |
|
||||
|
||||
<Callout type={'warning'}>
|
||||
When using Gmail, you must use an App Password instead of your account password. Generate one at [Google App Passwords](https://myaccount.google.com/apppasswords).
|
||||
@@ -148,9 +148,9 @@ Send emails via SMTP protocol, suitable for users with existing email services.
|
||||
|
||||
Enable magic-link login (depends on a working email provider above, off by default):
|
||||
|
||||
| Environment Variable | Type | Description |
|
||||
| -------------------- | -------- | ------------------------------------------------------------------- |
|
||||
| `ENABLE_MAGIC_LINK` | Optional | Set to `1` to enable passwordless magic-link login (off by default) |
|
||||
| Environment Variable | Type | Description |
|
||||
| ------------------------ | -------- | ------------------------------------------------------------------- |
|
||||
| `AUTH_ENABLE_MAGIC_LINK` | Optional | Set to `1` to enable passwordless magic-link login (off by default) |
|
||||
|
||||
<Callout type={'tip'}>
|
||||
Go to [Environment Variables](/docs/self-hosting/environment-variables/auth#better-auth) for detailed information on all Better Auth variables.
|
||||
|
||||
@@ -107,15 +107,15 @@ LobeChat 使用 [Better Auth](https://www.better-auth.com) 作为身份验证解
|
||||
|
||||
使用 SMTP 协议发送邮件,适合已有邮箱服务的用户。参考 [Nodemailer SMTP 文档](https://nodemailer.com/smtp/)。
|
||||
|
||||
| 环境变量 | 类型 | 描述 | 示例 |
|
||||
| ------------------------ | -- | ---------------------------------------------- | ---------------------- |
|
||||
| `EMAIL_SERVICE_PROVIDER` | 可选 | 设置为 `nodemailer`(默认值) | `nodemailer` |
|
||||
| `SMTP_HOST` | 必选 | SMTP 服务器主机名 | `smtp.gmail.com` |
|
||||
| `SMTP_PORT` | 必选 | SMTP 服务器端口(TLS 通常为 `587`,SSL 为 `465`) | `587` |
|
||||
| `SMTP_SECURE` | 可选 | SSL 设置为 `true`(端口 465),TLS 设置为 `false`(端口 587) | `false` |
|
||||
| `SMTP_USER` | 必选 | SMTP 认证用户名 | `user@gmail.com` |
|
||||
| `SMTP_PASS` | 必选 | SMTP 认证密码 | `your-app-password` |
|
||||
| `SMTP_FROM` | 可选 | 发件人地址(AWS SES 必填),默认为 `SMTP_USER` | `noreply@example.com` |
|
||||
| 环境变量 | 类型 | 描述 | 示例 |
|
||||
| ------------------------ | -- | ---------------------------------------------- | --------------------- |
|
||||
| `EMAIL_SERVICE_PROVIDER` | 可选 | 设置为 `nodemailer`(默认值) | `nodemailer` |
|
||||
| `SMTP_HOST` | 必选 | SMTP 服务器主机名 | `smtp.gmail.com` |
|
||||
| `SMTP_PORT` | 必选 | SMTP 服务器端口(TLS 通常为 `587`,SSL 为 `465`) | `587` |
|
||||
| `SMTP_SECURE` | 可选 | SSL 设置为 `true`(端口 465),TLS 设置为 `false`(端口 587) | `false` |
|
||||
| `SMTP_USER` | 必选 | SMTP 认证用户名 | `user@gmail.com` |
|
||||
| `SMTP_PASS` | 必选 | SMTP 认证密码 | `your-app-password` |
|
||||
| `SMTP_FROM` | 可选 | 发件人地址(AWS SES 必填),默认为 `SMTP_USER` | `noreply@example.com` |
|
||||
|
||||
<Callout type={'warning'}>
|
||||
使用 Gmail 时,需使用应用专用密码而非账户密码。前往 [Google 应用专用密码](https://myaccount.google.com/apppasswords) 生成。
|
||||
@@ -145,9 +145,9 @@ LobeChat 使用 [Better Auth](https://www.better-auth.com) 作为身份验证解
|
||||
|
||||
启用魔法链接登录(依赖上方已配置好的邮件服务,默认关闭):
|
||||
|
||||
| 环境变量 | 类型 | 描述 |
|
||||
| ------------------- | -- | ----------------------- |
|
||||
| `ENABLE_MAGIC_LINK` | 可选 | 设置为 `1` 以启用魔法链接登录(默认关闭) |
|
||||
| 环境变量 | 类型 | 描述 |
|
||||
| ------------------------ | -- | ----------------------- |
|
||||
| `AUTH_ENABLE_MAGIC_LINK` | 可选 | 设置为 `1` 以启用魔法链接登录(默认关闭) |
|
||||
|
||||
<Callout type={'tip'}>
|
||||
前往 [环境变量](/zh/docs/self-hosting/environment-variables/auth#better-auth) 可查阅所有 Better Auth 相关变量详情。
|
||||
|
||||
@@ -45,10 +45,10 @@ For small self-hosted deployments, the simplest approach is to let users reset t
|
||||
|
||||
**Example scenario**: If your previous account had two SSO accounts linked:
|
||||
|
||||
- Primary email (Google): `mail1@google.com`
|
||||
- Secondary email (Microsoft): `mail2@outlook.com`
|
||||
- Primary email (Google): `a@google.com`
|
||||
- Secondary email (Microsoft): `b@outlook.com`
|
||||
|
||||
After migrating and resetting password with `mail1@google.com`, logging in with `mail2@outlook.com` will create a **new user** instead of linking to your existing account.
|
||||
After migrating and resetting password with `a@google.com`, logging in with `b@outlook.com` will create a **new user** instead of linking to your existing account.
|
||||
</Callout>
|
||||
|
||||

|
||||
|
||||
@@ -43,10 +43,10 @@ tags:
|
||||
|
||||
**示例场景**:假设你之前的账户绑定了两个 SSO 账户:
|
||||
|
||||
- 主邮箱(Google):`mail1@google.com`
|
||||
- 副邮箱(Microsoft):`mail2@outlook.com`
|
||||
- 主邮箱(Google):`a@google.com`
|
||||
- 副邮箱(Microsoft):`b@outlook.com`
|
||||
|
||||
迁移后使用 `mail1@google.com` 重置密码,之后再用 `mail2@outlook.com` 登录将会**创建新用户**,而非关联到原有账户。
|
||||
迁移后使用 `a@google.com` 重置密码,之后再用 `b@outlook.com` 登录将会**创建新用户**,而非关联到原有账户。
|
||||
</Callout>
|
||||
|
||||

|
||||
|
||||
@@ -54,21 +54,23 @@ This guide helps you migrate your existing NextAuth-based LobeChat 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 |
|
||||
| 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 |
|
||||
|
||||
<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_`.
|
||||
@@ -82,7 +84,7 @@ Better Auth supports additional features with these new environment variables:
|
||||
| ------------------------- | ----------------------------------------- |
|
||||
| `AUTH_ALLOWED_EMAILS` | Email whitelist (restrict registration) |
|
||||
| `AUTH_EMAIL_VERIFICATION` | Enable email verification (set to `1`) |
|
||||
| `ENABLE_MAGIC_LINK` | Enable magic link login (set to `1`) |
|
||||
| `AUTH_ENABLE_MAGIC_LINK` | Enable magic link login (set to `1`) |
|
||||
| `EMAIL_SERVICE_PROVIDER` | Email provider (`nodemailer` or `resend`) |
|
||||
|
||||
## Simple Migration
|
||||
@@ -96,10 +98,10 @@ For small self-hosted deployments, the simplest approach is to let users re-logi
|
||||
|
||||
**Example scenario**: If your previous account had two SSO accounts linked:
|
||||
|
||||
- Primary email (Google): `mail1@google.com`
|
||||
- Secondary email (Microsoft): `mail2@outlook.com`
|
||||
- Primary email (Google): `a@google.com`
|
||||
- Secondary email (Microsoft): `b@outlook.com`
|
||||
|
||||
After migrating, logging in with `mail2@outlook.com` will create a **new user** instead of linking to your existing account.
|
||||
After migrating, logging in with `b@outlook.com` will create a **new user** instead of linking to your existing account.
|
||||
</Callout>
|
||||
|
||||
### Steps
|
||||
|
||||
@@ -80,7 +80,7 @@ Better Auth 支持更多功能,以下是新增的环境变量:
|
||||
| ------------------------- | -------------------------------- |
|
||||
| `AUTH_ALLOWED_EMAILS` | 邮箱白名单(限制注册) |
|
||||
| `AUTH_EMAIL_VERIFICATION` | 启用邮箱验证(设为 `1`) |
|
||||
| `ENABLE_MAGIC_LINK` | 启用魔法链接登录(设为 `1`) |
|
||||
| `AUTH_ENABLE_MAGIC_LINK` | 启用魔法链接登录(设为 `1`) |
|
||||
| `EMAIL_SERVICE_PROVIDER` | 邮件服务提供商(`nodemailer` 或 `resend`) |
|
||||
|
||||
## 简单迁移
|
||||
@@ -94,10 +94,10 @@ Better Auth 支持更多功能,以下是新增的环境变量:
|
||||
|
||||
**示例场景**:假设你之前的账户绑定了两个 SSO 账户:
|
||||
|
||||
- 主邮箱(Google):`mail1@google.com`
|
||||
- 副邮箱(Microsoft):`mail2@outlook.com`
|
||||
- 主邮箱(Google):`a@google.com`
|
||||
- 副邮箱(Microsoft):`b@outlook.com`
|
||||
|
||||
迁移后使用 `mail2@outlook.com` 登录将会**创建新用户**,而非关联到原有账户。
|
||||
迁移后使用 `b@outlook.com` 登录将会**创建新用户**,而非关联到原有账户。
|
||||
</Callout>
|
||||
|
||||
### 步骤
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import { defineConfig } from './src/libs/next/config/define-config';
|
||||
|
||||
const isVercel = !!process.env.VERCEL_ENV;
|
||||
|
||||
const nextConfig = defineConfig({
|
||||
experimental: {
|
||||
webpackBuildWorker: true,
|
||||
webpackMemoryOptimizations: true,
|
||||
},
|
||||
// Vercel serverless optimization: exclude musl binaries
|
||||
// Vercel uses Amazon Linux (glibc), not Alpine Linux (musl)
|
||||
// This saves ~45MB (29MB canvas-musl + 16MB sharp-musl)
|
||||
outputFileTracingExcludes: isVercel
|
||||
? {
|
||||
'*': [
|
||||
'node_modules/.pnpm/@napi-rs+canvas-*-musl*',
|
||||
'node_modules/.pnpm/@img+sharp-libvips-*musl*',
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
webpack: (webpackConfig, context) => {
|
||||
const { dev } = context;
|
||||
if (!dev) {
|
||||
|
||||
114
package.json
114
package.json
@@ -153,14 +153,14 @@
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@fal-ai/client": "^1.8.0",
|
||||
"@fal-ai/client": "^1.8.4",
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@google/genai": "^1.34.0",
|
||||
"@google/genai": "^1.38.0",
|
||||
"@henrygd/queue": "^1.2.0",
|
||||
"@huggingface/inference": "^4.13.5",
|
||||
"@huggingface/inference": "^4.13.10",
|
||||
"@icons-pack/react-simple-icons": "^13.8.0",
|
||||
"@khmyznikov/pwa-install": "0.3.9",
|
||||
"@langchain/community": "^0.3.58",
|
||||
"@langchain/community": "^0.3.59",
|
||||
"@lobechat/agent-runtime": "workspace:*",
|
||||
"@lobechat/builtin-agents": "workspace:*",
|
||||
"@lobechat/builtin-tool-agent-builder": "workspace:*",
|
||||
@@ -197,28 +197,28 @@
|
||||
"@lobechat/utils": "workspace:*",
|
||||
"@lobechat/web-crawler": "workspace:*",
|
||||
"@lobehub/analytics": "^1.6.0",
|
||||
"@lobehub/charts": "^4.0.2",
|
||||
"@lobehub/charts": "^4.0.3",
|
||||
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
||||
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
||||
"@lobehub/desktop-ipc-typings": "workspace:*",
|
||||
"@lobehub/editor": "^3.11.0",
|
||||
"@lobehub/icons": "^4.0.2",
|
||||
"@lobehub/editor": "^3.14.0",
|
||||
"@lobehub/icons": "^4.0.3",
|
||||
"@lobehub/market-sdk": "0.29.1",
|
||||
"@lobehub/tts": "^4.0.2",
|
||||
"@lobehub/ui": "^4.29.0",
|
||||
"@modelcontextprotocol/sdk": "^1.25.1",
|
||||
"@modelcontextprotocol/sdk": "^1.25.3",
|
||||
"@napi-rs/canvas": "^0.1.88",
|
||||
"@neondatabase/serverless": "^1.0.2",
|
||||
"@next/third-parties": "^16.1.1",
|
||||
"@opentelemetry/exporter-jaeger": "^2.2.0",
|
||||
"@next/third-parties": "^16.1.4",
|
||||
"@opentelemetry/exporter-jaeger": "^2.5.0",
|
||||
"@opentelemetry/winston-transport": "^0.19.0",
|
||||
"@react-pdf/renderer": "^4.3.2",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.4.2",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@saintno/comfyui-sdk": "^0.2.49",
|
||||
"@serwist/next": "^9.3.0",
|
||||
"@serwist/next": "^9.5.0",
|
||||
"@t3-oss/env-nextjs": "^0.13.10",
|
||||
"@tanstack/react-query": "^5.90.12",
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"@trpc/client": "^11.8.1",
|
||||
"@trpc/next": "^11.8.1",
|
||||
"@trpc/react-query": "^11.8.1",
|
||||
@@ -227,13 +227,13 @@
|
||||
"@upstash/workflow": "^0.2.23",
|
||||
"@vercel/analytics": "^1.6.1",
|
||||
"@vercel/edge-config": "^1.4.3",
|
||||
"@vercel/functions": "^3.3.4",
|
||||
"@vercel/functions": "^3.3.6",
|
||||
"@vercel/speed-insights": "^1.3.1",
|
||||
"@virtuoso.dev/masonry": "^1.3.5",
|
||||
"@virtuoso.dev/masonry": "^1.4.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"@zumer/snapdom": "^1.9.14",
|
||||
"ahooks": "^3.9.6",
|
||||
"antd": "^6.1.1",
|
||||
"antd": "^6.2.1",
|
||||
"antd-style": "4.1.0",
|
||||
"async-retry": "^1.3.3",
|
||||
"bcryptjs": "^3.0.3",
|
||||
@@ -249,26 +249,26 @@
|
||||
"dayjs": "^1.11.19",
|
||||
"debug": "^4.4.3",
|
||||
"dexie": "^3.2.7",
|
||||
"diff": "^8.0.2",
|
||||
"diff": "^8.0.3",
|
||||
"drizzle-orm": "^0.44.7",
|
||||
"drizzle-zod": "^0.5.1",
|
||||
"epub2": "^3.0.2",
|
||||
"es-toolkit": "^1.43.0",
|
||||
"es-toolkit": "^1.44.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fflate": "^0.8.2",
|
||||
"file-type": "^21.1.1",
|
||||
"file-type": "^21.3.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-to-text": "^9.0.5",
|
||||
"i18next": "^25.7.3",
|
||||
"i18next": "^25.8.0",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"immer": "^11.1.0",
|
||||
"ioredis": "^5.8.2",
|
||||
"immer": "^11.1.3",
|
||||
"ioredis": "^5.9.2",
|
||||
"jose": "^6.1.3",
|
||||
"js-sha256": "^0.11.1",
|
||||
"jsonl-parse-stringify": "^1.0.3",
|
||||
"klavis": "^2.15.0",
|
||||
"langchain": "^0.3.36",
|
||||
"langchain": "^0.3.37",
|
||||
"langfuse": "^3.38.6",
|
||||
"langfuse-core": "^3.38.6",
|
||||
"lucide-react": "^0.562.0",
|
||||
@@ -276,16 +276,16 @@
|
||||
"marked": "^17.0.1",
|
||||
"mdast-util-to-markdown": "^2.1.2",
|
||||
"model-bank": "workspace:*",
|
||||
"motion": "^12.23.26",
|
||||
"motion": "^12.29.0",
|
||||
"nanoid": "^5.1.6",
|
||||
"next": "^16.1.1",
|
||||
"next": "^16.1.4",
|
||||
"next-mdx-remote": "^5.0.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"nextjs-toploader": "^3.9.17",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"nodemailer": "^7.0.11",
|
||||
"nodemailer": "^7.0.12",
|
||||
"numeral": "^2.0.6",
|
||||
"nuqs": "^2.8.5",
|
||||
"nuqs": "^2.8.6",
|
||||
"officeparser": "5.1.1",
|
||||
"ogl": "^1.0.11",
|
||||
"oidc-provider": "^9.6.0",
|
||||
@@ -298,8 +298,8 @@
|
||||
"pdf-parse": "^1.1.4",
|
||||
"pdfjs-dist": "5.4.530",
|
||||
"pdfkit": "^0.17.2",
|
||||
"pg": "^8.16.3",
|
||||
"pino": "^10.1.0",
|
||||
"pg": "^8.17.2",
|
||||
"pino": "^10.3.0",
|
||||
"plaiceholder": "^3.0.0",
|
||||
"polished": "^4.3.1",
|
||||
"posthog-js": "~1.278.0",
|
||||
@@ -313,47 +313,47 @@
|
||||
"react-diff-view": "^3.3.2",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-fast-marquee": "^1.6.5",
|
||||
"react-hotkeys-hook": "^5.2.1",
|
||||
"react-i18next": "^16.5.0",
|
||||
"react-hotkeys-hook": "^5.2.3",
|
||||
"react-i18next": "^16.5.3",
|
||||
"react-lazy-load": "^4.0.1",
|
||||
"react-pdf": "^10.3.0",
|
||||
"react-responsive": "^10.0.1",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router-dom": "^7.11.0",
|
||||
"react-router-dom": "^7.13.0",
|
||||
"react-scan": "^0.4.3",
|
||||
"react-virtuoso": "^4.17.0",
|
||||
"react-virtuoso": "^4.18.1",
|
||||
"react-wrap-balancer": "^1.1.1",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-html": "^16.0.1",
|
||||
"remove-markdown": "^0.6.0",
|
||||
"resend": "^6.6.0",
|
||||
"remove-markdown": "^0.6.3",
|
||||
"resend": "^6.8.0",
|
||||
"resolve-accept-language": "^3.1.15",
|
||||
"rtl-detect": "^1.1.2",
|
||||
"semver": "^7.7.3",
|
||||
"sharp": "^0.34.5",
|
||||
"shiki": "^3.20.0",
|
||||
"shiki": "^3.21.0",
|
||||
"stripe": "^17.7.0",
|
||||
"superjson": "^2.2.6",
|
||||
"svix": "^1.82.0",
|
||||
"svix": "^1.84.1",
|
||||
"swr": "^2.3.8",
|
||||
"systemjs": "^6.15.1",
|
||||
"three": "^0.181.2",
|
||||
"tokenx": "^1.2.1",
|
||||
"tokenx": "^1.3.0",
|
||||
"ts-md5": "^2.0.1",
|
||||
"ua-parser-js": "^1.0.41",
|
||||
"unist-builder": "^4.0.0",
|
||||
"url-join": "^5.0.0",
|
||||
"use-merge-value": "^1.2.0",
|
||||
"uuid": "^13.0.0",
|
||||
"virtua": "^0.48.2",
|
||||
"virtua": "^0.48.3",
|
||||
"word-extractor": "^1.0.4",
|
||||
"ws": "^8.18.3",
|
||||
"ws": "^8.19.0",
|
||||
"xast-util-to-xml": "^4.0.0",
|
||||
"xastscript": "^4.0.0",
|
||||
"yaml": "^2.8.2",
|
||||
"zod": "^3.25.76",
|
||||
"zod-to-json-schema": "^3.25.0",
|
||||
"zod-to-json-schema": "^3.25.1",
|
||||
"zustand": "5.0.4",
|
||||
"zustand-utils": "^2.1.1"
|
||||
},
|
||||
@@ -361,20 +361,20 @@
|
||||
"@ast-grep/napi": "^0.40.5",
|
||||
"@commitlint/cli": "^19.8.1",
|
||||
"@edge-runtime/vm": "^5.0.0",
|
||||
"@huggingface/tasks": "^0.19.74",
|
||||
"@huggingface/tasks": "^0.19.80",
|
||||
"@lobechat/types": "workspace:*",
|
||||
"@lobehub/i18n-cli": "^1.26.0",
|
||||
"@lobehub/lint": "^1.26.3",
|
||||
"@lobehub/market-types": "^1.12.3",
|
||||
"@lobehub/seo-cli": "^1.7.0",
|
||||
"@next/bundle-analyzer": "^16.1.1",
|
||||
"@next/eslint-plugin-next": "^16.1.1",
|
||||
"@next/bundle-analyzer": "^16.1.4",
|
||||
"@next/eslint-plugin-next": "^16.1.4",
|
||||
"@peculiar/webcrypto": "^1.5.0",
|
||||
"@playwright/test": "^1.57.0",
|
||||
"@playwright/test": "^1.58.0",
|
||||
"@prettier/sync": "^0.6.1",
|
||||
"@semantic-release/exec": "^6.0.3",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/async-retry": "^1.4.9",
|
||||
"@types/chroma-js": "^3.1.2",
|
||||
@@ -383,13 +383,13 @@
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@types/node": "^24.10.4",
|
||||
"@types/nodemailer": "^7.0.4",
|
||||
"@types/node": "^24.10.9",
|
||||
"@types/nodemailer": "^7.0.5",
|
||||
"@types/numeral": "^2.0.5",
|
||||
"@types/oidc-provider": "^9.5.0",
|
||||
"@types/pdfkit": "^0.17.4",
|
||||
"@types/pg": "^8.16.0",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react": "^19.2.9",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/rtl-detect": "^1.0.3",
|
||||
"@types/semver": "^7.7.1",
|
||||
@@ -418,11 +418,11 @@
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"fs-extra": "^11.3.3",
|
||||
"glob": "^13.0.0",
|
||||
"happy-dom": "^20.0.11",
|
||||
"happy-dom": "^20.3.7",
|
||||
"husky": "^9.1.7",
|
||||
"import-in-the-middle": "^2.0.1",
|
||||
"import-in-the-middle": "^2.0.5",
|
||||
"just-diff": "^6.0.2",
|
||||
"knip": "^5.76.1",
|
||||
"knip": "^5.82.1",
|
||||
"lint-staged": "^16.2.7",
|
||||
"markdown-table": "^3.0.4",
|
||||
"mcp-hello-world": "^1.1.2",
|
||||
@@ -431,21 +431,21 @@
|
||||
"node-gyp": "^11.5.0",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"p-map": "^7.0.4",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"remark-cli": "^12.0.1",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-mdx": "^3.1.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"require-in-the-middle": "^8.0.1",
|
||||
"semantic-release": "^21.1.2",
|
||||
"serwist": "^9.3.0",
|
||||
"serwist": "^9.5.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"tsx": "^4.21.0",
|
||||
"type-fest": "^5.3.1",
|
||||
"type-fest": "^5.4.1",
|
||||
"typescript": "^5.9.3",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^7.3.0",
|
||||
"unist-util-visit": "^5.1.0",
|
||||
"vite": "^7.3.1",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.20.0",
|
||||
|
||||
@@ -39,38 +39,132 @@ const DEPRECATED_CHECKS = [
|
||||
{
|
||||
formatVar: (envVar) => {
|
||||
const mapping = {
|
||||
ENABLE_MAGIC_LINK: 'AUTH_ENABLE_MAGIC_LINK',
|
||||
NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION: 'AUTH_EMAIL_VERIFICATION',
|
||||
NEXT_PUBLIC_ENABLE_MAGIC_LINK: 'ENABLE_MAGIC_LINK',
|
||||
NEXT_PUBLIC_ENABLE_MAGIC_LINK: 'AUTH_ENABLE_MAGIC_LINK',
|
||||
};
|
||||
return `${envVar} → Please use ${mapping[envVar]} instead`;
|
||||
},
|
||||
getVars: () =>
|
||||
['NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION', 'NEXT_PUBLIC_ENABLE_MAGIC_LINK'].filter(
|
||||
(key) => process.env[key],
|
||||
),
|
||||
[
|
||||
'ENABLE_MAGIC_LINK',
|
||||
'NEXT_PUBLIC_AUTH_EMAIL_VERIFICATION',
|
||||
'NEXT_PUBLIC_ENABLE_MAGIC_LINK',
|
||||
].filter((key) => process.env[key]),
|
||||
message: 'Please update to the new environment variable names.',
|
||||
name: 'Deprecated Auth',
|
||||
},
|
||||
{
|
||||
getVars: () => (process.env['NEXT_PUBLIC_SERVICE_MODE'] ? ['NEXT_PUBLIC_SERVICE_MODE'] : []),
|
||||
message:
|
||||
'LobeChat 2.0 no longer supports client-side database mode. This environment variable is now obsolete and can be removed.',
|
||||
name: 'Service Mode',
|
||||
},
|
||||
{
|
||||
getVars: () => (process.env['ACCESS_CODE'] ? ['ACCESS_CODE'] : []),
|
||||
message:
|
||||
'ACCESS_CODE is no longer supported in LobeChat 2.0. Please use Better Auth authentication system instead.',
|
||||
name: 'Access Code',
|
||||
},
|
||||
{
|
||||
getVars: () =>
|
||||
process.env['NEXT_PUBLIC_ENABLE_BETTER_AUTH'] ? ['NEXT_PUBLIC_ENABLE_BETTER_AUTH'] : [],
|
||||
message:
|
||||
'NEXT_PUBLIC_ENABLE_BETTER_AUTH is no longer needed. Better Auth is now automatically detected via AUTH_SECRET presence.',
|
||||
name: 'Better Auth Flag',
|
||||
},
|
||||
{
|
||||
getVars: () => ['NEXT_PUBLIC_AUTH_URL', 'AUTH_URL'].filter((key) => process.env[key]),
|
||||
message:
|
||||
'AUTH_URL is no longer needed. The authentication URL is now automatically detected from request headers.',
|
||||
name: 'Auth URL',
|
||||
},
|
||||
{
|
||||
docUrl: `${MIGRATION_DOC_BASE}/nextauth-to-betterauth`,
|
||||
formatVar: (envVar) => {
|
||||
const mapping = {
|
||||
AUTH_AZURE_AD_ID: 'AUTH_MICROSOFT_ID',
|
||||
AUTH_AZURE_AD_SECRET: 'AUTH_MICROSOFT_SECRET',
|
||||
AUTH_AZURE_AD_TENANT_ID: 'No longer needed',
|
||||
AZURE_AD_CLIENT_ID: 'AUTH_MICROSOFT_ID',
|
||||
AZURE_AD_CLIENT_SECRET: 'AUTH_MICROSOFT_SECRET',
|
||||
AZURE_AD_TENANT_ID: 'No longer needed',
|
||||
};
|
||||
return `${envVar} → ${mapping[envVar]}`;
|
||||
},
|
||||
getVars: () =>
|
||||
[
|
||||
'AZURE_AD_CLIENT_ID',
|
||||
'AZURE_AD_CLIENT_SECRET',
|
||||
'AZURE_AD_TENANT_ID',
|
||||
'AUTH_AZURE_AD_ID',
|
||||
'AUTH_AZURE_AD_SECRET',
|
||||
'AUTH_AZURE_AD_TENANT_ID',
|
||||
].filter((key) => process.env[key]),
|
||||
message:
|
||||
'Azure AD provider has been renamed to Microsoft. Please update your environment variables.',
|
||||
name: 'Azure AD',
|
||||
},
|
||||
{
|
||||
formatVar: (envVar) => {
|
||||
const mapping = {
|
||||
ZITADEL_CLIENT_ID: 'AUTH_ZITADEL_ID',
|
||||
ZITADEL_CLIENT_SECRET: 'AUTH_ZITADEL_SECRET',
|
||||
ZITADEL_ISSUER: 'AUTH_ZITADEL_ISSUER',
|
||||
};
|
||||
return `${envVar} → Please use ${mapping[envVar]} instead`;
|
||||
},
|
||||
getVars: () =>
|
||||
['ZITADEL_CLIENT_ID', 'ZITADEL_CLIENT_SECRET', 'ZITADEL_ISSUER'].filter(
|
||||
(key) => process.env[key],
|
||||
),
|
||||
message: 'ZITADEL environment variables have been renamed.',
|
||||
name: 'ZITADEL',
|
||||
},
|
||||
{
|
||||
formatVar: () => 'OIDC_JWKS_KEY → Please use JWKS_KEY instead',
|
||||
getVars: () => (process.env['OIDC_JWKS_KEY'] ? ['OIDC_JWKS_KEY'] : []),
|
||||
message: 'OIDC_JWKS_KEY has been renamed to JWKS_KEY.',
|
||||
name: 'OIDC JWKS',
|
||||
},
|
||||
{
|
||||
docUrl: `${MIGRATION_DOC_BASE}/nextauth-to-betterauth`,
|
||||
formatVar: (envVar) => {
|
||||
const mapping = {
|
||||
AUTH_MICROSOFT_ENTRA_ID_BASE_URL: 'No longer needed',
|
||||
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',
|
||||
};
|
||||
return `${envVar} → ${mapping[envVar]}`;
|
||||
},
|
||||
getVars: () =>
|
||||
[
|
||||
'AUTH_MICROSOFT_ENTRA_ID_ID',
|
||||
'AUTH_MICROSOFT_ENTRA_ID_SECRET',
|
||||
'AUTH_MICROSOFT_ENTRA_ID_TENANT_ID',
|
||||
'AUTH_MICROSOFT_ENTRA_ID_BASE_URL',
|
||||
].filter((key) => process.env[key]),
|
||||
message:
|
||||
'Microsoft Entra ID provider has been renamed to Microsoft. Please update your environment variables.',
|
||||
name: 'Microsoft Entra ID',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Print error message and exit
|
||||
* Print a single deprecation error block
|
||||
*/
|
||||
function printErrorAndExit(name, vars, message, action, docUrl, formatVar) {
|
||||
console.error('\n' + '═'.repeat(70));
|
||||
console.error(`❌ ERROR: ${name} environment variables are deprecated!`);
|
||||
console.error('═'.repeat(70));
|
||||
console.error('\nDetected deprecated environment variables:');
|
||||
function printErrorBlock(name, vars, message, docUrl, formatVar) {
|
||||
console.error(`\n❌ ${name}`);
|
||||
console.error('─'.repeat(50));
|
||||
console.error('Detected deprecated environment variables:');
|
||||
for (const envVar of vars) {
|
||||
console.error(` • ${formatVar ? formatVar(envVar) : envVar}`);
|
||||
}
|
||||
console.error(`\n${message}`);
|
||||
if (docUrl) {
|
||||
console.error(`\n📖 Migration guide: ${docUrl}`);
|
||||
console.error(`📖 Migration guide: ${docUrl}`);
|
||||
}
|
||||
console.error(`\nPlease update your environment variables and ${action}.`);
|
||||
console.error('═'.repeat(70) + '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,19 +175,28 @@ function printErrorAndExit(name, vars, message, action, docUrl, formatVar) {
|
||||
function checkDeprecatedAuth(options = {}) {
|
||||
const { action = 'redeploy' } = options;
|
||||
|
||||
const foundIssues = [];
|
||||
for (const check of DEPRECATED_CHECKS) {
|
||||
const foundVars = check.getVars();
|
||||
if (foundVars.length > 0) {
|
||||
printErrorAndExit(
|
||||
check.name,
|
||||
foundVars,
|
||||
check.message,
|
||||
action,
|
||||
check.docUrl,
|
||||
check.formatVar,
|
||||
);
|
||||
foundIssues.push({ ...check, foundVars });
|
||||
}
|
||||
}
|
||||
|
||||
if (foundIssues.length > 0) {
|
||||
console.error('\n' + '═'.repeat(70));
|
||||
console.error(`❌ ERROR: Found ${foundIssues.length} deprecated environment variable issue(s)!`);
|
||||
console.error('═'.repeat(70));
|
||||
|
||||
for (const issue of foundIssues) {
|
||||
printErrorBlock(issue.name, issue.foundVars, issue.message, issue.docUrl, issue.formatVar);
|
||||
}
|
||||
|
||||
console.error('\n' + '═'.repeat(70));
|
||||
console.error(`Please update your environment variables and ${action}.`);
|
||||
console.error('═'.repeat(70) + '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkDeprecatedAuth };
|
||||
|
||||
@@ -89,7 +89,7 @@ const printEnvInfo = () => {
|
||||
console.log(` VERCEL_BRANCH_URL: ${process.env.VERCEL_BRANCH_URL ?? '(not set)'}`);
|
||||
console.log(` VERCEL_PROJECT_PRODUCTION_URL: ${process.env.VERCEL_PROJECT_PRODUCTION_URL ?? '(not set)'}`);
|
||||
console.log(` AUTH_EMAIL_VERIFICATION: ${process.env.AUTH_EMAIL_VERIFICATION ?? '(not set)'}`);
|
||||
console.log(` ENABLE_MAGIC_LINK: ${process.env.ENABLE_MAGIC_LINK ?? '(not set)'}`);
|
||||
console.log(` AUTH_ENABLE_MAGIC_LINK: ${process.env.AUTH_ENABLE_MAGIC_LINK ?? '(not set)'}`);
|
||||
|
||||
// Check SSO providers configuration
|
||||
const ssoProviders = process.env.AUTH_SSO_PROVIDERS;
|
||||
|
||||
@@ -9,7 +9,7 @@ declare global {
|
||||
// ===== Better Auth ===== //
|
||||
AUTH_SECRET?: string;
|
||||
AUTH_EMAIL_VERIFICATION?: string;
|
||||
ENABLE_MAGIC_LINK?: string;
|
||||
AUTH_ENABLE_MAGIC_LINK?: string;
|
||||
AUTH_SSO_PROVIDERS?: string;
|
||||
AUTH_TRUSTED_ORIGINS?: string;
|
||||
AUTH_ALLOWED_EMAILS?: string;
|
||||
@@ -70,11 +70,6 @@ declare global {
|
||||
AUTH_LOGTO_SECRET?: string;
|
||||
AUTH_LOGTO_ISSUER?: string;
|
||||
|
||||
AUTH_MICROSOFT_ENTRA_ID_ID?: string;
|
||||
AUTH_MICROSOFT_ENTRA_ID_SECRET?: string;
|
||||
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID?: string;
|
||||
AUTH_MICROSOFT_ENTRA_ID_BASE_URL?: string;
|
||||
|
||||
AUTH_OKTA_ID?: string;
|
||||
AUTH_OKTA_SECRET?: string;
|
||||
AUTH_OKTA_ISSUER?: string;
|
||||
@@ -86,19 +81,6 @@ declare global {
|
||||
AUTH_ZITADEL_SECRET?: string;
|
||||
AUTH_ZITADEL_ISSUER?: string;
|
||||
|
||||
AUTH_AZURE_AD_ID?: string;
|
||||
AUTH_AZURE_AD_SECRET?: string;
|
||||
AUTH_AZURE_AD_TENANT_ID?: string;
|
||||
|
||||
AZURE_AD_CLIENT_ID?: string;
|
||||
AZURE_AD_CLIENT_SECRET?: string;
|
||||
AZURE_AD_TENANT_ID?: string;
|
||||
|
||||
// ZITADEL
|
||||
ZITADEL_CLIENT_ID?: string;
|
||||
ZITADEL_CLIENT_SECRET?: string;
|
||||
ZITADEL_ISSUER?: string;
|
||||
|
||||
// ===== JWKS Key ===== //
|
||||
/**
|
||||
* Generic JWKS key for signing/verifying JWTs.
|
||||
@@ -128,7 +110,7 @@ export const getAuthConfig = () => {
|
||||
AUTH_SSO_PROVIDERS: z.string().optional().default(''),
|
||||
AUTH_TRUSTED_ORIGINS: z.string().optional(),
|
||||
AUTH_EMAIL_VERIFICATION: z.boolean().optional().default(false),
|
||||
ENABLE_MAGIC_LINK: z.boolean().optional().default(false),
|
||||
AUTH_ENABLE_MAGIC_LINK: z.boolean().optional().default(false),
|
||||
AUTH_ALLOWED_EMAILS: z.string().optional(),
|
||||
|
||||
AUTH_GOOGLE_ID: z.string().optional(),
|
||||
@@ -186,11 +168,6 @@ export const getAuthConfig = () => {
|
||||
AUTH_LOGTO_SECRET: z.string().optional(),
|
||||
AUTH_LOGTO_ISSUER: z.string().optional(),
|
||||
|
||||
AUTH_MICROSOFT_ENTRA_ID_ID: z.string().optional(),
|
||||
AUTH_MICROSOFT_ENTRA_ID_SECRET: z.string().optional(),
|
||||
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID: z.string().optional(),
|
||||
AUTH_MICROSOFT_ENTRA_ID_BASE_URL: z.string().optional(),
|
||||
|
||||
AUTH_OKTA_ID: z.string().optional(),
|
||||
AUTH_OKTA_SECRET: z.string().optional(),
|
||||
AUTH_OKTA_ISSUER: z.string().optional(),
|
||||
@@ -202,18 +179,6 @@ export const getAuthConfig = () => {
|
||||
AUTH_ZITADEL_SECRET: z.string().optional(),
|
||||
AUTH_ZITADEL_ISSUER: z.string().optional(),
|
||||
|
||||
AUTH_AZURE_AD_ID: z.string().optional(),
|
||||
AUTH_AZURE_AD_SECRET: z.string().optional(),
|
||||
AUTH_AZURE_AD_TENANT_ID: z.string().optional(),
|
||||
|
||||
AZURE_AD_CLIENT_ID: z.string().optional(),
|
||||
AZURE_AD_CLIENT_SECRET: z.string().optional(),
|
||||
AZURE_AD_TENANT_ID: z.string().optional(),
|
||||
|
||||
ZITADEL_CLIENT_ID: z.string().optional(),
|
||||
ZITADEL_CLIENT_SECRET: z.string().optional(),
|
||||
ZITADEL_ISSUER: z.string().optional(),
|
||||
|
||||
LOGTO_WEBHOOK_SIGNING_KEY: z.string().optional(),
|
||||
|
||||
// Casdoor
|
||||
@@ -229,7 +194,7 @@ export const getAuthConfig = () => {
|
||||
|
||||
runtimeEnv: {
|
||||
AUTH_EMAIL_VERIFICATION: process.env.AUTH_EMAIL_VERIFICATION === '1',
|
||||
ENABLE_MAGIC_LINK: process.env.ENABLE_MAGIC_LINK === '1',
|
||||
AUTH_ENABLE_MAGIC_LINK: process.env.AUTH_ENABLE_MAGIC_LINK === '1',
|
||||
AUTH_SECRET: process.env.AUTH_SECRET,
|
||||
AUTH_SSO_PROVIDERS: process.env.AUTH_SSO_PROVIDERS,
|
||||
AUTH_TRUSTED_ORIGINS: process.env.AUTH_TRUSTED_ORIGINS,
|
||||
@@ -293,11 +258,6 @@ export const getAuthConfig = () => {
|
||||
AUTH_LOGTO_SECRET: process.env.AUTH_LOGTO_SECRET,
|
||||
AUTH_LOGTO_ISSUER: process.env.AUTH_LOGTO_ISSUER,
|
||||
|
||||
AUTH_MICROSOFT_ENTRA_ID_ID: process.env.AUTH_MICROSOFT_ENTRA_ID_ID,
|
||||
AUTH_MICROSOFT_ENTRA_ID_SECRET: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,
|
||||
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID: process.env.AUTH_MICROSOFT_ENTRA_ID_TENANT_ID,
|
||||
AUTH_MICROSOFT_ENTRA_ID_BASE_URL: process.env.AUTH_MICROSOFT_ENTRA_ID_BASE_URL,
|
||||
|
||||
AUTH_OKTA_ID: process.env.AUTH_OKTA_ID,
|
||||
AUTH_OKTA_SECRET: process.env.AUTH_OKTA_SECRET,
|
||||
AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
|
||||
@@ -309,28 +269,14 @@ export const getAuthConfig = () => {
|
||||
AUTH_ZITADEL_SECRET: process.env.AUTH_ZITADEL_SECRET,
|
||||
AUTH_ZITADEL_ISSUER: process.env.AUTH_ZITADEL_ISSUER,
|
||||
|
||||
AUTH_AZURE_AD_ID: process.env.AUTH_AZURE_AD_ID,
|
||||
AUTH_AZURE_AD_SECRET: process.env.AUTH_AZURE_AD_SECRET,
|
||||
AUTH_AZURE_AD_TENANT_ID: process.env.AUTH_AZURE_AD_TENANT_ID,
|
||||
|
||||
// legacy Azure AD envs for backward compatibility
|
||||
AZURE_AD_CLIENT_ID: process.env.AZURE_AD_CLIENT_ID,
|
||||
AZURE_AD_CLIENT_SECRET: process.env.AZURE_AD_CLIENT_SECRET,
|
||||
AZURE_AD_TENANT_ID: process.env.AZURE_AD_TENANT_ID,
|
||||
|
||||
ZITADEL_CLIENT_ID: process.env.ZITADEL_CLIENT_ID,
|
||||
ZITADEL_CLIENT_SECRET: process.env.ZITADEL_CLIENT_SECRET,
|
||||
ZITADEL_ISSUER: process.env.ZITADEL_ISSUER,
|
||||
|
||||
// LOGTO
|
||||
LOGTO_WEBHOOK_SIGNING_KEY: process.env.LOGTO_WEBHOOK_SIGNING_KEY,
|
||||
|
||||
// Casdoor
|
||||
CASDOOR_WEBHOOK_SECRET: process.env.CASDOOR_WEBHOOK_SECRET,
|
||||
|
||||
// Generic JWKS key (fallback to OIDC_JWKS_KEY for backward compatibility)
|
||||
JWKS_KEY: process.env.JWKS_KEY || process.env.OIDC_JWKS_KEY,
|
||||
ENABLE_OIDC: !!(process.env.JWKS_KEY || process.env.OIDC_JWKS_KEY),
|
||||
JWKS_KEY: process.env.JWKS_KEY,
|
||||
ENABLE_OIDC: !!process.env.JWKS_KEY,
|
||||
|
||||
// Internal JWT expiration time
|
||||
INTERNAL_JWT_EXPIRATION: process.env.INTERNAL_JWT_EXPIRATION,
|
||||
|
||||
@@ -34,7 +34,8 @@ export const getRedisEnv = () => {
|
||||
REDIS_PASSWORD: z.string().optional(),
|
||||
REDIS_PREFIX: z.string(),
|
||||
REDIS_TLS: z.boolean().default(false),
|
||||
REDIS_URL: z.string().url().optional(),
|
||||
// NOTE: don't use z.string().url() because docker will pass empty string when not set
|
||||
REDIS_URL: z.string().optional(),
|
||||
REDIS_USERNAME: z.string().optional(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,8 +22,8 @@ import {
|
||||
getVerificationEmailTemplate,
|
||||
getVerificationOTPEmailTemplate,
|
||||
} from '@/libs/better-auth/email-templates';
|
||||
import { initBetterAuthSSOProviders } from '@/libs/better-auth/sso';
|
||||
import { emailWhitelist } from '@/libs/better-auth/plugins/email-whitelist';
|
||||
import { initBetterAuthSSOProviders } from '@/libs/better-auth/sso';
|
||||
import { createSecondaryStorage, getTrustedOrigins } from '@/libs/better-auth/utils/config';
|
||||
import { parseSSOProviders } from '@/libs/better-auth/utils/server';
|
||||
import { EmailService } from '@/server/services/email';
|
||||
@@ -61,7 +61,7 @@ const getPasskeyOrigins = (): string[] | undefined => {
|
||||
const MAGIC_LINK_EXPIRES_IN = 900;
|
||||
// OTP expiration time (in seconds) - 5 minutes for mobile OTP verification
|
||||
const OTP_EXPIRES_IN = 300;
|
||||
const enableMagicLink = authEnv.ENABLE_MAGIC_LINK;
|
||||
const enableMagicLink = authEnv.AUTH_ENABLE_MAGIC_LINK;
|
||||
const enabledSSOProviders = parseSSOProviders(authEnv.AUTH_SSO_PROVIDERS);
|
||||
|
||||
const { socialProviders, genericOAuthProviders } = initBetterAuthSSOProviders();
|
||||
|
||||
@@ -2,17 +2,6 @@ import type { GenericOAuthConfig } from 'better-auth/plugins';
|
||||
|
||||
export const DEFAULT_OIDC_SCOPES = ['openid', 'email', 'profile'];
|
||||
|
||||
export const pickEnv = (...values: (string | undefined | null)[]) => {
|
||||
for (const value of values) {
|
||||
const trimmed = value?.trim();
|
||||
if (trimmed) {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const createDiscoveryUrl = (issuer: string) => {
|
||||
const normalized = issuer.replace(/\/$/, '');
|
||||
return normalized.includes('/.well-known/')
|
||||
|
||||
@@ -1,60 +1,25 @@
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { pickEnv } from '../helpers';
|
||||
import type { BuiltinProviderDefinition } from '../types';
|
||||
|
||||
type MicrosoftEnv = {
|
||||
AUTH_AZURE_AD_ID?: string;
|
||||
AUTH_AZURE_AD_SECRET?: string;
|
||||
AUTH_MICROSOFT_ENTRA_ID_ID?: string;
|
||||
AUTH_MICROSOFT_ENTRA_ID_SECRET?: string;
|
||||
AUTH_MICROSOFT_ID?: string;
|
||||
AUTH_MICROSOFT_SECRET?: string;
|
||||
AZURE_AD_CLIENT_ID?: string;
|
||||
AZURE_AD_CLIENT_SECRET?: string;
|
||||
};
|
||||
|
||||
const getClientId = (env: MicrosoftEnv) => {
|
||||
return pickEnv(
|
||||
env.AUTH_MICROSOFT_ID,
|
||||
env.AUTH_MICROSOFT_ENTRA_ID_ID,
|
||||
env.AUTH_AZURE_AD_ID,
|
||||
env.AZURE_AD_CLIENT_ID,
|
||||
);
|
||||
};
|
||||
|
||||
const getClientSecret = (env: MicrosoftEnv) => {
|
||||
return pickEnv(
|
||||
env.AUTH_MICROSOFT_SECRET,
|
||||
env.AUTH_MICROSOFT_ENTRA_ID_SECRET,
|
||||
env.AUTH_AZURE_AD_SECRET,
|
||||
env.AZURE_AD_CLIENT_SECRET,
|
||||
);
|
||||
};
|
||||
|
||||
const provider: BuiltinProviderDefinition<MicrosoftEnv, 'microsoft'> = {
|
||||
aliases: ['microsoft-entra-id'],
|
||||
build: (env) => {
|
||||
const clientId = getClientId(env)!;
|
||||
const clientSecret = getClientSecret(env)!;
|
||||
return {
|
||||
clientId,
|
||||
clientSecret,
|
||||
};
|
||||
},
|
||||
build: (env) => ({
|
||||
clientId: env.AUTH_MICROSOFT_ID!,
|
||||
clientSecret: env.AUTH_MICROSOFT_SECRET!,
|
||||
}),
|
||||
checkEnvs: () => {
|
||||
const clientId = getClientId(authEnv);
|
||||
const clientSecret = getClientSecret(authEnv);
|
||||
const clientId = authEnv.AUTH_MICROSOFT_ID;
|
||||
const clientSecret = authEnv.AUTH_MICROSOFT_SECRET;
|
||||
return !!(clientId && clientSecret)
|
||||
? {
|
||||
AUTH_AZURE_AD_ID: authEnv.AUTH_AZURE_AD_ID,
|
||||
AUTH_AZURE_AD_SECRET: authEnv.AUTH_AZURE_AD_SECRET,
|
||||
AUTH_MICROSOFT_ENTRA_ID_ID: authEnv.AUTH_MICROSOFT_ENTRA_ID_ID,
|
||||
AUTH_MICROSOFT_ENTRA_ID_SECRET: authEnv.AUTH_MICROSOFT_ENTRA_ID_SECRET,
|
||||
AUTH_MICROSOFT_ID: authEnv.AUTH_MICROSOFT_ID,
|
||||
AUTH_MICROSOFT_SECRET: authEnv.AUTH_MICROSOFT_SECRET,
|
||||
AZURE_AD_CLIENT_ID: authEnv.AZURE_AD_CLIENT_ID,
|
||||
AZURE_AD_CLIENT_SECRET: authEnv.AZURE_AD_CLIENT_SECRET,
|
||||
AUTH_MICROSOFT_ID: clientId,
|
||||
AUTH_MICROSOFT_SECRET: clientSecret,
|
||||
}
|
||||
: false;
|
||||
},
|
||||
|
||||
@@ -1,49 +1,31 @@
|
||||
import { authEnv } from '@/envs/auth';
|
||||
|
||||
import { buildOidcConfig, pickEnv } from '../helpers';
|
||||
import { buildOidcConfig } from '../helpers';
|
||||
import type { GenericProviderDefinition } from '../types';
|
||||
|
||||
type ZitadelEnv = {
|
||||
AUTH_ZITADEL_ID?: string;
|
||||
AUTH_ZITADEL_ISSUER?: string;
|
||||
AUTH_ZITADEL_SECRET?: string;
|
||||
ZITADEL_CLIENT_ID?: string;
|
||||
ZITADEL_CLIENT_SECRET?: string;
|
||||
ZITADEL_ISSUER?: string;
|
||||
};
|
||||
|
||||
const getClientId = (env: ZitadelEnv) => {
|
||||
return pickEnv(env.ZITADEL_CLIENT_ID, env.AUTH_ZITADEL_ID);
|
||||
};
|
||||
|
||||
const getClientSecret = (env: ZitadelEnv) => {
|
||||
return pickEnv(env.ZITADEL_CLIENT_SECRET, env.AUTH_ZITADEL_SECRET);
|
||||
};
|
||||
|
||||
const getIssuer = (env: ZitadelEnv) => {
|
||||
return pickEnv(env.ZITADEL_ISSUER, env.AUTH_ZITADEL_ISSUER);
|
||||
};
|
||||
|
||||
const provider: GenericProviderDefinition<ZitadelEnv> = {
|
||||
build: (env) =>
|
||||
buildOidcConfig({
|
||||
clientId: getClientId(env)!,
|
||||
clientSecret: getClientSecret(env)!,
|
||||
issuer: getIssuer(env)!,
|
||||
clientId: env.AUTH_ZITADEL_ID!,
|
||||
clientSecret: env.AUTH_ZITADEL_SECRET!,
|
||||
issuer: env.AUTH_ZITADEL_ISSUER!,
|
||||
providerId: 'zitadel',
|
||||
}),
|
||||
checkEnvs: () => {
|
||||
const clientId = getClientId(authEnv);
|
||||
const clientSecret = getClientSecret(authEnv);
|
||||
const issuer = getIssuer(authEnv);
|
||||
const clientId = authEnv.AUTH_ZITADEL_ID;
|
||||
const clientSecret = authEnv.AUTH_ZITADEL_SECRET;
|
||||
const issuer = authEnv.AUTH_ZITADEL_ISSUER;
|
||||
return !!(clientId && clientSecret && issuer)
|
||||
? {
|
||||
AUTH_ZITADEL_ID: authEnv.AUTH_ZITADEL_ID,
|
||||
AUTH_ZITADEL_ISSUER: authEnv.AUTH_ZITADEL_ISSUER,
|
||||
AUTH_ZITADEL_SECRET: authEnv.AUTH_ZITADEL_SECRET,
|
||||
ZITADEL_CLIENT_ID: authEnv.ZITADEL_CLIENT_ID,
|
||||
ZITADEL_CLIENT_SECRET: authEnv.ZITADEL_CLIENT_SECRET,
|
||||
ZITADEL_ISSUER: authEnv.ZITADEL_ISSUER,
|
||||
AUTH_ZITADEL_ID: clientId,
|
||||
AUTH_ZITADEL_ISSUER: issuer,
|
||||
AUTH_ZITADEL_SECRET: clientSecret,
|
||||
}
|
||||
: false;
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import ReactComponentName from 'react-scan/react-component-name/webpack';
|
||||
interface CustomNextConfig {
|
||||
experimental?: NextConfig['experimental'];
|
||||
headers?: Header[];
|
||||
outputFileTracingExcludes?: NextConfig['outputFileTracingExcludes'];
|
||||
redirects?: Redirect[];
|
||||
serverExternalPackages?: NextConfig['serverExternalPackages'];
|
||||
turbopack?: NextConfig['turbopack'];
|
||||
@@ -31,22 +32,10 @@ export function defineConfig(config: CustomNextConfig) {
|
||||
outputFileTracingIncludes: { '*': ['public/**/*', '.next/static/**/*'] },
|
||||
};
|
||||
|
||||
// Vercel serverless optimization: exclude musl binaries
|
||||
// Vercel uses Amazon Linux (glibc), not Alpine Linux (musl)
|
||||
// This saves ~45MB (29MB canvas-musl + 16MB sharp-musl)
|
||||
const vercelConfig: NextConfig = {
|
||||
outputFileTracingExcludes: {
|
||||
'*': [
|
||||
'node_modules/.pnpm/@napi-rs+canvas-*-musl*',
|
||||
'node_modules/.pnpm/@img+sharp-libvips-*musl*',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const assetPrefix = process.env.NEXT_PUBLIC_ASSET_PREFIX;
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
...(isStandaloneMode ? standaloneConfig : vercelConfig),
|
||||
...(isStandaloneMode ? standaloneConfig : {}),
|
||||
assetPrefix,
|
||||
|
||||
compiler: {
|
||||
@@ -250,6 +239,9 @@ export function defineConfig(config: CustomNextConfig) {
|
||||
hmrRefreshes: true,
|
||||
},
|
||||
},
|
||||
...(config.outputFileTracingExcludes && {
|
||||
outputFileTracingExcludes: config.outputFileTracingExcludes,
|
||||
}),
|
||||
reactStrictMode: true,
|
||||
redirects: async () => [
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ export const getServerGlobalConfig = async () => {
|
||||
enableEmailVerification: authEnv.AUTH_EMAIL_VERIFICATION,
|
||||
enableKlavis: !!klavisEnv.KLAVIS_API_KEY,
|
||||
enableLobehubSkill: !!(appEnv.MARKET_TRUSTED_CLIENT_SECRET && appEnv.MARKET_TRUSTED_CLIENT_ID),
|
||||
enableMagicLink: authEnv.ENABLE_MAGIC_LINK,
|
||||
enableMagicLink: authEnv.AUTH_ENABLE_MAGIC_LINK,
|
||||
enableMarketTrustedClient: !!(
|
||||
appEnv.MARKET_TRUSTED_CLIENT_SECRET && appEnv.MARKET_TRUSTED_CLIENT_ID
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user