diff --git a/web/app/components/provider/serwist.tsx b/web/app/components/provider/serwist.tsx
index 2eef43a7d6..bd7ed86388 100644
--- a/web/app/components/provider/serwist.tsx
+++ b/web/app/components/provider/serwist.tsx
@@ -3,6 +3,7 @@
import { SerwistProvider } from '@serwist/turbopack/react'
import { useEffect } from 'react'
import { IS_DEV } from '@/config'
+import { env } from '@/env'
import { isClient } from '@/utils/client'
export function PWAProvider({ children }: { children: React.ReactNode }) {
@@ -10,7 +11,7 @@ export function PWAProvider({ children }: { children: React.ReactNode }) {
return {children}
}
- const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
+ const basePath = env.NEXT_PUBLIC_BASE_PATH || ''
const swUrl = `${basePath}/serwist/sw.js`
return (
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
index 845cae2d4e..a642d73259 100644
--- a/web/app/layout.tsx
+++ b/web/app/layout.tsx
@@ -5,6 +5,7 @@ import { Instrument_Serif } from 'next/font/google'
import { NuqsAdapter } from 'nuqs/adapters/next/app'
import GlobalPublicStoreProvider from '@/context/global-public-context'
import { TanstackQueryInitializer } from '@/context/query-client'
+import { env } from '@/env'
import { getLocaleOnServer } from '@/i18n-config/server'
import { DatasetAttr } from '@/types/feature'
import { cn } from '@/utils/classnames'
@@ -41,37 +42,37 @@ const LocaleLayout = async ({
const locale = await getLocaleOnServer()
const datasetMap: Record = {
- [DatasetAttr.DATA_API_PREFIX]: process.env.NEXT_PUBLIC_API_PREFIX,
- [DatasetAttr.DATA_PUBLIC_API_PREFIX]: process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
- [DatasetAttr.DATA_MARKETPLACE_API_PREFIX]: process.env.NEXT_PUBLIC_MARKETPLACE_API_PREFIX,
- [DatasetAttr.DATA_MARKETPLACE_URL_PREFIX]: process.env.NEXT_PUBLIC_MARKETPLACE_URL_PREFIX,
- [DatasetAttr.DATA_PUBLIC_EDITION]: process.env.NEXT_PUBLIC_EDITION,
- [DatasetAttr.DATA_PUBLIC_AMPLITUDE_API_KEY]: process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY,
- [DatasetAttr.DATA_PUBLIC_COOKIE_DOMAIN]: process.env.NEXT_PUBLIC_COOKIE_DOMAIN,
- [DatasetAttr.DATA_PUBLIC_SUPPORT_MAIL_LOGIN]: process.env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN,
- [DatasetAttr.DATA_PUBLIC_SENTRY_DSN]: process.env.NEXT_PUBLIC_SENTRY_DSN,
- [DatasetAttr.DATA_PUBLIC_MAINTENANCE_NOTICE]: process.env.NEXT_PUBLIC_MAINTENANCE_NOTICE,
- [DatasetAttr.DATA_PUBLIC_SITE_ABOUT]: process.env.NEXT_PUBLIC_SITE_ABOUT,
- [DatasetAttr.DATA_PUBLIC_TEXT_GENERATION_TIMEOUT_MS]: process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS,
- [DatasetAttr.DATA_PUBLIC_MAX_TOOLS_NUM]: process.env.NEXT_PUBLIC_MAX_TOOLS_NUM,
- [DatasetAttr.DATA_PUBLIC_MAX_PARALLEL_LIMIT]: process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT,
- [DatasetAttr.DATA_PUBLIC_TOP_K_MAX_VALUE]: process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE,
- [DatasetAttr.DATA_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH]: process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH,
- [DatasetAttr.DATA_PUBLIC_LOOP_NODE_MAX_COUNT]: process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT,
- [DatasetAttr.DATA_PUBLIC_MAX_ITERATIONS_NUM]: process.env.NEXT_PUBLIC_MAX_ITERATIONS_NUM,
- [DatasetAttr.DATA_PUBLIC_MAX_TREE_DEPTH]: process.env.NEXT_PUBLIC_MAX_TREE_DEPTH,
- [DatasetAttr.DATA_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME]: process.env.NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME,
- [DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_JINAREADER]: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER,
- [DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_FIRECRAWL]: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
- [DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_WATERCRAWL]: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
- [DatasetAttr.DATA_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX]: process.env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
- [DatasetAttr.NEXT_PUBLIC_ZENDESK_WIDGET_KEY]: process.env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
- [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
- [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
- [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL,
- [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID,
- [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN,
- [DatasetAttr.DATA_PUBLIC_BATCH_CONCURRENCY]: process.env.NEXT_PUBLIC_BATCH_CONCURRENCY,
+ [DatasetAttr.DATA_API_PREFIX]: env.NEXT_PUBLIC_API_PREFIX,
+ [DatasetAttr.DATA_PUBLIC_API_PREFIX]: env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
+ [DatasetAttr.DATA_MARKETPLACE_API_PREFIX]: env.NEXT_PUBLIC_MARKETPLACE_API_PREFIX,
+ [DatasetAttr.DATA_MARKETPLACE_URL_PREFIX]: env.NEXT_PUBLIC_MARKETPLACE_URL_PREFIX,
+ [DatasetAttr.DATA_PUBLIC_EDITION]: env.NEXT_PUBLIC_EDITION,
+ [DatasetAttr.DATA_PUBLIC_AMPLITUDE_API_KEY]: env.NEXT_PUBLIC_AMPLITUDE_API_KEY,
+ [DatasetAttr.DATA_PUBLIC_COOKIE_DOMAIN]: env.NEXT_PUBLIC_COOKIE_DOMAIN,
+ [DatasetAttr.DATA_PUBLIC_SUPPORT_MAIL_LOGIN]: env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN,
+ [DatasetAttr.DATA_PUBLIC_SENTRY_DSN]: env.NEXT_PUBLIC_SENTRY_DSN,
+ [DatasetAttr.DATA_PUBLIC_MAINTENANCE_NOTICE]: env.NEXT_PUBLIC_MAINTENANCE_NOTICE,
+ [DatasetAttr.DATA_PUBLIC_SITE_ABOUT]: env.NEXT_PUBLIC_SITE_ABOUT,
+ [DatasetAttr.DATA_PUBLIC_TEXT_GENERATION_TIMEOUT_MS]: env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS,
+ [DatasetAttr.DATA_PUBLIC_MAX_TOOLS_NUM]: env.NEXT_PUBLIC_MAX_TOOLS_NUM,
+ [DatasetAttr.DATA_PUBLIC_MAX_PARALLEL_LIMIT]: env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT,
+ [DatasetAttr.DATA_PUBLIC_TOP_K_MAX_VALUE]: env.NEXT_PUBLIC_TOP_K_MAX_VALUE,
+ [DatasetAttr.DATA_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH]: env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH,
+ [DatasetAttr.DATA_PUBLIC_LOOP_NODE_MAX_COUNT]: env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT,
+ [DatasetAttr.DATA_PUBLIC_MAX_ITERATIONS_NUM]: env.NEXT_PUBLIC_MAX_ITERATIONS_NUM,
+ [DatasetAttr.DATA_PUBLIC_MAX_TREE_DEPTH]: env.NEXT_PUBLIC_MAX_TREE_DEPTH,
+ [DatasetAttr.DATA_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME]: env.NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME,
+ [DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_JINAREADER]: env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER,
+ [DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_FIRECRAWL]: env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
+ [DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_WATERCRAWL]: env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
+ [DatasetAttr.DATA_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX]: env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
+ [DatasetAttr.NEXT_PUBLIC_ZENDESK_WIDGET_KEY]: env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
+ [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT]: env.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
+ [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION]: env.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
+ [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL]: env.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL,
+ [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID]: env.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID,
+ [DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN]: env.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN,
+ [DatasetAttr.DATA_PUBLIC_BATCH_CONCURRENCY]: env.NEXT_PUBLIC_BATCH_CONCURRENCY,
}
return (
diff --git a/web/app/serwist/[path]/route.ts b/web/app/serwist/[path]/route.ts
index beca2cd412..090c7a1665 100644
--- a/web/app/serwist/[path]/route.ts
+++ b/web/app/serwist/[path]/route.ts
@@ -1,6 +1,7 @@
import { createSerwistRoute } from '@serwist/turbopack'
+import { env } from '@/env'
-const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
+const basePath = env.NEXT_PUBLIC_BASE_PATH || ''
export const { dynamic, dynamicParams, revalidate, generateStaticParams, GET } = createSerwistRoute({
swSrc: 'app/sw.ts',
diff --git a/web/config/index.ts b/web/config/index.ts
index 08ce14b264..bc034378d1 100644
--- a/web/config/index.ts
+++ b/web/config/index.ts
@@ -1,5 +1,6 @@
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { InputVarType } from '@/app/components/workflow/types'
+import { env } from '@/env'
import { PromptRole } from '@/models/debug'
import { PipelineInputVarType } from '@/models/pipeline'
import { AgentStrategy } from '@/types/app'
@@ -54,28 +55,28 @@ const getStringConfig = (
}
export const API_PREFIX = getStringConfig(
- process.env.NEXT_PUBLIC_API_PREFIX,
+ env.NEXT_PUBLIC_API_PREFIX,
DatasetAttr.DATA_API_PREFIX,
'http://localhost:5001/console/api',
)
export const PUBLIC_API_PREFIX = getStringConfig(
- process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
+ env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
DatasetAttr.DATA_PUBLIC_API_PREFIX,
'http://localhost:5001/api',
)
export const MARKETPLACE_API_PREFIX = getStringConfig(
- process.env.NEXT_PUBLIC_MARKETPLACE_API_PREFIX,
+ env.NEXT_PUBLIC_MARKETPLACE_API_PREFIX,
DatasetAttr.DATA_MARKETPLACE_API_PREFIX,
'http://localhost:5002/api',
)
export const MARKETPLACE_URL_PREFIX = getStringConfig(
- process.env.NEXT_PUBLIC_MARKETPLACE_URL_PREFIX,
+ env.NEXT_PUBLIC_MARKETPLACE_URL_PREFIX,
DatasetAttr.DATA_MARKETPLACE_URL_PREFIX,
'',
)
const EDITION = getStringConfig(
- process.env.NEXT_PUBLIC_EDITION,
+ env.NEXT_PUBLIC_EDITION,
DatasetAttr.DATA_PUBLIC_EDITION,
'SELF_HOSTED',
)
@@ -84,16 +85,16 @@ export const IS_CE_EDITION = EDITION === 'SELF_HOSTED'
export const IS_CLOUD_EDITION = EDITION === 'CLOUD'
export const AMPLITUDE_API_KEY = getStringConfig(
- process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY,
+ env.NEXT_PUBLIC_AMPLITUDE_API_KEY,
DatasetAttr.DATA_PUBLIC_AMPLITUDE_API_KEY,
'',
)
-export const IS_DEV = process.env.NODE_ENV === 'development'
-export const IS_PROD = process.env.NODE_ENV === 'production'
+export const IS_DEV = env.NODE_ENV === 'development'
+export const IS_PROD = env.NODE_ENV === 'production'
export const SUPPORT_MAIL_LOGIN = !!(
- process.env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN
+ env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN
|| globalThis.document?.body?.getAttribute('data-public-support-mail-login')
)
@@ -161,13 +162,13 @@ export const getMaxToken = (modelId: string) => {
export const LOCALE_COOKIE_NAME = 'locale'
const COOKIE_DOMAIN = getStringConfig(
- process.env.NEXT_PUBLIC_COOKIE_DOMAIN,
+ env.NEXT_PUBLIC_COOKIE_DOMAIN,
DatasetAttr.DATA_PUBLIC_COOKIE_DOMAIN,
'',
).trim()
export const BATCH_CONCURRENCY = getNumberConfig(
- process.env.NEXT_PUBLIC_BATCH_CONCURRENCY,
+ env.NEXT_PUBLIC_BATCH_CONCURRENCY,
DatasetAttr.DATA_PUBLIC_BATCH_CONCURRENCY,
5, // default
)
@@ -342,10 +343,10 @@ export const VAR_REGEX
export const resetReg = () => (VAR_REGEX.lastIndex = 0)
export const DISABLE_UPLOAD_IMAGE_AS_ICON
- = process.env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON === 'true'
+ = env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON === 'true'
export const GITHUB_ACCESS_TOKEN
- = process.env.NEXT_PUBLIC_GITHUB_ACCESS_TOKEN || ''
+ = env.NEXT_PUBLIC_GITHUB_ACCESS_TOKEN || ''
export const SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS = '.difypkg,.difybndl'
export const FULL_DOC_PREVIEW_LENGTH = 50
@@ -353,58 +354,58 @@ export const FULL_DOC_PREVIEW_LENGTH = 50
export const JSON_SCHEMA_MAX_DEPTH = 10
export const MAX_TOOLS_NUM = getNumberConfig(
- process.env.NEXT_PUBLIC_MAX_TOOLS_NUM,
+ env.NEXT_PUBLIC_MAX_TOOLS_NUM,
DatasetAttr.DATA_PUBLIC_MAX_TOOLS_NUM,
10,
)
export const MAX_PARALLEL_LIMIT = getNumberConfig(
- process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT,
+ env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT,
DatasetAttr.DATA_PUBLIC_MAX_PARALLEL_LIMIT,
10,
)
export const TEXT_GENERATION_TIMEOUT_MS = getNumberConfig(
- process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS,
+ env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS,
DatasetAttr.DATA_PUBLIC_TEXT_GENERATION_TIMEOUT_MS,
60000,
)
export const LOOP_NODE_MAX_COUNT = getNumberConfig(
- process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT,
+ env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT,
DatasetAttr.DATA_PUBLIC_LOOP_NODE_MAX_COUNT,
100,
)
export const MAX_ITERATIONS_NUM = getNumberConfig(
- process.env.NEXT_PUBLIC_MAX_ITERATIONS_NUM,
+ env.NEXT_PUBLIC_MAX_ITERATIONS_NUM,
DatasetAttr.DATA_PUBLIC_MAX_ITERATIONS_NUM,
99,
)
export const MAX_TREE_DEPTH = getNumberConfig(
- process.env.NEXT_PUBLIC_MAX_TREE_DEPTH,
+ env.NEXT_PUBLIC_MAX_TREE_DEPTH,
DatasetAttr.DATA_PUBLIC_MAX_TREE_DEPTH,
50,
)
export const ALLOW_UNSAFE_DATA_SCHEME = getBooleanConfig(
- process.env.NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME,
+ env.NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME,
DatasetAttr.DATA_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME,
false,
)
export const ENABLE_WEBSITE_JINAREADER = getBooleanConfig(
- process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER,
+ env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER,
DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_JINAREADER,
true,
)
export const ENABLE_WEBSITE_FIRECRAWL = getBooleanConfig(
- process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
+ env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
true,
)
export const ENABLE_WEBSITE_WATERCRAWL = getBooleanConfig(
- process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
+ env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
false,
)
export const ENABLE_SINGLE_DOLLAR_LATEX = getBooleanConfig(
- process.env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
+ env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
DatasetAttr.DATA_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
false,
)
@@ -414,33 +415,33 @@ export const VALUE_SELECTOR_DELIMITER = '@@@'
export const validPassword = /^(?=.*[a-z])(?=.*\d)\S{8,}$/i
export const ZENDESK_WIDGET_KEY = getStringConfig(
- process.env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
+ env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
DatasetAttr.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
'',
)
export const ZENDESK_FIELD_IDS = {
ENVIRONMENT: getStringConfig(
- process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
+ env.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
'',
),
VERSION: getStringConfig(
- process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
+ env.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
'',
),
EMAIL: getStringConfig(
- process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL,
+ env.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL,
DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL,
'',
),
WORKSPACE_ID: getStringConfig(
- process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID,
+ env.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID,
DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID,
'',
),
PLAN: getStringConfig(
- process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN,
+ env.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN,
DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN,
'',
),
diff --git a/web/env.ts b/web/env.ts
new file mode 100644
index 0000000000..f54f36b048
--- /dev/null
+++ b/web/env.ts
@@ -0,0 +1,101 @@
+import { createEnv } from '@t3-oss/env-nextjs'
+import { z } from 'zod'
+
+const optionalString = z.string().optional()
+
+export const env = createEnv({
+ server: {
+ ANALYZE: optionalString,
+ INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: optionalString,
+ NEXT_TELEMETRY_DISABLED: optionalString,
+ PORT: optionalString,
+ TEXT_GENERATION_TIMEOUT_MS: optionalString,
+ },
+ shared: {
+ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
+ },
+ client: {
+ NEXT_PUBLIC_ALLOW_EMBED: optionalString,
+ NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME: optionalString,
+ NEXT_PUBLIC_AMPLITUDE_API_KEY: optionalString,
+ NEXT_PUBLIC_API_PREFIX: optionalString,
+ NEXT_PUBLIC_BASE_PATH: optionalString,
+ NEXT_PUBLIC_BATCH_CONCURRENCY: optionalString,
+ NEXT_PUBLIC_COOKIE_DOMAIN: optionalString,
+ NEXT_PUBLIC_CSP_WHITELIST: optionalString,
+ NEXT_PUBLIC_DEPLOY_ENV: optionalString,
+ NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON: optionalString,
+ NEXT_PUBLIC_EDITION: optionalString,
+ NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX: optionalString,
+ NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: optionalString,
+ NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: optionalString,
+ NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL: optionalString,
+ NEXT_PUBLIC_GITHUB_ACCESS_TOKEN: optionalString,
+ NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: optionalString,
+ NEXT_PUBLIC_LOOP_NODE_MAX_COUNT: optionalString,
+ NEXT_PUBLIC_MAINTENANCE_NOTICE: optionalString,
+ NEXT_PUBLIC_MARKETPLACE_API_PREFIX: optionalString,
+ NEXT_PUBLIC_MARKETPLACE_URL_PREFIX: optionalString,
+ NEXT_PUBLIC_MAX_ITERATIONS_NUM: optionalString,
+ NEXT_PUBLIC_MAX_PARALLEL_LIMIT: optionalString,
+ NEXT_PUBLIC_MAX_TOOLS_NUM: optionalString,
+ NEXT_PUBLIC_MAX_TREE_DEPTH: optionalString,
+ NEXT_PUBLIC_PUBLIC_API_PREFIX: optionalString,
+ NEXT_PUBLIC_SENTRY_DSN: optionalString,
+ NEXT_PUBLIC_SITE_ABOUT: optionalString,
+ NEXT_PUBLIC_SUPPORT_MAIL_LOGIN: optionalString,
+ NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS: optionalString,
+ NEXT_PUBLIC_TOP_K_MAX_VALUE: optionalString,
+ NEXT_PUBLIC_UPLOAD_IMAGE_AS_ICON: optionalString,
+ NEXT_PUBLIC_WEB_PREFIX: optionalString,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL: optionalString,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT: optionalString,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN: optionalString,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION: optionalString,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID: optionalString,
+ NEXT_PUBLIC_ZENDESK_WIDGET_KEY: optionalString,
+ },
+ experimental__runtimeEnv: {
+ NODE_ENV: process.env.NODE_ENV,
+ NEXT_PUBLIC_ALLOW_EMBED: process.env.NEXT_PUBLIC_ALLOW_EMBED,
+ NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME: process.env.NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME,
+ NEXT_PUBLIC_AMPLITUDE_API_KEY: process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY,
+ NEXT_PUBLIC_API_PREFIX: process.env.NEXT_PUBLIC_API_PREFIX,
+ NEXT_PUBLIC_BASE_PATH: process.env.NEXT_PUBLIC_BASE_PATH,
+ NEXT_PUBLIC_BATCH_CONCURRENCY: process.env.NEXT_PUBLIC_BATCH_CONCURRENCY,
+ NEXT_PUBLIC_COOKIE_DOMAIN: process.env.NEXT_PUBLIC_COOKIE_DOMAIN,
+ NEXT_PUBLIC_CSP_WHITELIST: process.env.NEXT_PUBLIC_CSP_WHITELIST,
+ NEXT_PUBLIC_DEPLOY_ENV: process.env.NEXT_PUBLIC_DEPLOY_ENV,
+ NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON: process.env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON,
+ NEXT_PUBLIC_EDITION: process.env.NEXT_PUBLIC_EDITION,
+ NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX: process.env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
+ NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
+ NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER,
+ NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
+ NEXT_PUBLIC_GITHUB_ACCESS_TOKEN: process.env.NEXT_PUBLIC_GITHUB_ACCESS_TOKEN,
+ NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH,
+ NEXT_PUBLIC_LOOP_NODE_MAX_COUNT: process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT,
+ NEXT_PUBLIC_MAINTENANCE_NOTICE: process.env.NEXT_PUBLIC_MAINTENANCE_NOTICE,
+ NEXT_PUBLIC_MARKETPLACE_API_PREFIX: process.env.NEXT_PUBLIC_MARKETPLACE_API_PREFIX,
+ NEXT_PUBLIC_MARKETPLACE_URL_PREFIX: process.env.NEXT_PUBLIC_MARKETPLACE_URL_PREFIX,
+ NEXT_PUBLIC_MAX_ITERATIONS_NUM: process.env.NEXT_PUBLIC_MAX_ITERATIONS_NUM,
+ NEXT_PUBLIC_MAX_PARALLEL_LIMIT: process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT,
+ NEXT_PUBLIC_MAX_TOOLS_NUM: process.env.NEXT_PUBLIC_MAX_TOOLS_NUM,
+ NEXT_PUBLIC_MAX_TREE_DEPTH: process.env.NEXT_PUBLIC_MAX_TREE_DEPTH,
+ NEXT_PUBLIC_PUBLIC_API_PREFIX: process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
+ NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
+ NEXT_PUBLIC_SITE_ABOUT: process.env.NEXT_PUBLIC_SITE_ABOUT,
+ NEXT_PUBLIC_SUPPORT_MAIL_LOGIN: process.env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN,
+ NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS: process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS,
+ NEXT_PUBLIC_TOP_K_MAX_VALUE: process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE,
+ NEXT_PUBLIC_UPLOAD_IMAGE_AS_ICON: process.env.NEXT_PUBLIC_UPLOAD_IMAGE_AS_ICON,
+ NEXT_PUBLIC_WEB_PREFIX: process.env.NEXT_PUBLIC_WEB_PREFIX,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_EMAIL,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_PLAN,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
+ NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_WORKSPACE_ID,
+ NEXT_PUBLIC_ZENDESK_WIDGET_KEY: process.env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
+ },
+ emptyStringAsUndefined: true,
+})
diff --git a/web/next.config.ts b/web/next.config.ts
index 0bbdbaf32c..02035481b0 100644
--- a/web/next.config.ts
+++ b/web/next.config.ts
@@ -1,10 +1,10 @@
import type { NextConfig } from 'next'
-import process from 'node:process'
import withBundleAnalyzerInit from '@next/bundle-analyzer'
import createMDX from '@next/mdx'
import { codeInspectorPlugin } from 'code-inspector-plugin'
+import { env } from './env'
-const isDev = process.env.NODE_ENV === 'development'
+const isDev = env.NODE_ENV === 'development'
const withMDX = createMDX({
extension: /\.mdx?$/,
options: {
@@ -18,19 +18,19 @@ const withMDX = createMDX({
},
})
const withBundleAnalyzer = withBundleAnalyzerInit({
- enabled: process.env.ANALYZE === 'true',
+ enabled: env.ANALYZE === 'true',
})
// the default url to prevent parse url error when running jest
-const hasSetWebPrefix = process.env.NEXT_PUBLIC_WEB_PREFIX
-const port = process.env.PORT || 3000
+const hasSetWebPrefix = env.NEXT_PUBLIC_WEB_PREFIX
+const port = env.PORT || '3000'
const locImageURLs = !hasSetWebPrefix ? [new URL(`http://localhost:${port}/**`), new URL(`http://127.0.0.1:${port}/**`)] : []
-const remoteImageURLs = ([hasSetWebPrefix ? new URL(`${process.env.NEXT_PUBLIC_WEB_PREFIX}/**`) : '', ...locImageURLs].filter(item => !!item)) as URL[]
+const remoteImageURLs = ([hasSetWebPrefix ? new URL(`${env.NEXT_PUBLIC_WEB_PREFIX}/**`) : '', ...locImageURLs].filter(item => !!item)) as URL[]
const nextConfig: NextConfig = {
- basePath: process.env.NEXT_PUBLIC_BASE_PATH || '',
+ basePath: env.NEXT_PUBLIC_BASE_PATH || '',
serverExternalPackages: ['esbuild'],
- transpilePackages: ['echarts', 'zrender'],
+ transpilePackages: ['@t3-oss/env-core', '@t3-oss/env-nextjs', 'echarts', 'zrender'],
turbopack: {
rules: codeInspectorPlugin({
bundler: 'turbopack',
diff --git a/web/proxy.ts b/web/proxy.ts
index 05436557d7..c511df70b0 100644
--- a/web/proxy.ts
+++ b/web/proxy.ts
@@ -1,13 +1,14 @@
import type { NextRequest } from 'next/server'
import { Buffer } from 'node:buffer'
import { NextResponse } from 'next/server'
+import { env } from '@/env'
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com https://api2.amplitude.com *.amplitude.com'
const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => {
// prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking
// Chatbot page should be allowed to be embedded in iframe. It's a feature
- if (process.env.NEXT_PUBLIC_ALLOW_EMBED !== 'true' && !pathname.startsWith('/chat') && !pathname.startsWith('/workflow') && !pathname.startsWith('/completion') && !pathname.startsWith('/webapp-signin'))
+ if (env.NEXT_PUBLIC_ALLOW_EMBED !== 'true' && !pathname.startsWith('/chat') && !pathname.startsWith('/workflow') && !pathname.startsWith('/completion') && !pathname.startsWith('/webapp-signin'))
response.headers.set('X-Frame-Options', 'DENY')
return response
@@ -21,11 +22,11 @@ export function proxy(request: NextRequest) {
},
})
- const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production'
+ const isWhiteListEnabled = !!env.NEXT_PUBLIC_CSP_WHITELIST && env.NODE_ENV === 'production'
if (!isWhiteListEnabled)
return wrapResponseWithXFrameOptions(response, pathname)
- const whiteList = `${process.env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}`
+ const whiteList = `${env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}`
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const csp = `'nonce-${nonce}'`
diff --git a/web/utils/var.ts b/web/utils/var.ts
index 1851084b2e..360c825ba6 100644
--- a/web/utils/var.ts
+++ b/web/utils/var.ts
@@ -8,6 +8,7 @@ import {
} from '@/app/components/base/prompt-editor/constants'
import { InputVarType } from '@/app/components/workflow/types'
import { getMaxVarNameLength, MARKETPLACE_URL_PREFIX, MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW } from '@/config'
+import { env } from '@/env'
const otherAllowedRegex = /^\w+$/
@@ -129,7 +130,7 @@ export const getVars = (value: string) => {
// Set the value of basePath
// example: /dify
-export const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
+export const basePath = env.NEXT_PUBLIC_BASE_PATH || ''
export function getMarketplaceUrl(path: string, params?: Record) {
const searchParams = new URLSearchParams({ source: encodeURIComponent(window.location.origin) })