feat(openwrt): add OpenVPN package installation workflow
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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::<SocketAddr>() {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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<PrepareResult | null>(null);
|
||||
|
||||
const [installingPackages, setInstallingPackages] = useState(false);
|
||||
const [packageResult, setPackageResult] = useState<PackageInstallResult | null>(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<PackageInstallResult>(
|
||||
"install_openwrt_openvpn_packages",
|
||||
{
|
||||
host,
|
||||
username,
|
||||
password,
|
||||
}
|
||||
);
|
||||
|
||||
setPackageResult(result);
|
||||
|
||||
if (result.success) {
|
||||
const refreshed = await invoke<SshResult>("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<SshResult>("test_openwrt_ssh", {
|
||||
host,
|
||||
username,
|
||||
@@ -394,6 +441,22 @@ export function OpenWrtConfigPage({ routers }: Props) {
|
||||
value={sshResult.readiness.free_memory || "-"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="modal-actions">
|
||||
<button
|
||||
className="primary"
|
||||
onClick={installOpenVpnPackages}
|
||||
disabled={
|
||||
installingPackages ||
|
||||
!sshResult?.connected ||
|
||||
!sshResult.readiness.opkg_ok ||
|
||||
!sshResult.readiness.internet_ok ||
|
||||
!sshResult.readiness.dns_ok
|
||||
}
|
||||
>
|
||||
{installingPackages ? "A instalar pacotes..." : "Instalar Pacotes OpenVPN"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -543,6 +606,59 @@ export function OpenWrtConfigPage({ routers }: Props) {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{packageResult && (
|
||||
<div className="dashboard-card wide-panel">
|
||||
<div className="card-header">
|
||||
<div>
|
||||
<h2>Instalação OpenVPN</h2>
|
||||
<p>Pacotes necessários no router</p>
|
||||
</div>
|
||||
|
||||
<span className={`badge ${packageResult.success ? "success" : "failed"}`}>
|
||||
{packageResult.success ? "Sucesso" : "Falhou"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="detail-list">
|
||||
<DetailRow
|
||||
label="OPKG update"
|
||||
value={packageResult.updated_opkg ? "OK" : "Falhou"}
|
||||
/>
|
||||
|
||||
<DetailRow
|
||||
label="openvpn-openssl"
|
||||
value={packageResult.installed_openvpn ? "Instalado" : "Falhou"}
|
||||
/>
|
||||
|
||||
<DetailRow
|
||||
label="luci-app-openvpn"
|
||||
value={packageResult.installed_luci ? "Instalado" : "Falhou"}
|
||||
/>
|
||||
|
||||
<DetailRow
|
||||
label="ca-bundle"
|
||||
value={packageResult.installed_ca_bundle ? "Instalado" : "Falhou"}
|
||||
/>
|
||||
|
||||
<DetailRow
|
||||
label="kmod-tun"
|
||||
value={packageResult.installed_kmod_tun ? "Instalado" : "Falhou"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="status-note">
|
||||
{packageResult.success ? <CheckCircle2 size={24} /> : <XCircle size={24} />}
|
||||
<p>{packageResult.message}</p>
|
||||
</div>
|
||||
|
||||
{packageResult.logs && (
|
||||
<div className="log-box">
|
||||
<pre>{packageResult.logs}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user