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) })