forked from Drop-OSS/archived-drop-app
Some checks failed
publish / publish-tauri (, ubuntu-22.04) (release) Failing after 14m38s
publish / publish-tauri (, windows-latest) (release) Has been cancelled
publish / publish-tauri (--target aarch64-apple-darwin, macos-14) (release) Has been cancelled
publish / publish-tauri (--target aarch64-unknown-linux-gnu, ubuntu-22.04-arm) (release) Has been cancelled
publish / publish-tauri (--target x86_64-apple-darwin, macos-14) (release) Has been cancelled
* feat: depot api downloads * feat: frontend fixes and experimental webview store * feat: sync downloader * feat: cleanup and fixes * feat: encrypted database and fixed resuming * feat: launch option selector * fix: autostart when no options * fix: clippy * fix: clippy x2 * feat: executor launch * feat: executor launch * feat: not installed error handling * feat: better offline handling * feat: dependency popup * fix: cancelation and resuming issues * feat: dedup by platform * feat: new ui for additional components and fix dl manager clog * feat: auto-queue dependencies * feat: depot scanning and ranking * feat: new library fetching stack * In-app store page (Windows + macOS) (#176) * feat: async store loading * feat: fix overscroll behaviour * fix: query params in server protocol * fix: clippy
192 lines
6.8 KiB
Rust
192 lines
6.8 KiB
Rust
use std::{
|
|
fs::{self, File},
|
|
io::Read,
|
|
sync::LazyLock,
|
|
time::Duration,
|
|
};
|
|
|
|
use client::{app_state::AppState, app_status::AppStatus};
|
|
use database::db::DATA_ROOT_DIR;
|
|
use http::Extensions;
|
|
use log::{debug, info, warn};
|
|
use reqwest::Certificate;
|
|
use reqwest_middleware::{
|
|
ClientBuilder, ClientWithMiddleware, Error, Middleware, Next, Result,
|
|
reqwest::{Request, Response},
|
|
};
|
|
use serde::Deserialize;
|
|
use tauri::{AppHandle, Emitter, Manager, async_runtime::Mutex};
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DropHealthcheck {
|
|
app_name: String,
|
|
}
|
|
impl DropHealthcheck {
|
|
pub fn app_name(&self) -> &String {
|
|
&self.app_name
|
|
}
|
|
}
|
|
static DROP_CERT_BUNDLE: LazyLock<Vec<Certificate>> = LazyLock::new(fetch_certificates);
|
|
pub static DROP_CLIENT_SYNC: LazyLock<reqwest::blocking::Client> = LazyLock::new(get_client_sync);
|
|
pub static DROP_CLIENT_ASYNC: LazyLock<ClientWithMiddleware> = LazyLock::new(get_client_async);
|
|
pub static DROP_CLIENT_WS_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(get_client_ws);
|
|
|
|
pub static DROP_APP_HANDLE: LazyLock<Mutex<Option<AppHandle>>> = LazyLock::new(|| Mutex::new(None));
|
|
|
|
struct AutoOfflineMiddleware;
|
|
|
|
#[async_trait::async_trait]
|
|
impl Middleware for AutoOfflineMiddleware {
|
|
async fn handle(
|
|
&self,
|
|
req: Request,
|
|
extensions: &mut Extensions,
|
|
next: Next<'_>,
|
|
) -> Result<Response> {
|
|
let res = next.run(req, extensions).await;
|
|
match res {
|
|
Ok(res) => {
|
|
tauri::async_runtime::spawn(async move {
|
|
let lock = DROP_APP_HANDLE.lock().await;
|
|
if let Some(app_handle) = &*lock {
|
|
let state = app_handle.state::<std::sync::nonpoison::Mutex<AppState>>();
|
|
let mut state_lock = state.lock();
|
|
if state_lock.status == AppStatus::Offline {
|
|
state_lock.status = AppStatus::SignedIn;
|
|
app_handle
|
|
.emit("update_state", &*state_lock)
|
|
.expect("failed to emit state update");
|
|
}
|
|
};
|
|
});
|
|
|
|
Ok(res)
|
|
}
|
|
Err(err) => match err {
|
|
Error::Middleware(error) => Err(Error::Middleware(error)),
|
|
Error::Reqwest(error) => {
|
|
if error.is_connect() {
|
|
// Spawn to defer this action - the state will most likely be locked
|
|
tauri::async_runtime::spawn(async move {
|
|
let lock = DROP_APP_HANDLE.lock().await;
|
|
if let Some(app_handle) = &*lock {
|
|
let state =
|
|
app_handle.state::<std::sync::nonpoison::Mutex<AppState>>();
|
|
let mut state_lock = state.lock();
|
|
state_lock.status = AppStatus::Offline;
|
|
app_handle
|
|
.emit("update_state", &*state_lock)
|
|
.expect("failed to emit state update");
|
|
};
|
|
});
|
|
};
|
|
Err(Error::Reqwest(error))
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn fetch_certificates() -> Vec<Certificate> {
|
|
let certificate_dir = DATA_ROOT_DIR.join("certificates");
|
|
|
|
let mut certs = Vec::new();
|
|
match fs::read_dir(certificate_dir) {
|
|
Ok(c) => {
|
|
for entry in c {
|
|
match entry {
|
|
Ok(c) => {
|
|
let mut buf = Vec::new();
|
|
match File::open(c.path()) {
|
|
Ok(f) => f,
|
|
Err(e) => {
|
|
warn!(
|
|
"Failed to open file at {} with error {}",
|
|
c.path().display(),
|
|
e
|
|
);
|
|
continue;
|
|
}
|
|
}
|
|
.read_to_end(&mut buf)
|
|
.unwrap_or_else(|e| {
|
|
panic!(
|
|
"Failed to read to end of certificate file {} with error {}",
|
|
c.path().display(),
|
|
e
|
|
)
|
|
});
|
|
|
|
match Certificate::from_pem_bundle(&buf) {
|
|
Ok(certificates) => {
|
|
for cert in certificates {
|
|
certs.push(cert);
|
|
}
|
|
info!(
|
|
"added {} certificate(s) from {}",
|
|
certs.len(),
|
|
c.file_name().display()
|
|
);
|
|
}
|
|
Err(e) => warn!(
|
|
"Invalid certificate file {} with error {}",
|
|
c.path().display(),
|
|
e
|
|
),
|
|
}
|
|
}
|
|
Err(_) => todo!(),
|
|
}
|
|
}
|
|
}
|
|
Err(e) => {
|
|
debug!("not loading certificates due to error: {e}");
|
|
}
|
|
};
|
|
certs
|
|
}
|
|
|
|
pub fn get_client_sync() -> reqwest::blocking::Client {
|
|
let mut client = reqwest::blocking::ClientBuilder::new();
|
|
|
|
for cert in DROP_CERT_BUNDLE.iter() {
|
|
client = client.add_root_certificate(cert.clone());
|
|
}
|
|
client
|
|
.use_rustls_tls()
|
|
.user_agent("Drop Desktop Client")
|
|
.connect_timeout(Duration::from_millis(1500))
|
|
.build()
|
|
.expect("Failed to build synchronous client")
|
|
}
|
|
pub fn get_client_async() -> ClientWithMiddleware {
|
|
let mut client = reqwest::ClientBuilder::new();
|
|
|
|
for cert in DROP_CERT_BUNDLE.iter() {
|
|
client = client.add_root_certificate(cert.clone());
|
|
}
|
|
let normal_client = client
|
|
.use_rustls_tls()
|
|
.user_agent("Drop Desktop Client")
|
|
.build()
|
|
.expect("Failed to build asynchronous client");
|
|
|
|
ClientBuilder::new(normal_client)
|
|
.with(AutoOfflineMiddleware)
|
|
.build()
|
|
}
|
|
pub fn get_client_ws() -> reqwest::Client {
|
|
let mut client = reqwest::ClientBuilder::new();
|
|
|
|
for cert in DROP_CERT_BUNDLE.iter() {
|
|
client = client.add_root_certificate(cert.clone());
|
|
}
|
|
client
|
|
.use_rustls_tls()
|
|
.user_agent("Drop Desktop Client")
|
|
.http1_only()
|
|
.build()
|
|
.expect("Failed to build websocket client")
|
|
}
|