env var NEXT_PUBLIC_SOCKET_URL

This commit is contained in:
hjlarry 2026-01-20 20:34:56 +08:00
parent f99ac24d5c
commit eaf888b02a
11 changed files with 27 additions and 42 deletions

View File

@ -406,6 +406,8 @@ CONSOLE_CORS_ALLOW_ORIGINS=*
COOKIE_DOMAIN=
# When the frontend and backend run on different subdomains, set NEXT_PUBLIC_COOKIE_DOMAIN=1.
NEXT_PUBLIC_COOKIE_DOMAIN=
# WebSocket server URL.
NEXT_PUBLIC_SOCKET_URL=ws://localhost
NEXT_PUBLIC_BATCH_CONCURRENCY=5
# ------------------------------

View File

@ -139,6 +139,7 @@ services:
APP_API_URL: ${APP_API_URL:-}
AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-}
NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-}
NEXT_PUBLIC_SOCKET_URL: ${NEXT_PUBLIC_SOCKET_URL:-ws://localhost}
SENTRY_DSN: ${WEB_SENTRY_DSN:-}
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}

View File

@ -110,6 +110,7 @@ x-shared-env: &shared-api-worker-env
CONSOLE_CORS_ALLOW_ORIGINS: ${CONSOLE_CORS_ALLOW_ORIGINS:-*}
COOKIE_DOMAIN: ${COOKIE_DOMAIN:-}
NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-}
NEXT_PUBLIC_SOCKET_URL: ${NEXT_PUBLIC_SOCKET_URL:-ws://localhost}
NEXT_PUBLIC_BATCH_CONCURRENCY: ${NEXT_PUBLIC_BATCH_CONCURRENCY:-5}
STORAGE_TYPE: ${STORAGE_TYPE:-opendal}
OPENDAL_SCHEME: ${OPENDAL_SCHEME:-fs}
@ -824,6 +825,7 @@ services:
APP_API_URL: ${APP_API_URL:-}
AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-}
NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-}
NEXT_PUBLIC_SOCKET_URL: ${NEXT_PUBLIC_SOCKET_URL:-}
SENTRY_DSN: ${WEB_SENTRY_DSN:-}
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}

View File

@ -14,6 +14,8 @@ NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
# When the frontend and backend run on different subdomains, set NEXT_PUBLIC_COOKIE_DOMAIN=1.
NEXT_PUBLIC_COOKIE_DOMAIN=
# WebSocket server URL.
NEXT_PUBLIC_SOCKET_URL=ws://localhost:5001
# The API PREFIX for MARKETPLACE
NEXT_PUBLIC_MARKETPLACE_API_PREFIX=https://marketplace.dify.ai/api/v1

View File

@ -43,6 +43,8 @@ NEXT_PUBLIC_EDITION=SELF_HOSTED
# example: http://cloud.dify.ai/console/api
NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
NEXT_PUBLIC_COOKIE_DOMAIN=
# WebSocket server URL.
NEXT_PUBLIC_SOCKET_URL=ws://localhost:5001
# The URL for Web APP, refers to the Web App base URL of WEB service if web app domain is different from
# console or api domain.
# example: http://udify.app/api

View File

@ -42,33 +42,13 @@ const createMockSocket = (id: string): MockSocket => {
return socket
}
const setGlobalWindow = (value?: typeof window): void => {
const globalWithWindow = globalThis as Partial<typeof globalThis> & { window?: typeof window }
if (value)
globalWithWindow.window = value
else
delete globalWithWindow.window
}
describe('WebSocketClient', () => {
let originalWindow: typeof window | undefined
beforeEach(() => {
vi.resetModules()
ioMock.mockReset()
originalWindow = globalThis.window
})
afterEach(() => {
if (originalWindow)
setGlobalWindow(originalWindow)
else
setGlobalWindow(undefined)
})
it('connects with fallback url and registers base listeners when window is undefined', async () => {
setGlobalWindow(undefined)
it('connects with default url and registers base listeners', async () => {
const mockSocket = createMockSocket('socket-fallback')
ioMock.mockImplementation(() => mockSocket)
@ -111,10 +91,6 @@ describe('WebSocketClient', () => {
return mockSocket
})
setGlobalWindow({
location: { protocol: 'https:', host: 'example.com' },
} as unknown as typeof window)
const { WebSocketClient } = await import('../websocket-manager')
const client = new WebSocketClient()
client.connect('app-auth')

View File

@ -1,6 +1,7 @@
import type { Socket } from 'socket.io-client'
import type { DebugInfo, WebSocketConfig } from '../types/websocket'
import { io } from 'socket.io-client'
import { SOCKET_URL } from '@/config'
type AckArgs = unknown[]
@ -46,21 +47,14 @@ export const emitWithAuthGuard = (
export class WebSocketClient {
private connections: Map<string, Socket> = new Map()
private connecting: Set<string> = new Set()
private config: WebSocketConfig
private readonly url: string
private readonly transports: WebSocketConfig['transports']
private readonly withCredentials?: boolean
constructor(config: WebSocketConfig = {}) {
const inferUrl = () => {
if (typeof window === 'undefined')
return 'ws://localhost:5001'
const scheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
return `${scheme}//${window.location.host}`
}
this.config = {
url: config.url || process.env.NEXT_PUBLIC_SOCKET_URL || inferUrl(),
transports: config.transports || ['websocket'],
withCredentials: config.withCredentials !== false,
...config,
}
this.url = SOCKET_URL
this.transports = config.transports || ['websocket']
this.withCredentials = config.withCredentials !== false
}
connect(appId: string): Socket {
@ -87,11 +81,11 @@ export class WebSocketClient {
withCredentials?: boolean
} = {
path: '/socket.io',
transports: this.config.transports,
withCredentials: this.config.withCredentials,
transports: this.transports,
withCredentials: this.withCredentials,
}
const socket = io(this.config.url!, socketOptions)
const socket = io(this.url, socketOptions)
this.connections.set(appId, socket)
this.setupBaseEventListeners(socket, appId)

View File

@ -1,5 +1,4 @@
export type WebSocketConfig = {
url?: string
token?: string
transports?: string[]
withCredentials?: boolean

View File

@ -51,6 +51,7 @@ const LocaleLayout = async ({
[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_SOCKET_URL]: process.env.NEXT_PUBLIC_SOCKET_URL,
[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,

View File

@ -165,6 +165,11 @@ const COOKIE_DOMAIN = getStringConfig(
DatasetAttr.DATA_PUBLIC_COOKIE_DOMAIN,
'',
).trim()
export const SOCKET_URL = getStringConfig(
process.env.NEXT_PUBLIC_SOCKET_URL,
DatasetAttr.DATA_PUBLIC_SOCKET_URL,
'ws://localhost:5001',
).trim()
export const BATCH_CONCURRENCY = getNumberConfig(
process.env.NEXT_PUBLIC_BATCH_CONCURRENCY,

View File

@ -110,6 +110,7 @@ export enum DatasetAttr {
DATA_PUBLIC_EDITION = 'data-public-edition',
DATA_PUBLIC_AMPLITUDE_API_KEY = 'data-public-amplitude-api-key',
DATA_PUBLIC_COOKIE_DOMAIN = 'data-public-cookie-domain',
DATA_PUBLIC_SOCKET_URL = 'data-public-socket-url',
DATA_PUBLIC_SUPPORT_MAIL_LOGIN = 'data-public-support-mail-login',
DATA_PUBLIC_SENTRY_DSN = 'data-public-sentry-dsn',
DATA_PUBLIC_MAINTENANCE_NOTICE = 'data-public-maintenance-notice',