@@ -28,8 +28,11 @@ import SqliteRepository from './db/sqliteRepository.js';
2828import { registerMigrationHandlers } from './db/migration.js' ;
2929
3030// ================================================================
31- // .env 文件加载(Electron 主进程由 tsc 编译,不经过 Vite,
32- // 需手动解析 .env 文件注入 process.env,使 VITE_* 变量可用)
31+ // 环境变量加载
32+ // ================================================================
33+ // 开发模式:从 .env 文件加载(与 Vite dev server 保持一致)
34+ // 生产模式:从 Vite 构建时生成的 build-config.json 读取
35+ // (.env.production 仅用于 Vite 构建阶段,不打包进安装包)
3336// ================================================================
3437
3538/**
@@ -64,15 +67,39 @@ function loadEnvFile(filePath: string, overrideKeys: Set<string>): void {
6467 }
6568}
6669
67- // 按 Vite 约定分层加载:.env(基础)→ .env.<mode>(覆盖)
68- // electron:dev 使用 --mode test,electron:build 使用 production
69- {
70- const mode = process . env . NODE_ENV === 'development' ? 'test' : 'production' ;
70+ /**
71+ * 从 Vite 构建时生成的 build-config.json 读取环境变量
72+ * 该文件由 vite.config.ts 中的 electronBuildConfigPlugin 在 vite build 时生成,
73+ * 位于 dist-electron/build-config.json,随 asar 一起打包。
74+ */
75+ function loadBuildConfig ( ) : void {
76+ try {
77+ // __dirname 在编译后为 dist-electron/electron/,build-config.json 在 dist-electron/
78+ const configPath = path . resolve ( __dirname , '..' , 'build-config.json' ) ;
79+ if ( ! existsSync ( configPath ) ) return ;
80+ const raw = readFileSync ( configPath , 'utf-8' ) ;
81+ const config = JSON . parse ( raw ) as Record < string , string > ;
82+ for ( const [ key , value ] of Object . entries ( config ) ) {
83+ // 不覆盖系统环境变量(如 cross-env 设置的值)
84+ if ( value && ! ( key in process . env ) ) {
85+ process . env [ key ] = value ;
86+ }
87+ }
88+ } catch {
89+ // build-config.json 加载失败不阻塞启动
90+ }
91+ }
92+
93+ if ( isDevMode ( ) ) {
94+ // 开发模式:从 .env 文件加载(electron:dev 由 cross-env 设置 NODE_ENV=development)
7195 // __dirname 在编译后为 dist-electron/electron/,需回退到 client/ 根目录
7296 const clientRoot = path . resolve ( __dirname , '..' , '..' ) ;
7397 const envKeys = new Set < string > ( ) ;
7498 loadEnvFile ( path . join ( clientRoot , '.env' ) , envKeys ) ;
75- loadEnvFile ( path . join ( clientRoot , `.env.${ mode } ` ) , envKeys ) ;
99+ loadEnvFile ( path . join ( clientRoot , '.env.test' ) , envKeys ) ;
100+ } else {
101+ // 生产模式:从构建时生成的 build-config.json 读取(不依赖 .env 文件)
102+ loadBuildConfig ( ) ;
76103}
77104
78105// 仅开发模式禁用 Electron 安全警告,生产环境保留
@@ -140,24 +167,32 @@ if (!gotTheLock) {
140167 logger . info ( 'App ready' ) ;
141168
142169 // ================================================================
143- // SEC-005: CSP 安全策略注入
170+ // SEC-005: CSP 安全策略注入(动态跟踪运行时网关 URL)
144171 // ================================================================
145172 const isDev = isDevMode ( ) ;
146173
147174 try {
148- // 动态构建 connect-src:除固定域名外,追加配置的 AI 网关和 API 地址
149- const configuredGateway = process . env . VITE_AI_GATEWAY_URL || '' ;
150- const configuredApi = process . env . VITE_API_BASE_URL || '' ;
151- const extraOrigins = new Set < string > ( ) ;
152- if ( configuredGateway && configuredGateway !== 'https://entropydecrease.com' ) {
153- extraOrigins . add ( configuredGateway ) ;
154- }
155- if ( configuredApi && configuredApi !== 'https://entropydecrease.com' ) {
156- extraOrigins . add ( configuredApi ) ;
175+ /**
176+ * 动态构建 connect-src 白名单
177+ * 每次请求时重新计算,确保运行时网关 URL 变更(如用户通过设置页修改)能及时生效
178+ */
179+ function buildExtraConnectSrc ( ) : string {
180+ const extraOrigins = new Set < string > ( ) ;
181+ // 当前运行时网关 URL(可能已被用户通过 IPC 修改)
182+ const currentGateway = gatewayUrl ( ) ;
183+ if ( currentGateway && currentGateway !== 'https://entropydecrease.com' ) {
184+ extraOrigins . add ( currentGateway ) ;
185+ }
186+ // 环境变量中的 API 地址
187+ const configuredApi = process . env . VITE_API_BASE_URL || '' ;
188+ if ( configuredApi && configuredApi !== 'https://entropydecrease.com' ) {
189+ extraOrigins . add ( configuredApi ) ;
190+ }
191+ return extraOrigins . size > 0 ? ` ${ [ ...extraOrigins ] . join ( ' ' ) } ` : '' ;
157192 }
158- const extraConnectSrc = extraOrigins . size > 0 ? ` ${ [ ...extraOrigins ] . join ( ' ' ) } ` : '' ;
159193
160194 session . defaultSession . webRequest . onHeadersReceived ( ( details , callback ) => {
195+ const extraConnectSrc = buildExtraConnectSrc ( ) ;
161196 // 开发环境:允许 unsafe-inline/unsafe-eval(Vite HMR 需要)
162197 // 生产环境:禁止 unsafe-eval,保留 unsafe-inline(Tailwind 运行时需要)
163198 const csp = isDev
@@ -171,7 +206,7 @@ if (!gotTheLock) {
171206 } ,
172207 } ) ;
173208 } ) ;
174- logger . info ( `[SEC] CSP policy injected (${ isDev ? 'development' : 'production' } mode)` ) ;
209+ logger . info ( `[SEC] CSP policy injected (${ isDev ? 'development' : 'production' } mode, dynamic gateway tracking enabled )` ) ;
175210 } catch ( err ) {
176211 // CSP 注入失败时记录错误但不阻塞启动
177212 logger . error ( '[SEC] Failed to inject CSP policy' , err ) ;
0 commit comments