From b2ff556aa65dfedad575367ae4abc25d4a0dcc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E7=91=BE?= <74231782+sj817@users.noreply.github.com> Date: Fri, 12 Sep 2025 21:56:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E4=B8=BB=E6=9C=BA=E5=9C=B0=E5=9D=80=E8=8E=B7=E5=8F=96=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=94=AF=E6=8C=81Docker=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/webui/src/helper/config.ts | 4 +- src/webui/src/utils/url.ts | 72 ++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/webui/src/helper/config.ts b/src/webui/src/helper/config.ts index c03d8535..14342aaa 100644 --- a/src/webui/src/helper/config.ts +++ b/src/webui/src/helper/config.ts @@ -7,12 +7,12 @@ import { resolve } from 'node:path'; import { deepMerge } from '../utils/object'; import { themeType } from '../types/theme'; -import { getRandomToken } from '../utils/url' +import { getRandomToken, getDefaultHost } from '../utils/url' // 限制尝试端口的次数,避免死循环 // 定义配置的类型 const WebUiConfigSchema = Type.Object({ - host: Type.String({ default: '127.0.0.1' }), + host: Type.String({ default: getDefaultHost() }), port: Type.Number({ default: 6099 }), token: Type.String({ default: getRandomToken(8) }), loginRate: Type.Number({ default: 10 }), diff --git a/src/webui/src/utils/url.ts b/src/webui/src/utils/url.ts index 8edf6e96..0df403b0 100644 --- a/src/webui/src/utils/url.ts +++ b/src/webui/src/utils/url.ts @@ -1,10 +1,58 @@ /** * @file URL工具 */ - -import { isIP } from 'node:net'; +import fs from 'node:fs' +import { isIP } from 'node:net' import { randomBytes } from 'node:crypto' +type Protocol = 'http' | 'https' + +let isDockerCached: boolean + +function hasDockerEnv () { + try { + fs.statSync('/.dockerenv') + return true + } catch { + return false + } +} + +function hasDockerCGroup () { + try { + return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker') + } catch { + return false + } +} + +const hasContainerEnv = () => { + try { + fs.statSync('/run/.containerenv') + return true + } catch { + return false + } +} + +const isDocker = () => { + if (isDockerCached === undefined) { + isDockerCached = hasContainerEnv() || hasDockerEnv() || hasDockerCGroup() + } + + return isDockerCached +} + +/** + * 获取默认host地址 + * @returns 根据环境返回合适的host地址 + * @example getDefaultHost() => '127.0.0.1' // 非Docker环境 + * @example getDefaultHost() => '0.0.0.0' // Docker环境 + */ +export const getDefaultHost = (): string => { + return isDocker() ? '0.0.0.0' : '127.0.0.1' +} + /** * 将 host(主机地址) 转换为标准格式 * @param host 主机地址 @@ -14,9 +62,9 @@ import { randomBytes } from 'node:crypto' * @example normalizeHost('2001:4860:4801:51::27') => '[2001:4860:4801:51::27]' */ export const normalizeHost = (host: string) => { - if (isIP(host) === 6) return `[${host}]`; - return host; -}; + if (isIP(host) === 6) return `[${host}]` + return host +} /** * 创建URL @@ -35,16 +83,16 @@ export const createUrl = ( search?: Record, protocol: Protocol = 'http' ) => { - const url = new URL(`${protocol}://${normalizeHost(host)}`); - url.port = port; - url.pathname = path; + const url = new URL(`${protocol}://${normalizeHost(host)}`) + url.port = port + url.pathname = path if (search) { for (const key in search) { - url.searchParams.set(key, search[key]); + url.searchParams.set(key, search[key]) } } - return url.toString(); -}; + return url.toString() +} /** * 生成随机Token @@ -53,5 +101,5 @@ export const createUrl = ( * @example getRandomToken */ export const getRandomToken = (length = 8) => { - return randomBytes(36).toString('hex').slice(0, length); + return randomBytes(36).toString('hex').slice(0, length) }