use crate::services::{persist, tunnel};
use crate::state::RELAYS;
use crate::state::{ActiveTab, AppState, TunnelPhase};
pub(crate) fn primary_action(state: &mut AppState) {
match state.tab {
ActiveTab::Host => toggle_host(state),
ActiveTab::Join => toggle_join(state),
ActiveTab::Relay => apply_relay(state),
}
}
pub(crate) fn toggle_host(state: &mut AppState) {
match state.phase {
TunnelPhase::Idle => {
let port: u16 = match state.host_port.value.parse() {
Ok(p) => p,
Err(_) => {
state.add_log("端口格式错误");
return;
}
};
let password = if state.host_password.value.is_empty() {
None
} else {
Some(state.host_password.value.clone())
};
let key_path = match persist::default_key_path() {
Ok(path) => path,
Err(e) => {
state.add_log(&format!("密钥路径获取失败: {e}"));
return;
}
};
let secret_key = match persist::load_or_generate_key(&key_path) {
Ok(k) => k,
Err(e) => {
state.add_log(&format!("密钥加载失败: {e}"));
return;
}
};
let custom_relay = if state.relay_idx == 1 {
Some(state.relay_url.value.as_str())
} else {
None
};
let relay_url = match persist::resolve_relay_url(&state.ctx.profile, custom_relay) {
Ok(r) => r,
Err(e) => {
state.add_log(&format!("中继配置错误: {e}"));
return;
}
};
state.phase = TunnelPhase::Starting;
state.active_mode = Some(ActiveTab::Host);
state.quit_pressed_at = None;
state.add_log(&format!("正在启动 host 隧道 (端口 {port})..."));
state.ctx.startup_handle = Some(tunnel::spawn_host(
port,
secret_key,
relay_url,
password,
state.ctx.app_tx.clone(),
));
}
TunnelPhase::Active if state.active_mode == Some(ActiveTab::Host) => {
stop_tunnel(state);
}
_ => {
state.add_log("隧道运行中,请先停止当前隧道");
}
}
}
pub(crate) fn toggle_join(state: &mut AppState) {
match state.phase {
TunnelPhase::Idle => {
if state.join_ticket.value.is_empty() {
state.add_log("请先输入票据");
return;
}
let port: u16 = match state.join_port.value.parse() {
Ok(p) => p,
Err(_) => {
state.add_log("端口格式错误");
return;
}
};
let password = if state.join_password.value.is_empty() {
None
} else {
Some(state.join_password.value.clone())
};
state.phase = TunnelPhase::Starting;
state.active_mode = Some(ActiveTab::Join);
state.quit_pressed_at = None;
state.add_log("正在连接...");
state.ctx.startup_handle = Some(tunnel::spawn_join(
&state.join_ticket.value,
port,
password,
state.ctx.app_tx.clone(),
));
}
TunnelPhase::Active if state.active_mode == Some(ActiveTab::Join) => {
stop_tunnel(state);
}
_ => {
state.add_log("隧道运行中,请先停止当前隧道");
}
}
}
pub(crate) fn apply_relay(state: &mut AppState) {
if state.phase != TunnelPhase::Idle {
state.add_log("隧道运行中,无法切换中继");
return;
}
let selected = match state.relay_state.selected() {
Some(idx) => idx,
None => state.relay_idx,
};
match selected {
0 => {
if selected == state.relay_idx {
state.add_log(&format!(
"中继保持不变: {}",
RELAYS.get(state.relay_idx).unwrap_or(&"未知")
));
return;
}
state.ctx.profile.relay.custom = false;
if let Err(e) = persist::save_profile(&state.ctx.profile) {
state.add_log(&format!("重置中继失败: {e}"));
return;
}
}
1 => {
let url = state.relay_url.value.trim().to_string();
if url.is_empty() {
state.add_log("请先输入自建中继 URL");
return;
}
if let Err(e) = persist::resolve_relay_url(&state.ctx.profile, Some(&url)) {
state.add_log(&format!("保存失败: {e}"));
return;
}
state.ctx.profile.relay.custom = true;
state.ctx.profile.relay.url = Some(url);
if let Err(e) = persist::save_profile(&state.ctx.profile) {
state.add_log(&format!("保存失败: {e}"));
return;
}
}
_ => {
if selected == state.relay_idx {
state.add_log(&format!(
"中继保持不变: {}",
RELAYS.get(state.relay_idx).unwrap_or(&"未知")
));
return;
}
}
}
let selected = selected.min(RELAYS.len().saturating_sub(1));
state.relay_idx = selected;
state.add_log(&format!(
"中继已切换到 {}",
RELAYS.get(state.relay_idx).unwrap_or(&"未知")
));
}
pub(crate) fn stop_tunnel(state: &mut AppState) {
if let Some(handle) = state.ctx.event_forwarder.take() {
handle.abort();
}
if let Some(t) = state.ctx.tunnel.take() {
state.phase = TunnelPhase::Stopping;
state.quit_pressed_at = None;
state.add_log("正在关闭隧道...");
tunnel::spawn_close(t, state.ctx.app_tx.clone());
}
}