From b5850d36e62b890383ca46e28986f4ed53978060 Mon Sep 17 00:00:00 2001 From: litoral05 Date: Wed, 6 May 2026 17:31:56 +0100 Subject: [PATCH] feat(openwrt): add OpenVPN package installation workflow --- src-tauri/src/lib.rs | 3 +- src-tauri/src/openwrt.rs | 148 ++++++++++++++++++++++++++++++++ src/pages/OpenWrtConfigPage.tsx | 116 +++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ebae293..6ae7977 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7,7 +7,8 @@ pub fn run() { .invoke_handler(tauri::generate_handler![ openwrt::detect_openwrt_router, openwrt::test_openwrt_ssh, - openwrt::prepare_openwrt_router + openwrt::prepare_openwrt_router, + openwrt::install_openwrt_openvpn_packages ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/openwrt.rs b/src-tauri/src/openwrt.rs index 4940df9..198a215 100644 --- a/src-tauri/src/openwrt.rs +++ b/src-tauri/src/openwrt.rs @@ -52,6 +52,18 @@ pub struct OpenWrtPrepareResult { logs: String, } +#[derive(Serialize)] +pub struct OpenWrtPackageInstallResult { + success: bool, + updated_opkg: bool, + installed_openvpn: bool, + installed_luci: bool, + installed_ca_bundle: bool, + installed_kmod_tun: bool, + message: String, + logs: String, +} + #[tauri::command] pub fn detect_openwrt_router() -> OpenWrtDetectionResult { let candidates = vec![detect_default_gateway(), Some("192.168.1.1".to_string())]; @@ -299,6 +311,123 @@ pub fn prepare_openwrt_router( } } +#[tauri::command] +pub fn install_openwrt_openvpn_packages( + host: String, + username: String, + password: String +) -> OpenWrtPackageInstallResult { + let address = format!("{}:22", host); + + let socket = match address.parse::() { + Ok(socket) => socket, + Err(err) => { + return OpenWrtPackageInstallResult { + success: false, + updated_opkg: false, + installed_openvpn: false, + installed_luci: false, + installed_ca_bundle: false, + installed_kmod_tun: false, + message: format!("Endereço inválido: {}", err), + logs: "".to_string(), + }; + } + }; + + let tcp = match TcpStream::connect_timeout(&socket, Duration::from_secs(5)) { + Ok(stream) => stream, + Err(err) => { + return OpenWrtPackageInstallResult { + success: false, + updated_opkg: false, + installed_openvpn: false, + installed_luci: false, + installed_ca_bundle: false, + installed_kmod_tun: false, + message: format!("Falha ao ligar por TCP/SSH: {}", err), + logs: "".to_string(), + }; + } + }; + + let mut session = match Session::new() { + Ok(session) => session, + Err(err) => { + return OpenWrtPackageInstallResult { + success: false, + updated_opkg: false, + installed_openvpn: false, + installed_luci: false, + installed_ca_bundle: false, + installed_kmod_tun: false, + message: format!("Falha ao criar sessão SSH: {}", err), + logs: "".to_string(), + }; + } + }; + + session.set_tcp_stream(tcp); + + if let Err(err) = session.handshake() { + return OpenWrtPackageInstallResult { + success: false, + updated_opkg: false, + installed_openvpn: false, + installed_luci: false, + installed_ca_bundle: false, + installed_kmod_tun: false, + message: format!("Falha no handshake SSH: {}", err), + logs: "".to_string(), + }; + } + + if let Err(err) = session.userauth_password(&username, &password) { + return OpenWrtPackageInstallResult { + success: false, + updated_opkg: false, + installed_openvpn: false, + installed_luci: false, + installed_ca_bundle: false, + installed_kmod_tun: false, + message: format!("Falha na autenticação SSH: {}", err), + logs: "".to_string(), + }; + } + + let command = build_openvpn_install_command(); + + let logs = run_ssh_command(&session, &command).unwrap_or_default(); + + let updated_opkg = logs.contains("OPKG_UPDATED"); + let installed_openvpn = logs.contains("OPENVPN_OK"); + let installed_luci = logs.contains("LUCI_OK"); + let installed_ca_bundle = logs.contains("CA_BUNDLE_OK"); + let installed_kmod_tun = logs.contains("KMOD_TUN_OK"); + + let success = + updated_opkg && + installed_openvpn && + installed_luci && + installed_ca_bundle && + installed_kmod_tun; + + OpenWrtPackageInstallResult { + success, + updated_opkg, + installed_openvpn, + installed_luci, + installed_ca_bundle, + installed_kmod_tun, + message: if success { + "Pacotes OpenVPN instalados com sucesso.".to_string() + } else { + "Instalação incompleta. Verifique os logs.".to_string() + }, + logs, + } +} + fn tcp_port_open(host: &str, port: u16) -> bool { let address = format!("{}:{}", host, port); @@ -645,3 +774,22 @@ fn build_prepare_command(target_hostname: &str, new_password: &str) -> String { fn shell_escape_single_quotes(value: &str) -> String { value.replace('\'', "'\\''") } + +fn build_openvpn_install_command() -> String { + "\ +set -e; \ +echo '=== OPENVPN PACKAGE INSTALL START ==='; \ +echo 'Updating OPKG repositories...'; \ +opkg update; \ +echo OPKG_UPDATED; \ +echo 'Installing OpenVPN packages...'; \ +opkg install openvpn-openssl luci-app-openvpn ca-bundle kmod-tun; \ +echo PACKAGES_INSTALL_DONE; \ +echo 'Validating installed packages...'; \ +command -v openvpn >/dev/null 2>&1 && echo OPENVPN_OK || echo OPENVPN_FAIL; \ +opkg list-installed | grep -q '^luci-app-openvpn ' && echo LUCI_OK || echo LUCI_FAIL; \ +opkg list-installed | grep -q '^ca-bundle ' && echo CA_BUNDLE_OK || echo CA_BUNDLE_FAIL; \ +opkg list-installed | grep -q '^kmod-tun ' && echo KMOD_TUN_OK || echo KMOD_TUN_FAIL; \ +echo '=== OPENVPN PACKAGE INSTALL COMPLETE ==='\ +".to_string() +} diff --git a/src/pages/OpenWrtConfigPage.tsx b/src/pages/OpenWrtConfigPage.tsx index 92850d4..8387a79 100644 --- a/src/pages/OpenWrtConfigPage.tsx +++ b/src/pages/OpenWrtConfigPage.tsx @@ -54,6 +54,17 @@ type SshResult = { message: string; }; +type PackageInstallResult = { + success: boolean; + updated_opkg: boolean; + installed_openvpn: boolean; + installed_luci: boolean; + installed_ca_bundle: boolean; + installed_kmod_tun: boolean; + message: string; + logs: string; +}; + type Props = { routers: RouterItem[]; }; @@ -75,6 +86,9 @@ export function OpenWrtConfigPage({ routers }: Props) { const [preparing, setPreparing] = useState(false); const [prepareResult, setPrepareResult] = useState(null); + const [installingPackages, setInstallingPackages] = useState(false); + const [packageResult, setPackageResult] = useState(null); + const compatibility = sshResult?.compatibility; async function detectRouter() { @@ -134,10 +148,43 @@ export function OpenWrtConfigPage({ routers }: Props) { } } + async function installOpenVpnPackages() { + try { + setInstallingPackages(true); + setPackageResult(null); + + const result = await invoke( + "install_openwrt_openvpn_packages", + { + host, + username, + password, + } + ); + + setPackageResult(result); + + if (result.success) { + const refreshed = await invoke("test_openwrt_ssh", { + host, + username, + password, + }); + + setSshResult(refreshed); + } + } finally { + setInstallingPackages(false); + } + } + async function connectSsh() { try { setConnecting(true); + setPackageResult(null); + setPrepareResult(null); + const result = await invoke("test_openwrt_ssh", { host, username, @@ -394,6 +441,22 @@ export function OpenWrtConfigPage({ routers }: Props) { value={sshResult.readiness.free_memory || "-"} /> + +
+ +
)} @@ -543,6 +606,59 @@ export function OpenWrtConfigPage({ routers }: Props) { )} )} + + {packageResult && ( +
+
+
+

Instalação OpenVPN

+

Pacotes necessários no router

+
+ + + {packageResult.success ? "Sucesso" : "Falhou"} + +
+ +
+ + + + + + + + + +
+ +
+ {packageResult.success ? : } +

{packageResult.message}

+
+ + {packageResult.logs && ( +
+
{packageResult.logs}
+
+ )} +
+ )}