mirror of
https://github.com/creating2morrow/neveko.git
synced 2024-12-22 19:49:24 +00:00
add i2p to installation manager
This commit is contained in:
parent
3c7b5f4da1
commit
1babfcd19c
6 changed files with 107 additions and 41 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ notes.txt
|
||||||
*.zip
|
*.zip
|
||||||
*/monero-x86_64-linux-gnu-v0.18.2.2/**
|
*/monero-x86_64-linux-gnu-v0.18.2.2/**
|
||||||
*/i2p-zero-linux.v1.20/**
|
*/i2p-zero-linux.v1.20/**
|
||||||
|
*.jar
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{fs, env, process::Command};
|
use std::{fs, env, process::Command};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use reqwest::Client;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::{args, utils};
|
use crate::{args, utils};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
@ -88,7 +87,15 @@ pub async fn start() {
|
||||||
.expect("i2p-zero failed to start");
|
.expect("i2p-zero failed to start");
|
||||||
debug!("{:?}", output.stdout);
|
debug!("{:?}", output.stdout);
|
||||||
find_tunnels().await;
|
find_tunnels().await;
|
||||||
{ tokio::spawn(async { check_connection(true).await }); }
|
{
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let tick: std::sync::mpsc::Receiver<()> = schedule_recv::periodic_ms(60000);
|
||||||
|
loop {
|
||||||
|
tick.recv().unwrap();
|
||||||
|
check_connection().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_tunnel() {
|
fn create_tunnel() {
|
||||||
|
@ -133,26 +140,9 @@ pub fn get_destination() -> String {
|
||||||
utils::empty_string()
|
utils::empty_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_proxy_status() -> HttpProxyStatus {
|
pub async fn check_connection() -> HttpProxyStatus {
|
||||||
check_connection(false).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ping i2pd tunnels local server for status
|
|
||||||
async fn check_connection(bg: bool) -> HttpProxyStatus {
|
|
||||||
let client: reqwest::Client = reqwest::Client::new();
|
let client: reqwest::Client = reqwest::Client::new();
|
||||||
let host: &str = "http://localhost:7657/tunnels";
|
let host: &str = "http://localhost:7657/tunnels";
|
||||||
if bg {
|
|
||||||
let tick: std::sync::mpsc::Receiver<()> = schedule_recv::periodic_ms(60000);
|
|
||||||
loop {
|
|
||||||
tick.recv().unwrap();
|
|
||||||
return process_connection_info(client, host).await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return process_connection_info(client, host).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn process_connection_info(client: Client, host: &str) -> HttpProxyStatus {
|
|
||||||
match client.get(host).send().await {
|
match client.get(host).send().await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
// do some parsing here to check the status
|
// do some parsing here to check the status
|
||||||
|
|
|
@ -17,7 +17,11 @@ pub const NEVMES_JWT_SECRET_KEY: &str = "NEVMES_JWT_SECRET_KEY";
|
||||||
|
|
||||||
/// The latest monero release download
|
/// The latest monero release download
|
||||||
pub const MONERO_RELEASE_VERSION: &str = "monero-linux-x64-v0.18.2.2.tar.bz2";
|
pub const MONERO_RELEASE_VERSION: &str = "monero-linux-x64-v0.18.2.2.tar.bz2";
|
||||||
|
pub const MONERO_RELEASE_HASH: &str = "186800de18f67cca8475ce392168aabeb5709a8f8058b0f7919d7c693786d56b";
|
||||||
/// The latest i2p-zero release version
|
/// The latest i2p-zero release version
|
||||||
pub const I2P_ZERO_RELEASE_VERSION: &str = "v1.20";
|
pub const I2P_ZERO_RELEASE_VERSION: &str = "v1.20";
|
||||||
|
pub const I2P_ZERO_RELEASH_HASH: &str = "7e7216b281624ec464b55217284017576d109eaba7b35f7e4994ae2a78634de7";
|
||||||
|
/// The latest i2pd release version
|
||||||
|
pub const I2P_RELEASE_VERSION: &str = "2.2.1";
|
||||||
|
pub const I2P_RELEASE_HASH: &str = "c9879b8f69ea13c758672c2fa083dc2e0abb289e0fc9a55af98f9f1795f82659";
|
||||||
// DO NOT EDIT BELOW THIS LINE
|
// DO NOT EDIT BELOW THIS LINE
|
||||||
|
|
|
@ -5,6 +5,10 @@ use crate::{args, db, i2p, message, models, monero, gpg, utils, reqres };
|
||||||
use log::{info, debug, error, warn};
|
use log::{info, debug, error, warn};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Enum for selecting hash validation
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum ExternalSoftware { I2P, I2PZero, XMR }
|
||||||
|
|
||||||
/// Handles the state for the installation manager popup
|
/// Handles the state for the installation manager popup
|
||||||
pub struct Installations {
|
pub struct Installations {
|
||||||
pub xmr: bool,
|
pub xmr: bool,
|
||||||
|
@ -416,9 +420,32 @@ pub fn stage_cleanup(f: String) {
|
||||||
/// software on their own. Note that software pull is over
|
/// software on their own. Note that software pull is over
|
||||||
///
|
///
|
||||||
/// clearnet. TODO(c2m): trusted download locations over i2p.
|
/// clearnet. TODO(c2m): trusted download locations over i2p.
|
||||||
pub async fn install_software(installations: Installations) {
|
pub async fn install_software(installations: Installations) -> bool {
|
||||||
|
let mut valid_i2p_hash = true;
|
||||||
|
let mut valid_i2p_zero_hash = true;
|
||||||
|
let mut valid_xmr_hash = true;
|
||||||
if installations.i2p {
|
if installations.i2p {
|
||||||
info!("installing i2p");
|
info!("installing i2p");
|
||||||
|
let i2p_version = crate::I2P_RELEASE_VERSION;
|
||||||
|
let i2p_jar = format!("i2pinstall_{}.jar", i2p_version);
|
||||||
|
let link = format!("https://download.i2p2.no/releases/{}/{}", i2p_version, i2p_jar);
|
||||||
|
let curl = std::process::Command::new("curl")
|
||||||
|
.args(["-O#", &link])
|
||||||
|
.status();
|
||||||
|
match curl {
|
||||||
|
Ok(curl_output) => {
|
||||||
|
debug!("{:?}", curl_output);
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
|
let jar_output = std::process::Command::new("java")
|
||||||
|
.args(["-jar", &i2p_jar])
|
||||||
|
.spawn()
|
||||||
|
.expect("i2p gui installation failed");
|
||||||
|
debug!("{:?}", jar_output.stdout);
|
||||||
|
},
|
||||||
|
_=> error!("i2p download failed")
|
||||||
|
}
|
||||||
|
valid_i2p_hash = validate_installation_hash(ExternalSoftware::I2P, &i2p_jar);
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
if installations.i2p_zero {
|
if installations.i2p_zero {
|
||||||
info!("installing i2p-zero");
|
info!("installing i2p-zero");
|
||||||
|
@ -434,13 +461,15 @@ pub async fn install_software(installations: Installations) {
|
||||||
debug!("{:?}", curl_output);
|
debug!("{:?}", curl_output);
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
let unzip_output = std::process::Command::new("unzip")
|
let unzip_output = std::process::Command::new("unzip")
|
||||||
.arg(i2p_zero_zip)
|
.arg(&i2p_zero_zip)
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("i2p unzip failed");
|
.expect("i2p unzip failed");
|
||||||
debug!("{:?}", unzip_output.stdout);
|
debug!("{:?}", unzip_output.stdout);
|
||||||
},
|
},
|
||||||
_=> error!("i2p-zero download failed")
|
_=> error!("i2p-zero download failed")
|
||||||
}
|
}
|
||||||
|
valid_i2p_zero_hash = validate_installation_hash(ExternalSoftware::I2PZero, &i2p_zero_zip);
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
if installations.xmr {
|
if installations.xmr {
|
||||||
info!("installing monero");
|
info!("installing monero");
|
||||||
|
@ -460,5 +489,29 @@ pub async fn install_software(installations: Installations) {
|
||||||
},
|
},
|
||||||
_=> error!("monero download failed")
|
_=> error!("monero download failed")
|
||||||
}
|
}
|
||||||
|
valid_xmr_hash = validate_installation_hash(ExternalSoftware::XMR, &String::from(crate::MONERO_RELEASE_VERSION));
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
|
valid_i2p_hash && valid_i2p_zero_hash && valid_xmr_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linux specific hash validation using the command `sha256sum`
|
||||||
|
fn validate_installation_hash(sw: ExternalSoftware, filename: &String) -> bool {
|
||||||
|
debug!("validating hash");
|
||||||
|
let expected_hash = if sw == ExternalSoftware::I2P {
|
||||||
|
String::from(crate::I2P_RELEASE_HASH)
|
||||||
|
} else if sw == ExternalSoftware::I2PZero {
|
||||||
|
String::from(crate::I2P_ZERO_RELEASH_HASH)
|
||||||
|
} else {
|
||||||
|
String::from(crate::MONERO_RELEASE_HASH)
|
||||||
|
};
|
||||||
|
let sha_output = std::process::Command::new("sha256sum")
|
||||||
|
.arg(filename).output().expect("hash validation failed");
|
||||||
|
let str_sha = String::from_utf8(sha_output.stdout).unwrap();
|
||||||
|
let split1 = str_sha.split(" ");
|
||||||
|
let mut v: Vec<String> = split1.map(|s| String::from(s)).collect();
|
||||||
|
let actual_hash = v.remove(0);
|
||||||
|
debug!("actual hash: {}", actual_hash);
|
||||||
|
debug!("expected hash: {}", expected_hash);
|
||||||
|
actual_hash == expected_hash
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,11 @@ use nevmes_core::*;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::{BLOCK_TIME_IN_SECS_EST, BYTES_IN_GB, START_CORE_TIMEOUT_SECS};
|
|
||||||
|
|
||||||
pub struct HomeApp {
|
pub struct HomeApp {
|
||||||
connections: utils::Connections,
|
connections: utils::Connections,
|
||||||
core_timeout_tx: Sender<bool>,
|
core_timeout_tx: Sender<bool>,
|
||||||
core_timeout_rx: Receiver<bool>,
|
core_timeout_rx: Receiver<bool>,
|
||||||
|
has_install_failed: bool,
|
||||||
installations: utils::Installations,
|
installations: utils::Installations,
|
||||||
installation_tx: Sender<bool>,
|
installation_tx: Sender<bool>,
|
||||||
installation_rx: Receiver<bool>,
|
installation_rx: Receiver<bool>,
|
||||||
|
@ -50,6 +49,7 @@ pub struct HomeApp {
|
||||||
impl Default for HomeApp {
|
impl Default for HomeApp {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let connections = Default::default();
|
let connections = Default::default();
|
||||||
|
let has_install_failed = false;
|
||||||
let installations = Default::default();
|
let installations = Default::default();
|
||||||
let is_core_running = false;
|
let is_core_running = false;
|
||||||
let is_editing_connections = false;
|
let is_editing_connections = false;
|
||||||
|
@ -80,6 +80,7 @@ impl Default for HomeApp {
|
||||||
connections,
|
connections,
|
||||||
core_timeout_rx,
|
core_timeout_rx,
|
||||||
core_timeout_tx,
|
core_timeout_tx,
|
||||||
|
has_install_failed,
|
||||||
installations,
|
installations,
|
||||||
installation_rx,
|
installation_rx,
|
||||||
installation_tx,
|
installation_tx,
|
||||||
|
@ -139,6 +140,7 @@ impl eframe::App for HomeApp {
|
||||||
}
|
}
|
||||||
if let Ok(install) = self.installation_rx.try_recv() {
|
if let Ok(install) = self.installation_rx.try_recv() {
|
||||||
self.is_installing = !install;
|
self.is_installing = !install;
|
||||||
|
if !install && self.is_loading { self.has_install_failed = true }
|
||||||
self.is_loading = false;
|
self.is_loading = false;
|
||||||
}
|
}
|
||||||
if let Ok(timeout) = self.core_timeout_rx.try_recv() {
|
if let Ok(timeout) = self.core_timeout_rx.try_recv() {
|
||||||
|
@ -150,6 +152,20 @@ impl eframe::App for HomeApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Installation Error window
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
let mut has_install_failed = self.has_install_failed;
|
||||||
|
egui::Window::new("Error")
|
||||||
|
.open(&mut has_install_failed)
|
||||||
|
.vscroll(false)
|
||||||
|
.show(&ctx, |ui| {
|
||||||
|
ui.heading("Installation Failure");
|
||||||
|
if ui.button("Exit").clicked() {
|
||||||
|
self.is_installing = false;
|
||||||
|
self.is_loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Connection Manager window
|
// Connection Manager window
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
let mut is_editing_connections = self.is_editing_connections;
|
let mut is_editing_connections = self.is_editing_connections;
|
||||||
|
@ -217,12 +233,12 @@ impl eframe::App for HomeApp {
|
||||||
.open(&mut is_installing)
|
.open(&mut is_installing)
|
||||||
.vscroll(true)
|
.vscroll(true)
|
||||||
.show(&ctx, |ui| {
|
.show(&ctx, |ui| {
|
||||||
// let mut wants_i2p = self.installations.i2p;
|
let mut wants_i2p = self.installations.i2p;
|
||||||
let mut wants_i2p_zero = self.installations.i2p_zero;
|
let mut wants_i2p_zero = self.installations.i2p_zero;
|
||||||
let mut wants_xmr = self.installations.xmr;
|
let mut wants_xmr = self.installations.xmr;
|
||||||
// if ui.checkbox(&mut wants_i2p, "i2p").changed() {
|
if ui.checkbox(&mut wants_i2p, "i2p").changed() {
|
||||||
// self.installations.i2p = !self.installations.i2p;
|
self.installations.i2p = !self.installations.i2p;
|
||||||
// }
|
}
|
||||||
if ui.checkbox(&mut wants_i2p_zero, "i2p-zero").changed() {
|
if ui.checkbox(&mut wants_i2p_zero, "i2p-zero").changed() {
|
||||||
self.installations.i2p_zero = !self.installations.i2p_zero;
|
self.installations.i2p_zero = !self.installations.i2p_zero;
|
||||||
}
|
}
|
||||||
|
@ -231,9 +247,11 @@ impl eframe::App for HomeApp {
|
||||||
}
|
}
|
||||||
let install = &self.installations;
|
let install = &self.installations;
|
||||||
if install.i2p || install.i2p_zero || install.xmr {
|
if install.i2p || install.i2p_zero || install.xmr {
|
||||||
if ui.button("Install").clicked() {
|
if !self.is_loading {
|
||||||
self.is_loading = true;
|
if ui.button("Install").clicked() {
|
||||||
install_software_req(self.installation_tx.clone(), ctx.clone(), &self.installations);
|
self.is_loading = true;
|
||||||
|
install_software_req(self.installation_tx.clone(), ctx.clone(), &self.installations);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.button("Exit").clicked() {
|
if ui.button("Exit").clicked() {
|
||||||
|
@ -280,10 +298,10 @@ impl eframe::App for HomeApp {
|
||||||
let address = &self.s_xmr_address.result.address;
|
let address = &self.s_xmr_address.result.address;
|
||||||
let unlocked_balance = self.s_xmr_balance.result.unlocked_balance;
|
let unlocked_balance = self.s_xmr_balance.result.unlocked_balance;
|
||||||
let locked_balance = self.s_xmr_balance.result.balance - unlocked_balance;
|
let locked_balance = self.s_xmr_balance.result.balance - unlocked_balance;
|
||||||
let unlock_time = self.s_xmr_balance.result.blocks_to_unlock * BLOCK_TIME_IN_SECS_EST;
|
let unlock_time = self.s_xmr_balance.result.blocks_to_unlock * crate::BLOCK_TIME_IN_SECS_EST;
|
||||||
let xmrd_info: &reqres::XmrDaemonGetInfoResult = &self.s_xmrd_get_info.result;
|
let xmrd_info: &reqres::XmrDaemonGetInfoResult = &self.s_xmrd_get_info.result;
|
||||||
let free_space = xmrd_info.free_space / BYTES_IN_GB;
|
let free_space = xmrd_info.free_space / crate::BYTES_IN_GB;
|
||||||
let db_size = xmrd_info.database_size / BYTES_IN_GB;
|
let db_size = xmrd_info.database_size / crate::BYTES_IN_GB;
|
||||||
ui.label(format!("- rpc version: {}\n- address: {}\n- balance: {} piconero(s)\n- locked balance: {} piconero(s)\n- unlock time (secs): {}\n- daemon info\n\t- net type: {}\n\t- current hash: {}\n\t- height: {}\n\t- synced: {}\n\t- blockchain size : ~{} GB\n\t- free space : ~{} GB\n\t- version: {}\n",
|
ui.label(format!("- rpc version: {}\n- address: {}\n- balance: {} piconero(s)\n- locked balance: {} piconero(s)\n- unlock time (secs): {}\n- daemon info\n\t- net type: {}\n\t- current hash: {}\n\t- height: {}\n\t- synced: {}\n\t- blockchain size : ~{} GB\n\t- free space : ~{} GB\n\t- version: {}\n",
|
||||||
self.s_xmr_rpc_ver.result.version, address, unlocked_balance, locked_balance,
|
self.s_xmr_rpc_ver.result.version, address, unlocked_balance, locked_balance,
|
||||||
unlock_time, xmrd_info.nettype, xmrd_info.top_block_hash, xmrd_info.height, xmrd_info.synchronized,
|
unlock_time, xmrd_info.nettype, xmrd_info.top_block_hash, xmrd_info.height, xmrd_info.synchronized,
|
||||||
|
@ -352,7 +370,7 @@ fn send_balance_req(tx: Sender<reqres::XmrRpcBalanceResponse>, ctx: egui::Contex
|
||||||
|
|
||||||
fn send_i2p_status_req(tx: Sender<i2p::HttpProxyStatus>, ctx: egui::Context) {
|
fn send_i2p_status_req(tx: Sender<i2p::HttpProxyStatus>, ctx: egui::Context) {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let status = i2p::get_proxy_status().await;
|
let status = i2p::check_connection().await;
|
||||||
let _ = tx.send(status);
|
let _ = tx.send(status);
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
});
|
});
|
||||||
|
@ -373,7 +391,7 @@ fn send_reset_refresh(tx: Sender<bool>, ctx: egui::Context, init: bool) {
|
||||||
fn start_core_timeout
|
fn start_core_timeout
|
||||||
(tx: Sender<bool>, ctx: egui::Context) {
|
(tx: Sender<bool>, ctx: egui::Context) {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(START_CORE_TIMEOUT_SECS)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(crate::START_CORE_TIMEOUT_SECS)).await;
|
||||||
log::error!("start nevmes-core timeout");
|
log::error!("start nevmes-core timeout");
|
||||||
let _ = tx.send(true);
|
let _ = tx.send(true);
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
|
@ -388,8 +406,8 @@ fn install_software_req
|
||||||
xmr: installations.xmr,
|
xmr: installations.xmr,
|
||||||
};
|
};
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
utils::install_software(req_install).await;
|
let did_install = utils::install_software(req_install).await;
|
||||||
let _ = tx.send(true);
|
let _ = tx.send(did_install);
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub async fn get_version(_jwp: proof::PaymentProof) -> Custom<Json<reqres::XmrRp
|
||||||
/// This also functions as a health check
|
/// This also functions as a health check
|
||||||
#[get("/status")]
|
#[get("/status")]
|
||||||
pub async fn get_i2p_status() -> Custom<Json<i2p::HttpProxyStatus>> {
|
pub async fn get_i2p_status() -> Custom<Json<i2p::HttpProxyStatus>> {
|
||||||
Custom(Status::Ok, Json(i2p::get_proxy_status().await))
|
Custom(Status::Ok, Json(i2p::check_connection().await))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Share your contact information
|
/// Share your contact information
|
||||||
|
|
Loading…
Reference in a new issue