Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src-tauri/src/commands/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,9 +881,12 @@ pub fn get_system_info() -> SystemInfo {

/// 获取 Web 服务器实际监听端口
///
/// 若服务器尚未完成绑定,最多等待 5 秒后返回(0 表示超时未启动)。
/// 若服务器尚未完成绑定,最多等待 5 秒后返回(0 表示超时未启动或用户手动禁用)。
#[tauri::command]
pub async fn get_web_server_port() -> u16 {
if !crate::web_server::is_web_server_enabled() {
return 0;
}
let port = crate::web_server::get_actual_port();
if port != 0 {
return port;
Expand Down
31 changes: 20 additions & 11 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ pub fn run() {
.unwrap();
let settings_obj = settings.get("settings");

let web_server_enabled = settings_obj
.and_then(|s| s.get("webServerEnabled"))
.and_then(|v| v.as_bool())
.unwrap_or(true);

let allow_lan_access = settings_obj
.and_then(|s| s.get("allowLanAccess"))
.and_then(|v| v.as_bool())
Expand All @@ -114,19 +119,23 @@ pub fn run() {
.filter(|&p| p > 0)
.unwrap_or(web_server::DEFAULT_PORT);

web_server::set_web_server_enabled(web_server_enabled);

drop(settings);

tauri::async_runtime::spawn(async move {
web_server::start_web_server(
cfg_clone,
maa_clone,
app_handle,
ws_clone,
web_port,
allow_lan_access,
)
.await;
});
if web_server_enabled {
tauri::async_runtime::spawn(async move {
web_server::start_web_server(
cfg_clone,
maa_clone,
app_handle,
ws_clone,
web_port,
allow_lan_access,
)
.await;
});
}
}

// Windows 下移除系统标题栏(使用自定义标题栏)
Expand Down
15 changes: 14 additions & 1 deletion src-tauri/src/web_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! 基于 axum 提供 HTTP API,供浏览器客户端(本机/局域网/公网)访问。
//! 与 Tauri invoke IPC 并列,实现同一套后端状态的双通道访问。

use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
use std::sync::{Arc, OnceLock};

use axum::{
Expand Down Expand Up @@ -41,9 +41,22 @@ pub const DEFAULT_PORT: u16 = 12701;
/// 端口搜索范围上限
const MAX_PORT_ATTEMPTS: u16 = 10;

/// 全局存储 Web 服务器是否已启用(由配置控制)
static WEB_SERVER_ENABLED: AtomicBool = AtomicBool::new(true);

/// 全局存储 Web 服务器实际监听端口(供前端查询)
static ACTUAL_PORT: AtomicU16 = AtomicU16::new(0);

/// 获取 Web 服务器是否已启用
pub fn is_web_server_enabled() -> bool {
WEB_SERVER_ENABLED.load(Ordering::Relaxed)
}

/// 设置 Web 服务器启用状态
pub fn set_web_server_enabled(value: bool) {
WEB_SERVER_ENABLED.store(value, Ordering::Relaxed);
}

/// 获取 Web 服务器实际监听端口(0 表示尚未启动或启动失败)
pub fn get_actual_port() -> u16 {
ACTUAL_PORT.load(Ordering::Relaxed)
Expand Down
27 changes: 26 additions & 1 deletion src/components/settings/DebugSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Globe,
ExternalLink,
Server,
EthernetPort,
} from 'lucide-react';

import { useAppStore } from '@/stores/appStore';
Expand All @@ -33,6 +34,8 @@ export function DebugSection() {
setTcpCompatMode,
allowLanAccess,
setAllowLanAccess,
webServerEnabled,
setWebServerEnabled,
webServerPort: configuredPort,
setWebServerPort: setConfiguredPort,
} = useAppStore();
Expand Down Expand Up @@ -168,6 +171,16 @@ export function DebugSection() {
[setAllowLanAccess],
);

const handleWebServerToggle = useCallback(
(v: boolean) => {
setWebServerEnabled(v);
if (isTauri()) {
setShowRestartPrompt(true);
}
},
[setWebServerEnabled],
);

const handlePortBlur = useCallback(() => {
const parsed = parseInt(portInput, 10);
if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) {
Expand Down Expand Up @@ -366,10 +379,22 @@ export function DebugSection() {
<SwitchButton value={tcpCompatMode} onChange={(v) => setTcpCompatMode(v)} />
</div>

{/* 启用 Web 服务器 */}
<div className="flex items-center justify-between pt-4 border-t border-border">
<div className="flex items-center gap-3">
<Server className="w-5 h-5 text-accent"/>
<div>
<span className="font-medium text-text-primary">{t('debug.webServerEnabled')}</span>
<p className="text-xs text-text-muted mt-0.5">{t('debug.webServerEnabledHint')}</p>
</div>
</div>
<SwitchButton value={webServerEnabled} onChange={handleWebServerToggle}/>
</div>

{/* Web 服务器端口 */}
<div className="flex items-center justify-between pt-4 border-t border-border">
<div className="flex items-center gap-3">
<Server className="w-5 h-5 text-accent" />
<EthernetPort className="w-5 h-5 text-accent"/>
<div>
<span className="font-medium text-text-primary">{t('debug.webServerPort')}</span>
<p className="text-xs text-text-muted mt-0.5">{t('debug.webServerPortHint')}</p>
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,9 @@ export default {
tcpCompatMode: 'Communication Compat Mode',
tcpCompatModeHint:
'Try enabling this if the app crashes immediately after starting tasks. Only use in this case, as it may reduce performance',
webServerEnabled: 'Enable Web Server',
webServerEnabledHint:
'When disabled, the built-in web server will not start (restart required)',
webServerPort: 'Web Server Port',
webServerPortHint: 'Custom Web server listening port (default 12701, restart required)',
allowLanAccess: 'Allow LAN Access',
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/ja-JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,9 @@ export default {
tcpCompatMode: '通信互換モード',
tcpCompatModeHint:
'タスク開始後にアプリがすぐにクラッシュする場合は有効にしてください。この場合のみ使用し、それ以外は性能に影響します',
webServerEnabled: 'Web サーバーを有効化',
webServerEnabledHint:
'無効にすると内蔵 Web サーバーは起動しません(再起動後に反映)',
webServerPort: 'Web サーバーポート',
webServerPortHint:
'Web サーバーのリッスンポートをカスタマイズ(デフォルト 12701、再起動後に反映)',
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ export default {
tcpCompatMode: '통신 호환 모드',
tcpCompatModeHint:
'작업 시작 후 앱이 즉시 충돌하면 활성화해 보세요. 이 경우에만 사용하세요, 성능에 영향을 줄 수 있습니다',
webServerEnabled: 'Web 서버 활성화',
webServerEnabledHint:
'비활성화하면 내장 Web 서버가 시작되지 않습니다 (재시작 필요)',
webServerPort: 'Web 서버 포트',
webServerPortHint: 'Web 서버 수신 포트를 사용자 지정합니다 (기본값 12701, 재시작 필요)',
allowLanAccess: 'LAN 접근 허용',
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ export default {
saveDrawHint: '保存识别和操作的调试图像到日志目录(重启软件后自动关闭)',
tcpCompatMode: '通信兼容模式',
tcpCompatModeHint: '若启动任务后软件立即闪退,可尝试开启。仅限此情况使用,否则会影响运行效率',
webServerEnabled: '启用 Web 服务',
webServerEnabledHint: '关闭后内置 Web 服务器将不会启动(重启生效)',
webServerPort: 'Web 服务端口',
webServerPortHint: '自定义 Web 服务器监听端口(默认 12701,重启生效)',
allowLanAccess: '允许局域网访问',
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ export default {
saveDrawHint: '儲存識別和操作的除錯圖像到日誌目錄(重啟軟體後自動關閉)',
tcpCompatMode: '通訊相容模式',
tcpCompatModeHint: '若啟動任務後軟體立即閃退,可嘗試開啟。僅限此情況使用,否則會影響運行效率',
webServerEnabled: '啟用 Web 服務',
webServerEnabledHint: '關閉後內建 Web 伺服器將不會啟動(重啟生效)',
webServerPort: 'Web 服務連接埠',
webServerPortHint: '自訂 Web 伺服器監聽連接埠(預設 12701,重啟生效)',
allowLanAccess: '允許區域網路存取',
Expand Down
55 changes: 31 additions & 24 deletions src/stores/appStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import i18n, { getInterfaceLangKey, setLanguage as setI18nLanguage } from '@/i18n';
import { saveConfig } from '@/services/configService';
import { maaService } from '@/services/maaService';
import i18n, {getInterfaceLangKey, setLanguage as setI18nLanguage} from '@/i18n';
import {saveConfig} from '@/services/configService';
import {maaService} from '@/services/maaService';
import {
type AccentColor,
applyTheme,
Expand All @@ -10,15 +10,15 @@ import {
resolveThemeMode,
unregisterCustomAccent,
} from '@/themes';
import type { MxuConfig, RecentlyClosedInstance, LegacyActionConfig } from '@/types/config';
import type {LegacyActionConfig, MxuConfig, RecentlyClosedInstance} from '@/types/config';
import {
DEFAULT_MAX_LOGS_PER_INSTANCE,
clampAddTaskPanelHeight,
DEFAULT_MAX_LOGS_PER_INSTANCE,
defaultAddTaskPanelHeight,
defaultMirrorChyanSettings,
normalizeAddTaskPanelHeight,
defaultScreenshotFrameRate,
defaultWindowSize,
normalizeAddTaskPanelHeight,
} from '@/types/config';
import type {
ActionConfig,
Expand All @@ -28,37 +28,37 @@ import type {
ProjectInterface,
SelectedTask,
} from '@/types/interface';
import type { ConnectionStatus, TaskStatus } from '@/types/maa';
import { getMxuSpecialTask, isMxuSpecialTask, MXU_SPECIAL_TASKS } from '@/types/specialTasks';
import { decryptCdk, encryptCdk } from '@/utils/cdkCrypto';
import { loggers } from '@/utils/logger';
import { findSwitchCase } from '@/utils/optionHelpers';
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';

import { logToStdout, pushLogToBackend, clearLogsOnBackend } from '@/utils/logStdout';
import type {ConnectionStatus, TaskStatus} from '@/types/maa';
import {getMxuSpecialTask, isMxuSpecialTask, MXU_SPECIAL_TASKS} from '@/types/specialTasks';
import {decryptCdk, encryptCdk} from '@/utils/cdkCrypto';
import {loggers} from '@/utils/logger';
import {findSwitchCase} from '@/utils/optionHelpers';
import {create} from 'zustand';
import {subscribeWithSelector} from 'zustand/middleware';

import {clearLogsOnBackend, logToStdout, pushLogToBackend} from '@/utils/logStdout';
import {
loadWebUIAppearance,
patchWebUIAppearance,
cacheBackendAppearance,
cacheBackendLayout,
getBackendAppearance,
getBackendLayout,
loadWebUIAppearance,
loadWebUILayout,
patchWebUIAppearance,
patchWebUILayout,
cacheBackendLayout,
getBackendLayout,
} from '@/services/appearanceStorage';
import { isTauri } from '@/utils/paths';
import {isTauri} from '@/utils/paths';
import {
generateId,
initializeAllOptionValues,
convertPresetOptionValue,
generateId,
getCurrentControllerAndResource,
initializeAllOptionValues,
isTaskCompatible,
sanitizeOptionValues,
} from './helpers';
import { persistRuntimeLogs } from '@/utils/runtimeLogPersistence';
import {persistRuntimeLogs} from '@/utils/runtimeLogPersistence';
// 从独立模块导入类型和辅助函数
import type { AppState, LogEntry, TaskRunStatus } from './types';
import type {AppState, LogEntry, TaskRunStatus} from './types';

/** 向后兼容:将旧版单个 preAction 迁移为 preActions 数组 */
function migratePreActions(inst: {
Expand Down Expand Up @@ -1206,6 +1206,7 @@ export const useAppStore = create<AppState>()(
devMode: config.settings.devMode ?? false,
tcpCompatMode: config.settings.tcpCompatMode ?? false,
allowLanAccess: config.settings.allowLanAccess ?? false,
webServerEnabled: config.settings.webServerEnabled ?? true,
webServerPort: config.settings.webServerPort ?? 12701,
autoStartInstanceId: config.settings.autoStartInstanceId,
autoRunOnLaunch: config.settings.autoRunOnLaunch ?? false,
Expand Down Expand Up @@ -1668,6 +1669,10 @@ export const useAppStore = create<AppState>()(
allowLanAccess: false,
setAllowLanAccess: (enabled) => set({ allowLanAccess: enabled }),

// Web 服务器启用开关(默认 true,需重启生效)
webServerEnabled: true,
setWebServerEnabled: (enabled) => set({webServerEnabled: enabled}),

// Web 服务器端口(默认 12701,需重启生效)
webServerPort: 12701,
setWebServerPort: (port) => set({ webServerPort: port }),
Expand Down Expand Up @@ -2084,6 +2089,7 @@ function generateConfig(): MxuConfig {
devMode: state.devMode,
tcpCompatMode: state.tcpCompatMode,
allowLanAccess: state.allowLanAccess,
webServerEnabled: state.webServerEnabled,
webServerPort: state.webServerPort,
autoStartInstanceId: state.autoStartInstanceId,
autoRunOnLaunch: state.autoRunOnLaunch,
Expand Down Expand Up @@ -2156,6 +2162,7 @@ useAppStore.subscribe(
devMode: state.devMode,
tcpCompatMode: state.tcpCompatMode,
allowLanAccess: state.allowLanAccess,
webServerEnabled: state.webServerEnabled,
webServerPort: state.webServerPort,
autoStartInstanceId: state.autoStartInstanceId,
autoRunOnLaunch: state.autoRunOnLaunch,
Expand Down
4 changes: 4 additions & 0 deletions src/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ export interface AppState {
tcpCompatMode: boolean;
setTcpCompatMode: (enabled: boolean) => void;

/** Web 服务器是否启用(默认 true,重启生效) */
webServerEnabled: boolean;
setWebServerEnabled: (enabled: boolean) => void;

/** Web UI 允许局域网访问(绑定 0.0.0.0,重启生效) */
allowLanAccess: boolean;
setAllowLanAccess: (enabled: boolean) => void;
Expand Down
5 changes: 3 additions & 2 deletions src/types/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// MXU 配置文件结构 (mxu.json)

import type { OptionValue, ActionConfig } from './interface';
import type { AccentColor, CustomAccent } from '@/themes/types';
import type {ActionConfig, OptionValue} from './interface';
import type {AccentColor, CustomAccent} from '@/themes/types';

export const DEFAULT_MAX_LOGS_PER_INSTANCE = 500;

Expand Down Expand Up @@ -153,6 +153,7 @@ export interface AppSettings {
onboardingCompleted?: boolean; // 新用户引导是否已完成
hotkeys?: HotkeySettings; // 快捷键设置
tcpCompatMode?: boolean; // 通信兼容模式,强制使用 TCP 而非 IPC
webServerEnabled?: boolean; // Web 服务器是否启用(默认 true,重启生效)
allowLanAccess?: boolean; // Web UI 允许局域网访问(绑定 0.0.0.0,重启生效)
webServerPort?: number; // Web 服务器监听端口(默认 12701,重启生效)
minimizeToTray?: boolean; // 关闭时最小化到托盘(默认 false)
Expand Down
Loading