🐛 fix: docker deploy REDIS_URL check (#11773)

This commit is contained in:
YuTengjing
2026-01-24 18:04:45 +08:00
committed by GitHub
parent 113b491dc7
commit a9702bf3a0
18 changed files with 284 additions and 291 deletions

View File

@@ -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.

View File

@@ -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`(端口 465TLS 设置为 `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`(端口 465TLS 设置为 `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 相关变量详情。

View File

@@ -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>
![Profile Page - Linked Accounts](https://hub-apac-1.lobeobjects.space/docs/d9b41a1607d49319fd670e88529199cf.png)

View File

@@ -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>
![个人资料页 - 关联账号信息](https://hub-apac-1.lobeobjects.space/docs/43dfa498b82a58c9f99e805e88ea711a.png)

View File

@@ -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

View File

@@ -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>
### 步骤

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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 };

View File

@@ -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;

View File

@@ -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,

View File

@@ -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(),
},
});

View File

@@ -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();

View File

@@ -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/')

View File

@@ -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;
},

View File

@@ -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;
},

View File

@@ -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 () => [
{

View File

@@ -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
),