mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor(proxy): 简化 HMAC 认证、移除明文 HTTP 代理并优化 setup 流程
- HMAC 签名移除 node_id,仅使用 timestamp,消除重注册时的认证竞态问题 - 删除 plain HTTP forward proxy(plain.rs),仅保留 CONNECT 隧道和 delegate,非支持方法返回 405 - 提取共享 BoxBody 类型和 empty_box_body() 到 proxy/mod.rs - CLI 解析重构为 clap 原生 subcommand,启用 subcommand_negates_reqs - setup 向导返回 SetupOutcome 枚举,支持保存后自动启动 proxy - setup TUI 增加未保存变更的退出确认(pending_quit) - validate_target 改为 async,使用 tokio::net::lookup_host 避免阻塞 DNS - 显式初始化 rustls ring CryptoProvider - ConfigFile 新增 inject_env_override() 用于 setup 后重载配置 - Python 端 HMAC 签名同步移除 node_id,缓存时间桶从 120s 调整为 240s
This commit is contained in:
@@ -2,4 +2,4 @@ pub(crate) mod service;
|
||||
mod tui;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
pub use self::tui::run;
|
||||
pub use self::tui::{run, SetupOutcome};
|
||||
|
||||
@@ -93,9 +93,11 @@ pub fn install_service(config_path: &Path) -> anyhow::Result<()> {
|
||||
|
||||
eprintln!();
|
||||
eprintln!(" Commands:");
|
||||
eprintln!(" sudo systemctl status {} # status", SERVICE_NAME);
|
||||
eprintln!(" sudo systemctl restart {} # restart", SERVICE_NAME);
|
||||
eprintln!(" sudo journalctl -u {} -f # logs", SERVICE_NAME);
|
||||
eprintln!(" aether-proxy status # service status");
|
||||
eprintln!(" aether-proxy logs # tail logs");
|
||||
eprintln!(" sudo aether-proxy restart # restart");
|
||||
eprintln!(" sudo aether-proxy stop # stop");
|
||||
eprintln!(" sudo aether-proxy uninstall # remove service");
|
||||
eprintln!();
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -21,6 +21,16 @@ use ratatui::Terminal;
|
||||
|
||||
use crate::config::ConfigFile;
|
||||
|
||||
/// Outcome of the setup wizard, returned to the caller.
|
||||
pub enum SetupOutcome {
|
||||
/// Config saved; systemd service installed and started.
|
||||
ServiceInstalled,
|
||||
/// Config saved; no service — caller should start the proxy directly.
|
||||
ReadyToRun(PathBuf),
|
||||
/// User quit without saving.
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
/// Column width reserved for the field label (chars).
|
||||
const LABEL_WIDTH: usize = 22;
|
||||
|
||||
@@ -63,6 +73,7 @@ struct App {
|
||||
message: Option<(String, Instant, bool)>, // (text, when, is_error)
|
||||
scroll_offset: usize,
|
||||
saved_once: bool,
|
||||
pending_quit: bool, // true after first q/Esc with unsaved changes
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -148,6 +159,7 @@ impl App {
|
||||
message: None,
|
||||
scroll_offset: 0,
|
||||
saved_once: false,
|
||||
pending_quit: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,9 +247,9 @@ impl App {
|
||||
|
||||
/// Returns `true` when the app should exit.
|
||||
fn handle_key(&mut self, key: KeyEvent) -> bool {
|
||||
// Expire old messages
|
||||
// Expire old messages (but keep quit-confirmation messages alive)
|
||||
if let Some((_, when, _)) = &self.message {
|
||||
if when.elapsed() > Duration::from_secs(4) {
|
||||
if !self.pending_quit && when.elapsed() > Duration::from_secs(4) {
|
||||
self.message = None;
|
||||
}
|
||||
}
|
||||
@@ -252,8 +264,29 @@ impl App {
|
||||
}
|
||||
|
||||
fn handle_normal(&mut self, key: KeyEvent) -> bool {
|
||||
// ── Quit handling (with unsaved-changes confirmation) ─────────
|
||||
let is_quit_key = matches!(key.code, KeyCode::Char('q') | KeyCode::Esc);
|
||||
|
||||
if is_quit_key {
|
||||
if !self.modified || self.pending_quit {
|
||||
return true;
|
||||
}
|
||||
self.pending_quit = true;
|
||||
self.message = Some((
|
||||
"unsaved changes! q again to discard, ^S to save".into(),
|
||||
Instant::now(),
|
||||
true,
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any other key cancels the pending quit
|
||||
if self.pending_quit {
|
||||
self.pending_quit = false;
|
||||
self.message = None;
|
||||
}
|
||||
|
||||
match key.code {
|
||||
KeyCode::Char('q') | KeyCode::Esc => return true,
|
||||
KeyCode::Char('s') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
if let Err(e) = self.save() {
|
||||
self.message = Some((format!("error: {}", e), Instant::now(), true));
|
||||
@@ -564,7 +597,7 @@ fn render_footer(f: &mut Frame, app: &App, area: Rect) {
|
||||
|
||||
// ── Entry point ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn run(config_path: PathBuf) -> anyhow::Result<()> {
|
||||
pub fn run(config_path: PathBuf) -> anyhow::Result<SetupOutcome> {
|
||||
// Setup terminal
|
||||
terminal::enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
@@ -584,46 +617,42 @@ pub fn run(config_path: PathBuf) -> anyhow::Result<()> {
|
||||
|
||||
result?;
|
||||
|
||||
// Post-TUI message
|
||||
if app.saved_once {
|
||||
eprintln!();
|
||||
eprintln!(" Config saved to {}", config_path.display());
|
||||
eprintln!();
|
||||
// ── Post-TUI: decide outcome ─────────────────────────────────────
|
||||
|
||||
let wants_service = app
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.key == "install_service")
|
||||
.map(|f| f.value == "true")
|
||||
.unwrap_or(false);
|
||||
if !app.saved_once {
|
||||
return Ok(SetupOutcome::Cancelled);
|
||||
}
|
||||
|
||||
if wants_service {
|
||||
match super::service::install_service(&config_path) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
eprintln!(" Service install failed: {}", e);
|
||||
eprintln!();
|
||||
}
|
||||
eprintln!();
|
||||
eprintln!(" Config saved to {}", config_path.display());
|
||||
eprintln!();
|
||||
|
||||
let wants_service = app
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.key == "install_service")
|
||||
.map(|f| f.value == "true")
|
||||
.unwrap_or(false);
|
||||
|
||||
if wants_service {
|
||||
match super::service::install_service(&config_path) {
|
||||
Ok(()) => return Ok(SetupOutcome::ServiceInstalled),
|
||||
Err(e) => {
|
||||
eprintln!(" Service install failed: {}", e);
|
||||
eprintln!(" Starting proxy directly instead.\n");
|
||||
}
|
||||
} else {
|
||||
// Uninstall service if it was previously installed
|
||||
if super::service::is_installed() {
|
||||
if let Err(e) = super::service::uninstall_service() {
|
||||
eprintln!(" Service uninstall failed: {}", e);
|
||||
eprintln!();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Uninstall service if it was previously installed but toggled off
|
||||
if super::service::is_installed() {
|
||||
if let Err(e) = super::service::uninstall_service() {
|
||||
eprintln!(" Service uninstall failed: {}", e);
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
eprintln!(" Run with:");
|
||||
eprintln!(
|
||||
" aether-proxy (auto-reads {})",
|
||||
config_path.display()
|
||||
);
|
||||
eprintln!();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(SetupOutcome::ReadyToRun(config_path))
|
||||
}
|
||||
|
||||
fn event_loop(
|
||||
|
||||
Reference in New Issue
Block a user