Skip to content

Commit 98537f3

Browse files
committed
fix: align ip-based language and timezone resolution with runtime fallback
1 parent 459237b commit 98537f3

5 files changed

Lines changed: 146 additions & 112 deletions

File tree

plugins/pages/create-window/src/api/index.ts

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type {
1313
CreateTemplateRequest,
1414
CreateTemplateResponse,
1515
} from './index.types';
16-
import { resolveFingerprintConfig } from '../utils/resolve-fingerprint-config';
1716

1817
// 导出类型
1918
export * from './index.types';
@@ -344,36 +343,7 @@ export async function createEnvironment(
344343
config: WindowConfig,
345344
options?: CreateEnvironmentOptions
346345
): Promise<CreateEnvironmentResponse> {
347-
// 获取代理信息(用于解析指纹配置)
348-
const { listProxies } = await import('../../../environment-manager/src/api');
349-
let proxyConfig = null;
350-
351-
if (options?.proxyUuid) {
352-
const proxies = await listProxies();
353-
const proxy = proxies.find(p => p.uuid === options.proxyUuid);
354-
if (proxy) {
355-
proxyConfig = {
356-
proxy_type: proxy.proxy_type,
357-
host: proxy.host,
358-
port: proxy.port,
359-
username: proxy.username || undefined,
360-
password: proxy.password || undefined,
361-
};
362-
}
363-
}
364-
365-
// 解析指纹配置
366-
const resolvedBasicSettings = await resolveFingerprintConfig(
367-
config.basicSettings,
368-
proxyConfig
369-
);
370-
371-
const resolvedConfig = {
372-
...config,
373-
basicSettings: resolvedBasicSettings,
374-
};
375-
376-
const requestData = transformWindowConfigToRequest(resolvedConfig, options);
346+
const requestData = transformWindowConfigToRequest(config, options);
377347
const result = await post<CreateEnvironmentResponse>(
378348
API_ENDPOINTS.CREATE_ENVIRONMENT,
379349
requestData
@@ -394,41 +364,8 @@ export async function batchCreateEnvironments(
394364
proxyUuids: string[] | undefined, // 代理 UUID 数组,按顺序分配给环境
395365
options?: CreateEnvironmentOptions
396366
): Promise<CreateEnvironmentResponse[]> {
397-
// 获取代理信息(用于解析指纹配置)
398-
const { listProxies } = await import('../../../environment-manager/src/api');
399-
const proxies = proxyUuids && proxyUuids.length > 0 ? await listProxies() : [];
400-
401-
// 解析每个配置的指纹配置
402-
const resolvedConfigs = await Promise.all(
403-
configs.map(async (config, index) => {
404-
// 获取当前环境对应的代理
405-
const proxyUuid = proxyUuids && index < proxyUuids.length ? proxyUuids[index] : undefined;
406-
const proxy = proxyUuid ? proxies.find(p => p.uuid === proxyUuid) : null;
407-
408-
// 构建代理配置对象
409-
const proxyConfig = proxy ? {
410-
proxy_type: proxy.proxy_type,
411-
host: proxy.host,
412-
port: proxy.port,
413-
username: proxy.username || undefined,
414-
password: proxy.password || undefined,
415-
} : null;
416-
417-
// 解析指纹配置
418-
const resolvedBasicSettings = await resolveFingerprintConfig(
419-
config.basicSettings,
420-
proxyConfig
421-
);
422-
423-
return {
424-
...config,
425-
basicSettings: resolvedBasicSettings,
426-
};
427-
})
428-
);
429-
430367
// 构建批量创建请求
431-
const environments: CreateEnvironmentRequest[] = resolvedConfigs.map((config, index) => {
368+
const environments: CreateEnvironmentRequest[] = configs.map((config, index) => {
432369
const request = transformWindowConfigToRequest(config, {
433370
// 统一设置分组和标签(批量创建时这些应该相同)
434371
groupUuid: options?.groupUuid,

plugins/pages/create-window/src/hooks/use-create-window.ts

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import {
1010
updateTemplate,
1111
transformWindowConfigToRequest,
1212
} from '../api';
13-
import {
14-
updateEnvironment,
15-
listProxies,
16-
listAccounts,
17-
type ProxyItem,
18-
type AccountItem,
19-
} from '../../../environment-manager/src/api';
13+
import {
14+
updateEnvironment,
15+
listAccounts,
16+
type ProxyItem,
17+
type AccountItem,
18+
} from '../../../environment-manager/src/api';
2019
import type { GroupItem, TagItem } from '../../../environment-manager/src/api';
2120
// @ts-ignore - Cross-plugin import
2221
import { useSettingsDialogStore, useRefreshStore } from '../../../../services/store/src';
@@ -373,43 +372,15 @@ export function useCreateWindow(
373372
try {
374373
// 编辑模式:更新环境
375374
if (editUuid) {
376-
// 获取代理信息(用于解析指纹配置)
377-
const proxyUuid = windowConfig.windowInfo.proxyUuids.length > 0
378-
? windowConfig.windowInfo.proxyUuids[0]
379-
: undefined;
380-
381-
let proxyConfig = null;
382-
if (proxyUuid) {
383-
const proxies = await listProxies();
384-
const proxy = proxies.find(p => p.uuid === proxyUuid);
385-
if (proxy) {
386-
proxyConfig = {
387-
proxy_type: proxy.proxy_type,
388-
host: proxy.host,
389-
port: proxy.port,
390-
username: proxy.username || undefined,
391-
password: proxy.password || undefined,
392-
};
393-
}
394-
}
395-
396-
// 解析指纹配置
397-
const { resolveFingerprintConfig } = await import('../utils/resolve-fingerprint-config');
398-
const resolvedBasicSettings = await resolveFingerprintConfig(
399-
windowConfig.basicSettings,
400-
proxyConfig
401-
);
402-
403-
const resolvedConfig = {
404-
...windowConfig,
405-
basicSettings: resolvedBasicSettings,
406-
};
407-
408-
const request = transformWindowConfigToRequest(resolvedConfig, {
409-
groupUuid: selectedGroupUuid,
410-
tagUuids: selectedTagUuids.length > 0 ? selectedTagUuids : undefined,
411-
accountUuids:
412-
windowConfig.windowInfo.accountUuids.length > 0
375+
const proxyUuid = windowConfig.windowInfo.proxyUuids.length > 0
376+
? windowConfig.windowInfo.proxyUuids[0]
377+
: undefined;
378+
379+
const request = transformWindowConfigToRequest(windowConfig, {
380+
groupUuid: selectedGroupUuid,
381+
tagUuids: selectedTagUuids.length > 0 ? selectedTagUuids : undefined,
382+
accountUuids:
383+
windowConfig.windowInfo.accountUuids.length > 0
413384
? windowConfig.windowInfo.accountUuids
414385
: undefined,
415386
proxyUuid, // 编辑模式使用第一个代理
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! 语言自动检测模块
2+
3+
use super::types::{ProxyConfig, TIMEZONE_DETECTION_TIMEOUT_SECS};
4+
5+
/// 检测语言
6+
///
7+
/// # 逻辑
8+
/// - 有代理配置:先用代理检测,失败则直连检测
9+
/// - 无代理配置:使用系统代理(reqwest 自动检测)
10+
///
11+
/// # 返回
12+
/// - `Some(language)`: 检测成功
13+
/// - `None`: 所有检测都失败
14+
pub async fn detect_language(proxy: Option<&ProxyConfig>) -> Option<String> {
15+
if let Some(proxy_cfg) = proxy {
16+
detect_with_proxy(proxy_cfg).await
17+
} else {
18+
detect_with_system_proxy().await
19+
}
20+
}
21+
22+
async fn detect_with_proxy(proxy_cfg: &ProxyConfig) -> Option<String> {
23+
let infra_proxy_cfg = proxy_cfg.to_infrastructure_proxy_config();
24+
let proxy_client = match crate::infrastructure::proxy::client::build_proxy_client(
25+
&infra_proxy_cfg,
26+
Some(TIMEZONE_DETECTION_TIMEOUT_SECS),
27+
) {
28+
Ok(client) => client,
29+
Err(_) => {
30+
return detect_with_direct_connection().await;
31+
}
32+
};
33+
34+
let proxy_result = crate::infrastructure::proxy::detector::detect_ip_with_timeout(
35+
proxy_client,
36+
TIMEZONE_DETECTION_TIMEOUT_SECS,
37+
)
38+
.await;
39+
40+
if proxy_result.success {
41+
let ip_info = proxy_result.ip_info.as_ref()?;
42+
infer_language_from_country_code(ip_info.country_code.as_str())
43+
} else {
44+
detect_with_direct_connection().await
45+
}
46+
}
47+
48+
async fn detect_with_system_proxy() -> Option<String> {
49+
let result = crate::infrastructure::proxy::detector::detect_with_system_proxy(
50+
TIMEZONE_DETECTION_TIMEOUT_SECS,
51+
)
52+
.await;
53+
54+
if result.success {
55+
let ip_info = result.ip_info.as_ref()?;
56+
infer_language_from_country_code(ip_info.country_code.as_str())
57+
} else {
58+
None
59+
}
60+
}
61+
62+
async fn detect_with_direct_connection() -> Option<String> {
63+
let local_result = crate::infrastructure::proxy::detector::detect_direct_ip_with_timeout(
64+
TIMEZONE_DETECTION_TIMEOUT_SECS,
65+
)
66+
.await;
67+
68+
if local_result.success {
69+
let ip_info = local_result.ip_info.as_ref()?;
70+
infer_language_from_country_code(ip_info.country_code.as_str())
71+
} else {
72+
None
73+
}
74+
}
75+
76+
fn infer_language_from_country_code(country_code: &str) -> Option<String> {
77+
let normalized = country_code.trim().to_ascii_uppercase();
78+
if normalized.is_empty() {
79+
return None;
80+
}
81+
82+
let language = match normalized.as_str() {
83+
"CN" => "zh-CN",
84+
"TW" => "zh-TW",
85+
"HK" => "zh-HK",
86+
"US" => "en-US",
87+
"GB" => "en-GB",
88+
"CA" => "en-CA",
89+
"AU" => "en-AU",
90+
"JP" => "ja-JP",
91+
"KR" => "ko-KR",
92+
"FR" => "fr-FR",
93+
"DE" => "de-DE",
94+
"ES" => "es-ES",
95+
"IT" => "it-IT",
96+
"RU" => "ru-RU",
97+
"BR" => "pt-BR",
98+
"MX" => "es-MX",
99+
"IN" => "en-IN",
100+
"SG" => "en-SG",
101+
_ => "en-US",
102+
};
103+
104+
Some(language.to_string())
105+
}

src-tauri/src/services/environment/kernel/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::fs;
88

99
pub mod downloader;
1010
pub mod extension;
11+
pub mod language;
1112
mod runtime_bridge;
1213
mod state;
1314
pub mod timezone;

src-tauri/src/services/environment/kernel/runtime_bridge.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::infrastructure::runtime::{
1313
};
1414

1515
use super::extension;
16+
use super::language;
1617
use super::timezone;
1718
use super::types::{
1819
AccountInfo, BatchLaunchRequest, BatchLaunchResult, CdpEndpointResponse, KernelStatusEmitter,
@@ -275,8 +276,26 @@ async fn prepare_start_request(
275276
);
276277

277278
if let Some(ref mut config) = fingerprint_config {
278-
if let Some(detected_timezone) = timezone::detect_timezone(proxy.as_ref()).await {
279-
config.timezone = Some(detected_timezone);
279+
let should_detect_language = match config.language.as_deref().map(str::trim) {
280+
Some(language) => language.is_empty() || language.eq_ignore_ascii_case("ip"),
281+
None => true,
282+
};
283+
284+
if should_detect_language {
285+
if let Some(detected_language) = language::detect_language(proxy.as_ref()).await {
286+
config.language = Some(detected_language);
287+
}
288+
}
289+
290+
let should_detect_timezone = match config.timezone.as_deref().map(str::trim) {
291+
Some(timezone) => timezone.is_empty() || timezone.eq_ignore_ascii_case("ip"),
292+
None => true,
293+
};
294+
295+
if should_detect_timezone {
296+
if let Some(detected_timezone) = timezone::detect_timezone(proxy.as_ref()).await {
297+
config.timezone = Some(detected_timezone);
298+
}
280299
}
281300
}
282301

@@ -344,7 +363,8 @@ async fn prepare_start_request(
344363
env_uuid: env_id,
345364
user_data_dir: user_data_dir.to_string_lossy().to_string(),
346365
cookies: cookies.map(|items| {
347-
items.into_iter()
366+
items
367+
.into_iter()
348368
.map(|item| CookieGroup {
349369
site: item.site,
350370
cookie_text: item.cookie_text,

0 commit comments

Comments
 (0)