diff --git a/docs/self-hosting/advanced/auth.mdx b/docs/self-hosting/advanced/auth.mdx index 3111c486c7..d0f603c485 100644 --- a/docs/self-hosting/advanced/auth.mdx +++ b/docs/self-hosting/advanced/auth.mdx @@ -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` | 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) | Go to [Environment Variables](/docs/self-hosting/environment-variables/auth#better-auth) for detailed information on all Better Auth variables. diff --git a/docs/self-hosting/advanced/auth.zh-CN.mdx b/docs/self-hosting/advanced/auth.zh-CN.mdx index d0c08fe226..d173f2d9c2 100644 --- a/docs/self-hosting/advanced/auth.zh-CN.mdx +++ b/docs/self-hosting/advanced/auth.zh-CN.mdx @@ -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` | 使用 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` 以启用魔法链接登录(默认关闭) | 前往 [环境变量](/zh/docs/self-hosting/environment-variables/auth#better-auth) 可查阅所有 Better Auth 相关变量详情。 diff --git a/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx b/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx index 8c31a5e3fb..a3e240fd00 100644 --- a/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +++ b/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx @@ -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. ![Profile Page - Linked Accounts](https://hub-apac-1.lobeobjects.space/docs/d9b41a1607d49319fd670e88529199cf.png) diff --git a/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx b/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx index 914a105147..81644a995a 100644 --- a/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +++ b/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx @@ -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` 登录将会**创建新用户**,而非关联到原有账户。 ![个人资料页 - 关联账号信息](https://hub-apac-1.lobeobjects.space/docs/43dfa498b82a58c9f99e805e88ea711a.png) diff --git a/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx b/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx index 37df2f8063..57bc82860a 100644 --- a/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx +++ b/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx @@ -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__ID` and `AUTH__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 | **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. ### Steps diff --git a/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx b/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx index 4df0f438be..a813d52b96 100644 --- a/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx +++ b/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx @@ -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` 登录将会**创建新用户**,而非关联到原有账户。 ### 步骤 diff --git a/next.config.ts b/next.config.ts index 0c8f58385a..95b3b76d2a 100644 --- a/next.config.ts +++ b/next.config.ts @@ -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) { diff --git a/package.json b/package.json index 6bf867b1ec..66fc9ab661 100644 --- a/package.json +++ b/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", diff --git a/scripts/_shared/checkDeprecatedAuth.js b/scripts/_shared/checkDeprecatedAuth.js index 6eedf9add1..b48577e775 100644 --- a/scripts/_shared/checkDeprecatedAuth.js +++ b/scripts/_shared/checkDeprecatedAuth.js @@ -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 }; diff --git a/scripts/prebuild.mts b/scripts/prebuild.mts index d7e078d090..c72a975310 100644 --- a/scripts/prebuild.mts +++ b/scripts/prebuild.mts @@ -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; diff --git a/src/envs/auth.ts b/src/envs/auth.ts index 2ba415421f..6a1ae7f038 100644 --- a/src/envs/auth.ts +++ b/src/envs/auth.ts @@ -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, diff --git a/src/envs/redis.ts b/src/envs/redis.ts index 5096afc7fe..834605c5e6 100644 --- a/src/envs/redis.ts +++ b/src/envs/redis.ts @@ -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(), }, }); diff --git a/src/libs/better-auth/define-config.ts b/src/libs/better-auth/define-config.ts index 5b51930376..d48c87fb71 100644 --- a/src/libs/better-auth/define-config.ts +++ b/src/libs/better-auth/define-config.ts @@ -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(); diff --git a/src/libs/better-auth/sso/helpers.ts b/src/libs/better-auth/sso/helpers.ts index 68b6e98ec5..fdd00514e9 100644 --- a/src/libs/better-auth/sso/helpers.ts +++ b/src/libs/better-auth/sso/helpers.ts @@ -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/') diff --git a/src/libs/better-auth/sso/providers/microsoft.ts b/src/libs/better-auth/sso/providers/microsoft.ts index efbea4da1f..b541dee06c 100644 --- a/src/libs/better-auth/sso/providers/microsoft.ts +++ b/src/libs/better-auth/sso/providers/microsoft.ts @@ -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 = { 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; }, diff --git a/src/libs/better-auth/sso/providers/zitadel.ts b/src/libs/better-auth/sso/providers/zitadel.ts index f939a8bd30..5d5933abd2 100644 --- a/src/libs/better-auth/sso/providers/zitadel.ts +++ b/src/libs/better-auth/sso/providers/zitadel.ts @@ -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 = { 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; }, diff --git a/src/libs/next/config/define-config.ts b/src/libs/next/config/define-config.ts index 220d48e287..29c88a941c 100644 --- a/src/libs/next/config/define-config.ts +++ b/src/libs/next/config/define-config.ts @@ -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 () => [ { diff --git a/src/server/globalConfig/index.ts b/src/server/globalConfig/index.ts index 8c46676666..3ced84212d 100644 --- a/src/server/globalConfig/index.ts +++ b/src/server/globalConfig/index.ts @@ -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 ),