mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
4
apps/gpgui-helper/src-tauri/.gitignore
vendored
Normal file
4
apps/gpgui-helper/src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
25
apps/gpgui-helper/src-tauri/Cargo.toml
Normal file
25
apps/gpgui-helper/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "gpgui-helper"
|
||||
authors.workspace = true
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.5", features = [] }
|
||||
|
||||
[dependencies]
|
||||
gpapi = { path = "../../../crates/gpapi", features = ["tauri"] }
|
||||
tauri = { workspace = true, features = ["window-start-dragging"] }
|
||||
tokio.workspace = true
|
||||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
clap.workspace = true
|
||||
compile-time.workspace = true
|
||||
env_logger.workspace = true
|
||||
futures-util.workspace = true
|
||||
tempfile.workspace = true
|
||||
reqwest = { workspace = true, features = ["stream"] }
|
||||
|
||||
[features]
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
3
apps/gpgui-helper/src-tauri/build.rs
Normal file
3
apps/gpgui-helper/src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
BIN
apps/gpgui-helper/src-tauri/icons/128x128.png
Normal file
BIN
apps/gpgui-helper/src-tauri/icons/128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
apps/gpgui-helper/src-tauri/icons/128x128@2x.png
Normal file
BIN
apps/gpgui-helper/src-tauri/icons/128x128@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
apps/gpgui-helper/src-tauri/icons/32x32.png
Normal file
BIN
apps/gpgui-helper/src-tauri/icons/32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
apps/gpgui-helper/src-tauri/icons/icon.icns
Normal file
BIN
apps/gpgui-helper/src-tauri/icons/icon.icns
Normal file
Binary file not shown.
BIN
apps/gpgui-helper/src-tauri/icons/icon.ico
Normal file
BIN
apps/gpgui-helper/src-tauri/icons/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
BIN
apps/gpgui-helper/src-tauri/icons/icon.png
Normal file
BIN
apps/gpgui-helper/src-tauri/icons/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
99
apps/gpgui-helper/src-tauri/icons/icon.svg
Normal file
99
apps/gpgui-helper/src-tauri/icons/icon.svg
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 96 96"
|
||||
style="enable-background:new 0 0 96 96;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="com.yuezk.qt.gpclient.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
|
||||
id="metadata14"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs12" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1006"
|
||||
id="namedview10"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.9532168"
|
||||
inkscape:cx="7.9545315"
|
||||
inkscape:cy="59.062386"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g8499" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#2980B9;}
|
||||
.st1{fill:#3498DB;}
|
||||
.st2{fill:#2ECC71;}
|
||||
.st3{fill:#27AE60;}
|
||||
</style>
|
||||
|
||||
<g
|
||||
id="g8499"
|
||||
transform="matrix(1.3407388,0,0,1.3407388,-16.409202,-16.355463)"><g
|
||||
id="XMLID_1_">
|
||||
<circle
|
||||
r="32.5"
|
||||
cy="48"
|
||||
cx="48"
|
||||
class="st0"
|
||||
id="XMLID_3_"
|
||||
style="fill:#2980b9" />
|
||||
<path
|
||||
d="m 48,15.5 v 65 C 65.9,80.5 80.5,65.7 80.5,48 80.5,30 65.9,15.5 48,15.5 Z"
|
||||
class="st1"
|
||||
id="XMLID_4_"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#3498db" />
|
||||
<path
|
||||
d="m 48,15.5 v 0.6 l 1.2,-0.3 c 0.3,-0.3 0.4,-0.3 0.6,-0.3 h -1.1 z m 7.3,0.9 c -0.1,0 0.4,0.9 1.1,1.8 0.8,1.5 1.1,2.1 1.3,2.1 0.3,-0.3 1.9,-1.2 3,-2.1 -1.7,-0.9 -3.5,-1.5 -5.4,-1.8 z m 10.3,6.2 c -0.1,0 -0.4,0 -0.9,0.6 l -0.8,0.9 0.6,0.6 c 0.3,0.6 0.8,0.9 1,1.2 0.5,0.6 0.6,0.6 0.1,1.5 -0.2,0.6 -0.3,0.9 -0.3,0.9 0.1,0.3 0.3,0.3 1.4,0.3 h 1.6 c 0.1,0 0.3,-0.6 0.4,-1.2 l 0.1,-0.9 -1.1,-0.9 c -1,-0.9 -1,-0.9 -1.4,-1.8 -0.3,-0.6 -0.6,-1.2 -0.7,-1.2 z m -3,2.4 c -0.2,0 -1.3,2.1 -1.3,2.4 0,0 0.3,0.6 0.7,0.9 0.4,0.3 0.7,0.6 0.7,0.6 0.1,0 1.2,-1.2 1.4,-1.5 C 64.2,27.1 64,26.8 63.5,26.2 63.1,25.5 62.7,25 62.6,25 Z m 9.5,1.1 0.2,0.3 c 0,0.3 -0.7,0.9 -1.4,1.5 -1.2,0.9 -1.4,1.2 -2,1.2 -0.6,0 -0.9,0.3 -1.8,0.9 -0.6,0.6 -1.2,0.9 -1.2,1.2 0,0 0.2,0.3 0.6,0.9 0.7,0.6 0.7,0.9 0.2,1.8 l -0.4,0.3 h -1.1 c -0.6,0 -1.5,0 -1.8,-0.3 -0.9,0 -0.8,0 -0.1,2.1 1,3 1.1,3.2 1.3,3.2 0.1,0 1.3,-1.2 2.8,-2.4 1.5,-1.2 2.7,-2.4 2.8,-2.4 l 0.6,0.3 c 0.4,0.3 0.5,0 1.3,-0.6 l 0.8,-0.6 0.8,0.6 c 1.9,1.2 2.2,1.5 2.3,2.4 0.2,1.5 0.3,1.8 0.5,1.8 0.1,0 1.3,-1.5 1.6,-1.8 0.1,-0.3 -0.1,-0.6 -1.1,-2.1 -0.7,-0.9 -1.1,-1.8 -1.1,-2.1 0,0 0.1,0 0.3,-0.3 0.2,0 0.4,0.3 1,0.9 -1.6,-2.3 -3.2,-4.7 -5.1,-6.8 z m 2.8,10.7 c -0.2,0 -0.9,0.9 -0.8,1.2 l 0.5,0.3 H 75 c 0.2,0 0.3,0 0.2,-0.3 C 75.1,37.4 75,36.8 74.9,36.8 Z M 72.3,38 h -2.4 l -2.4,0.3 -4.5,3.5 -4.4,3.8 v 3.5 c 0,2.1 0,3.8 0.1,3.8 0.1,0 0.7,0.9 1.5,1.5 0.8,0.9 1.5,1.5 1.8,1.8 0.4,0.3 0.5,0.3 4,0.6 l 3.4,0.3 1.6,0.9 c 0.8,0.6 1.5,1.2 1.6,1.2 0.1,0 -0.3,0.3 -0.6,0.6 l -0.6,0.6 1,1.2 c 0.5,0.6 1.3,1.5 1.7,1.8 l 0.6,0.9 v 1.7 0.9 c 3.7,-5 5.9,-11.5 6.1,-18.3 0.1,-2.7 -0.3,-5.3 -0.8,-8 l -0.6,-0.3 c -0.1,0 -0.5,0.3 -1,0.6 -0.5,0.3 -1,0.9 -1.1,0.9 -0.1,0 -0.8,-0.3 -1.8,-0.6 l -1.8,-0.6 v -0.9 c 0,-0.6 0,-0.9 -0.6,-1.5 z M 48,63.7 V 64 h 0.2 z"
|
||||
class="st2"
|
||||
id="XMLID_13_"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#2ecc71" />
|
||||
<path
|
||||
d="m 48,15.5 c -3.1,0 -6.2,0.5 -9,1.3 0.3,0.4 0.3,0.4 0.6,0.9 1.5,2.5 1.7,2.8 2.1,2.9 0.3,0 0.9,0.1 1.6,0.1 h 1.2 l 0.9,-2 0.8,-1.9 1.8,-0.6 z m -16.9,4.7 c -2.8,1.7 -5.4,3.9 -7.6,6.4 -3.8,4.3 -6.3,9.6 -7.4,15.4 0.5,0 0.9,-0.1 1.8,-0.1 2.8,0.1 2.5,0 3.4,1.4 0.5,0.8 0.6,0.8 1.4,0.8 1,0.1 0.9,0 0.5,-1.6 -0.2,-0.6 -0.3,-1.2 -0.3,-1.4 0,-0.2 0.5,-0.7 1.7,-1.6 1.9,-1.5 1.8,-1.3 1.5,-2.9 -0.1,-0.3 0.1,-0.6 0.6,-1.2 0.7,-0.7 0.7,-0.6 1.4,-0.6 h 0.7 l 0.1,-1.2 c 0.1,-0.7 0.1,-1.3 0.2,-1.3 0,0 1.9,-1.1 4.1,-2.3 2.2,-1.2 4.1,-2.2 4.2,-2.3 0.2,-0.2 -0.3,-0.8 -2.7,-3.8 -1.5,-1.9 -2.8,-3.6 -2.9,-3.7 z m -5.8,23 c -0.1,0 -0.1,0.3 -0.1,0.6 0,0.6 0,0.7 0.6,1 0.8,0.4 0.9,0.5 0.8,0.2 -0.1,-0.4 -1.2,-1.9 -1.3,-1.8 z m -3.4,2.1 -0.5,1.8 c 0.1,0.1 0.9,0.3 1.8,0.5 1,0.2 1.6,0.4 1.8,0.3 l 0.5,-1.3 z m -3.8,1 -1.1,0.6 c -0.6,0.3 -1.2,0.6 -1.4,0.6 h -0.1 c 0,1.4 0.1,2.8 0.3,4.2 l 0.6,0.4 1,-0.1 h 1 l 0.6,1.4 c 0.3,0.7 0.7,1.4 0.8,1.5 0.1,0.1 1,0.1 1.8,0.1 h 1.5 L 23,56.2 c 0,1.2 0,1.3 -0.6,2.2 -0.4,0.5 -0.6,1.2 -0.6,1.4 0,0.2 0.7,2.1 1.6,4.3 l 1.5,4 1.6,0.8 c 1.2,0.6 1.5,0.8 1.5,1 0,0.1 -0.4,2.1 -0.6,3.1 3,2.5 6.4,4.5 10.2,5.8 3.5,-3.6 6.8,-7.1 7.3,-7.6 l 0.7,-0.7 0.2,-1.9 c 0.2,-1.1 0.4,-2.1 0.4,-2.2 0,-0.1 0.5,-0.6 1,-1.2 0.5,-0.5 0.8,-1 0.8,-1.1 v -0.2 c -0.1,-0.1 -1.4,-1.1 -3,-2.2 l -3.1,-2.1 -1.1,-0.1 c -0.8,0 -1.2,0 -1.3,-0.2 C 39.4,59.2 39.2,58.5 39.1,57.7 39,56.9 38.9,56.2 38.8,56.1 38.8,56 38,56 37.1,56 36.2,56 35.4,55.9 35.3,55.8 35.2,55.7 35.2,55.1 35.1,54.3 35,53.6 34.9,53 34.8,52.9 34.7,52.8 33.7,52.7 32.5,52.6 30.5,52.5 30.1,52.5 29.1,52 l -1.2,-0.6 -1.6,0.7 -1.7,0.9 -1.8,-0.1 c -2,0 -1.9,0.2 -2.1,-1.6 C 20.6,50.7 20.6,50.1 20.5,50.1 20.4,50 20,50 19.6,49.9 L 18.9,49.7 19,49.2 c 0,-0.3 0,-1 0.1,-1.4 L 19.2,47 18.7,46.5 Z m 9.1,1.1 C 27.1,47.5 27.1,47.8 27,48 l -0.1,0.5 2.9,1.2 c 2.9,1.1 3.4,1.2 3.9,0.7 0.2,-0.2 0.1,-0.2 -0.3,-0.4 -0.3,-0.1 -1.7,-0.9 -3.2,-1.6 -1.7,-0.7 -2.9,-1.1 -3,-1 z"
|
||||
class="st3"
|
||||
id="XMLID_20_"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#27ae60" />
|
||||
</g><g
|
||||
transform="matrix(1.458069,0,0,1.458069,-22.631538,-19.615144)"
|
||||
id="g7664"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="XMLID_6_"
|
||||
class="st3"
|
||||
d="m 38.8,56.1 c 0,1.2 1,2.2 2.2,2.2 h 15.2 c 1.2,0 2.2,-1 2.2,-2.2 V 45.3 c 0,-1.2 -1,-2.2 -2.2,-2.2 H 40.9 c -1.2,0 -2.2,1 -2.2,2.2 v 10.8 z"
|
||||
style="fill:#f1aa27;fill-opacity:1" /><path
|
||||
style="fill:#e6e6e6"
|
||||
inkscape:connector-curvature="0"
|
||||
id="XMLID_7_"
|
||||
class="st4"
|
||||
d="m 55.5,43.1 h -3.3 v -3.7 c 0,-2.1 -1.7,-3.8 -3.8,-3.8 -2.1,0 -3.8,1.7 -3.8,3.8 v 3.8 h -3.1 v -3.8 c 0,-3.9 3.2,-7 7,-7 3.9,0 7,3.2 7,7 z" /><path
|
||||
style="fill:#e6e6e6;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="XMLID_8_"
|
||||
class="st5"
|
||||
d="m 50.35,48.2 c 0,-1 -0.8,-1.8 -1.8,-1.8 -1,0 -1.8,0.8 -1.8,1.8 0,0.7 0.4,1.3 1,1.6 l -1,5.2 h 3.6 l -1,-5.2 c 0.6,-0.3 1,-0.9 1,-1.6 z" /></g></g></svg>
|
After Width: | Height: | Size: 6.7 KiB |
56
apps/gpgui-helper/src-tauri/src/app.rs
Normal file
56
apps/gpgui-helper/src-tauri/src/app.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpapi::utils::window::WindowExt;
|
||||
use log::info;
|
||||
use tauri::Manager;
|
||||
|
||||
use crate::updater::{GuiUpdater, Installer, ProgressNotifier};
|
||||
|
||||
pub struct App {
|
||||
api_key: Vec<u8>,
|
||||
gui_version: String,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(api_key: Vec<u8>, gui_version: &str) -> Self {
|
||||
Self {
|
||||
api_key,
|
||||
gui_version: gui_version.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&self) -> anyhow::Result<()> {
|
||||
let gui_version = self.gui_version.clone();
|
||||
let api_key = self.api_key.clone();
|
||||
|
||||
tauri::Builder::default()
|
||||
.setup(move |app| {
|
||||
let win = app.get_window("main").expect("no main window");
|
||||
win.hide_menu();
|
||||
|
||||
let notifier = ProgressNotifier::new(win.clone());
|
||||
let installer = Installer::new(api_key);
|
||||
let updater = Arc::new(GuiUpdater::new(gui_version, notifier, installer));
|
||||
|
||||
let win_clone = win.clone();
|
||||
app.listen_global("app://update-done", move |_event| {
|
||||
info!("Update done");
|
||||
let _ = win_clone.close();
|
||||
});
|
||||
|
||||
// Listen for the update event
|
||||
win.listen("app://update", move |_event| {
|
||||
let updater = Arc::clone(&updater);
|
||||
tokio::spawn(async move { updater.update().await });
|
||||
});
|
||||
|
||||
// Update the GUI on startup
|
||||
win.trigger("app://update", None);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
56
apps/gpgui-helper/src-tauri/src/cli.rs
Normal file
56
apps/gpgui-helper/src-tauri/src/cli.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use clap::Parser;
|
||||
use gpapi::utils::base64;
|
||||
use log::{info, LevelFilter};
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
|
||||
const GP_API_KEY: &[u8; 32] = &[0; 32];
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version = VERSION)]
|
||||
struct Cli {
|
||||
#[arg(long, help = "Read the API key from stdin")]
|
||||
api_key_on_stdin: bool,
|
||||
|
||||
#[arg(long, default_value = env!("CARGO_PKG_VERSION"), help = "The version of the GUI")]
|
||||
gui_version: String,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
fn run(&self) -> anyhow::Result<()> {
|
||||
let api_key = self.read_api_key()?;
|
||||
let app = App::new(api_key, &self.gui_version);
|
||||
|
||||
app.run()
|
||||
}
|
||||
|
||||
fn read_api_key(&self) -> anyhow::Result<Vec<u8>> {
|
||||
if self.api_key_on_stdin {
|
||||
let mut api_key = String::new();
|
||||
std::io::stdin().read_line(&mut api_key)?;
|
||||
|
||||
let api_key = base64::decode_to_vec(api_key.trim())?;
|
||||
|
||||
Ok(api_key)
|
||||
} else {
|
||||
Ok(GP_API_KEY.to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
env_logger::builder().filter_level(LevelFilter::Info).init();
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
init_logger();
|
||||
info!("gpgui-helper started: {}", VERSION);
|
||||
|
||||
if let Err(e) = cli.run() {
|
||||
eprintln!("{}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
87
apps/gpgui-helper/src-tauri/src/downloader.rs
Normal file
87
apps/gpgui-helper/src-tauri/src/downloader.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::bail;
|
||||
use futures_util::StreamExt;
|
||||
use log::info;
|
||||
use tempfile::NamedTempFile;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
type OnProgress = Box<dyn Fn(Option<f64>) + Send + Sync + 'static>;
|
||||
|
||||
pub struct FileDownloader<'a> {
|
||||
url: &'a str,
|
||||
on_progress: RwLock<Option<OnProgress>>,
|
||||
}
|
||||
|
||||
impl<'a> FileDownloader<'a> {
|
||||
pub fn new(url: &'a str) -> Self {
|
||||
Self {
|
||||
url,
|
||||
on_progress: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_progress<T>(&self, on_progress: T)
|
||||
where
|
||||
T: Fn(Option<f64>) + Send + Sync + 'static,
|
||||
{
|
||||
if let Ok(mut guard) = self.on_progress.try_write() {
|
||||
*guard = Some(Box::new(on_progress));
|
||||
} else {
|
||||
info!("Failed to acquire on_progress lock");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download(&self) -> anyhow::Result<NamedTempFile> {
|
||||
let res = reqwest::get(self.url).await?.error_for_status()?;
|
||||
let content_length = res.content_length().unwrap_or(0);
|
||||
|
||||
info!("Content length: {}", content_length);
|
||||
|
||||
let mut current_length = 0;
|
||||
let mut stream = res.bytes_stream();
|
||||
|
||||
let mut file = NamedTempFile::new()?;
|
||||
|
||||
while let Some(item) = stream.next().await {
|
||||
let chunk = item?;
|
||||
let chunk_size = chunk.len() as u64;
|
||||
|
||||
file.write_all(&chunk)?;
|
||||
|
||||
current_length += chunk_size;
|
||||
let progress = current_length as f64 / content_length as f64 * 100.0;
|
||||
|
||||
if let Some(on_progress) = &*self.on_progress.read().await {
|
||||
let progress = if content_length > 0 { Some(progress) } else { None };
|
||||
|
||||
on_progress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
if content_length > 0 && current_length != content_length {
|
||||
bail!("Download incomplete");
|
||||
}
|
||||
|
||||
info!("Downloaded to: {:?}", file.path());
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChecksumFetcher<'a> {
|
||||
url: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> ChecksumFetcher<'a> {
|
||||
pub fn new(url: &'a str) -> Self {
|
||||
Self { url }
|
||||
}
|
||||
|
||||
pub async fn fetch(&self) -> anyhow::Result<String> {
|
||||
let res = reqwest::get(self.url).await?.error_for_status()?;
|
||||
let checksum = res.text().await?.trim().to_string();
|
||||
|
||||
Ok(checksum)
|
||||
}
|
||||
}
|
5
apps/gpgui-helper/src-tauri/src/lib.rs
Normal file
5
apps/gpgui-helper/src-tauri/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub(crate) mod app;
|
||||
pub(crate) mod downloader;
|
||||
pub(crate) mod updater;
|
||||
|
||||
pub mod cli;
|
9
apps/gpgui-helper/src-tauri/src/main.rs
Normal file
9
apps/gpgui-helper/src-tauri/src/main.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use gpgui_helper::cli;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
cli::run()
|
||||
}
|
137
apps/gpgui-helper/src-tauri/src/updater.rs
Normal file
137
apps/gpgui-helper/src-tauri/src/updater.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpapi::{
|
||||
service::request::UpdateGuiRequest,
|
||||
utils::{checksum::verify_checksum, crypto::Crypto, endpoint::http_endpoint},
|
||||
};
|
||||
use log::{info, warn};
|
||||
use tauri::{Manager, Window};
|
||||
|
||||
use crate::downloader::{ChecksumFetcher, FileDownloader};
|
||||
|
||||
pub struct ProgressNotifier {
|
||||
win: Window,
|
||||
}
|
||||
|
||||
impl ProgressNotifier {
|
||||
pub fn new(win: Window) -> Self {
|
||||
Self { win }
|
||||
}
|
||||
|
||||
fn notify(&self, progress: Option<f64>) {
|
||||
let _ = self.win.emit_all("app://update-progress", progress);
|
||||
}
|
||||
|
||||
fn notify_error(&self) {
|
||||
let _ = self.win.emit_all("app://update-error", ());
|
||||
}
|
||||
|
||||
fn notify_done(&self) {
|
||||
let _ = self.win.emit_and_trigger("app://update-done", ());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Installer {
|
||||
crypto: Crypto,
|
||||
}
|
||||
|
||||
impl Installer {
|
||||
pub fn new(api_key: Vec<u8>) -> Self {
|
||||
Self {
|
||||
crypto: Crypto::new(api_key),
|
||||
}
|
||||
}
|
||||
|
||||
async fn install(&self, path: &str, checksum: &str) -> anyhow::Result<()> {
|
||||
let service_endpoint = http_endpoint().await?;
|
||||
|
||||
let request = UpdateGuiRequest {
|
||||
path: path.to_string(),
|
||||
checksum: checksum.to_string(),
|
||||
};
|
||||
let payload = self.crypto.encrypt(&request)?;
|
||||
|
||||
reqwest::Client::default()
|
||||
.post(format!("{}/update-gui", service_endpoint))
|
||||
.body(payload)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuiUpdater {
|
||||
version: String,
|
||||
notifier: Arc<ProgressNotifier>,
|
||||
installer: Installer,
|
||||
}
|
||||
|
||||
impl GuiUpdater {
|
||||
pub fn new(version: String, notifier: ProgressNotifier, installer: Installer) -> Self {
|
||||
Self {
|
||||
version,
|
||||
notifier: Arc::new(notifier),
|
||||
installer,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update(&self) {
|
||||
info!("Update GUI, version: {}", self.version);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let release_tag = "latest";
|
||||
#[cfg(not(debug_assertions))]
|
||||
let release_tag = format!("v{}", self.version);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let arch = "x86_64";
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let arch = "aarch64";
|
||||
|
||||
let file_url = format!(
|
||||
"https://github.com/yuezk/GlobalProtect-openconnect/releases/download/{}/gpgui_{}_{}.bin.tar.xz",
|
||||
release_tag, self.version, arch
|
||||
);
|
||||
let checksum_url = format!("{}.sha256", file_url);
|
||||
|
||||
info!("Downloading file: {}", file_url);
|
||||
|
||||
let dl = FileDownloader::new(&file_url);
|
||||
let cf = ChecksumFetcher::new(&checksum_url);
|
||||
let notifier = Arc::clone(&self.notifier);
|
||||
|
||||
dl.on_progress(move |progress| notifier.notify(progress));
|
||||
|
||||
let res = tokio::try_join!(dl.download(), cf.fetch());
|
||||
|
||||
let (file, checksum) = match res {
|
||||
Ok((file, checksum)) => (file, checksum),
|
||||
Err(err) => {
|
||||
warn!("Download error: {}", err);
|
||||
self.notifier.notify_error();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let path = file.into_temp_path();
|
||||
let file_path = path.to_string_lossy();
|
||||
|
||||
if let Err(err) = verify_checksum(&file_path, &checksum) {
|
||||
warn!("Checksum error: {}", err);
|
||||
self.notifier.notify_error();
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Checksum success");
|
||||
|
||||
if let Err(err) = self.installer.install(&file_path, &checksum).await {
|
||||
warn!("Install error: {}", err);
|
||||
self.notifier.notify_error();
|
||||
} else {
|
||||
info!("Install success");
|
||||
self.notifier.notify_done();
|
||||
}
|
||||
}
|
||||
}
|
52
apps/gpgui-helper/src-tauri/tauri.conf.json
Normal file
52
apps/gpgui-helper/src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"beforeBuildCommand": "pnpm build",
|
||||
"devPath": "http://localhost:1421",
|
||||
"distDir": "../dist",
|
||||
"withGlobalTauri": false
|
||||
},
|
||||
"package": {
|
||||
"productName": "gpgui-helper"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false,
|
||||
"window": {
|
||||
"all": false,
|
||||
"startDragging": true
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": false,
|
||||
"targets": "deb",
|
||||
"identifier": "com.yuezk.gpgui-helper",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"title": "GlobalProtect GUI Helper",
|
||||
"center": true,
|
||||
"resizable": true,
|
||||
"width": 500,
|
||||
"height": 100,
|
||||
"minWidth": 500,
|
||||
"minHeight": 100,
|
||||
"maxWidth": 500,
|
||||
"maxHeight": 100,
|
||||
"label": "main",
|
||||
"decorations": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user