Compare commits

..

6 Commits

Author SHA1 Message Date
ea6fa551a2 chore: Remove all unwraps from util.rs and add state_lock macro
Signed-off-by: quexeky <git@quexeky.dev>
2025-09-04 18:02:36 +10:00
be4fc2d37a fix: Add lint and remove all unwraps from lib.rs
Signed-off-by: quexeky <git@quexeky.dev>
2025-09-04 17:29:52 +10:00
7e70a17a43 Bump version to v0.3.3 2025-08-28 18:23:12 +10:00
8d61a68b8a Add placeholders to unfinished pages (#126)
* feat: add placeholders for community & news pages

* feat: add placeholder to interface in settings menu
2025-08-28 18:22:33 +10:00
44a1be6991 Fix for multi-version downloads (#125)
* fix: multi version downloads

* fix: remove debug utils

* fix: clippy
2025-08-28 18:05:05 +10:00
4f5fccf0c1 Add umu-run discovery (#122)
Signed-off-by: quexeky <git@quexeky.dev>
2025-08-28 18:05:05 +10:00
17 changed files with 251 additions and 120 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "view", "name": "view",
"private": true, "private": true,
"version": "0.3.2", "version": "0.3.3",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "nuxt generate", "build": "nuxt generate",

25
main/pages/community.vue Normal file
View File

@ -0,0 +1,25 @@
<template>
<div class="grow w-full h-full flex items-center justify-center">
<div class="flex flex-col items-center">
<WrenchScrewdriverIcon
class="h-12 w-12 text-blue-600"
aria-hidden="true"
/>
<div class="mt-3 text-center sm:mt-5">
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
Under construction
</h1>
<div class="mt-4">
<p class="text-sm text-zinc-400 max-w-lg">
This page hasn't been implemented yet.
</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {
WrenchScrewdriverIcon,
} from "@heroicons/vue/20/solid";
</script>

View File

@ -1,16 +0,0 @@
<template>
<div class="mx-auto flex flex-col items-center gap-y-4 max-w-2xl py-32 sm:py-48 lg:py-56">
<div>
<Wordmark />
</div>
<div class="text-center">
<h1 class="text-balance text-4xl font-bold font-display tracking-tight text-zinc-100 sm:text-6xl">
Under construction
</h1>
<p class="mt-6 text-lg leading-8 text-zinc-400">
Yes, we know. We're working on it <a class="text-white" target="_blank"
href="https://github.com/Drop-OSS/drop-app/issues/52">here.</a>
</p>
</div>
</div>
</template>

25
main/pages/news.vue Normal file
View File

@ -0,0 +1,25 @@
<template>
<div class="grow w-full h-full flex items-center justify-center">
<div class="flex flex-col items-center">
<WrenchScrewdriverIcon
class="h-12 w-12 text-blue-600"
aria-hidden="true"
/>
<div class="mt-3 text-center sm:mt-5">
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
Under construction
</h1>
<div class="mt-4">
<p class="text-sm text-zinc-400 max-w-lg">
This page hasn't been implemented yet.
</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {
WrenchScrewdriverIcon,
} from "@heroicons/vue/20/solid";
</script>

View File

@ -1,16 +0,0 @@
<template>
<div class="mx-auto flex flex-col items-center gap-y-4 max-w-2xl py-32 sm:py-48 lg:py-56">
<div>
<Wordmark />
</div>
<div class="text-center">
<h1 class="text-balance text-4xl font-bold font-display tracking-tight text-zinc-100 sm:text-6xl">
Under construction
</h1>
<p class="mt-6 text-lg leading-8 text-zinc-400">
Yes, we know. We're working on it <a class="text-white" target="_blank"
href="https://github.com/Drop-OSS/drop-app/issues/52">here.</a>
</p>
</div>
</div>
</template>

View File

@ -1,7 +1,23 @@
<template> <template>
<div class="grow w-full h-full flex items-center justify-center">
<div class="flex flex-col items-center">
<WrenchScrewdriverIcon
class="h-12 w-12 text-blue-600"
aria-hidden="true"
/>
<div class="mt-3 text-center sm:mt-5">
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
Under construction
</h1>
<div class="mt-4">
<p class="text-sm text-zinc-400 max-w-lg">
This page hasn't been implemented yet.
</p>
</div>
</div>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { WrenchScrewdriverIcon } from "@heroicons/vue/20/solid";
</script> </script>

2
src-tauri/Cargo.lock generated
View File

@ -1284,7 +1284,7 @@ dependencies = [
[[package]] [[package]]
name = "drop-app" name = "drop-app"
version = "0.3.2" version = "0.3.3"
dependencies = [ dependencies = [
"atomic-instant-full", "atomic-instant-full",
"bitcode", "bitcode",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "drop-app" name = "drop-app"
version = "0.3.2" version = "0.3.3"
description = "The client application for the open-source, self-hosted game distribution platform Drop" description = "The client application for the open-source, self-hosted game distribution platform Drop"
authors = ["Drop OSS"] authors = ["Drop OSS"]
edition = "2024" edition = "2024"

View File

@ -23,6 +23,7 @@ pub enum RemoteAccessError {
ManifestDownloadFailed(StatusCode, String), ManifestDownloadFailed(StatusCode, String),
OutOfSync, OutOfSync,
Cache(std::io::Error), Cache(std::io::Error),
CorruptedState,
} }
impl Display for RemoteAccessError { impl Display for RemoteAccessError {
@ -81,6 +82,10 @@ impl Display for RemoteAccessError {
"server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other" "server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other"
), ),
RemoteAccessError::Cache(error) => write!(f, "Cache Error: {error}"), RemoteAccessError::Cache(error) => write!(f, "Cache Error: {error}"),
RemoteAccessError::CorruptedState => write!(
f,
"Drop encountered a corrupted internal state. Please report this to the developers, with details of reproduction."
),
} }
} }
} }

View File

@ -22,8 +22,8 @@ use crate::remote::requests::generate_url;
use crate::remote::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC}; use crate::remote::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::fs::{OpenOptions, create_dir_all}; use std::fs::{create_dir_all, OpenOptions};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -242,12 +242,8 @@ impl GameDownloadAgent {
let mut buckets = Vec::new(); let mut buckets = Vec::new();
let mut current_bucket = DownloadBucket { let mut current_buckets = HashMap::<String, DownloadBucket>::new();
game_id: game_id.clone(), let mut current_bucket_sizes = HashMap::<String, usize>::new();
version: self.version.clone(),
drops: Vec::new(),
};
let mut current_bucket_size = 0;
for (raw_path, chunk) in manifest { for (raw_path, chunk) in manifest {
let path = base_path.join(Path::new(&raw_path)); let path = base_path.join(Path::new(&raw_path));
@ -282,28 +278,41 @@ impl GameDownloadAgent {
buckets.push(DownloadBucket { buckets.push(DownloadBucket {
game_id: game_id.clone(), game_id: game_id.clone(),
version: self.version.clone(), version: chunk.version_name.clone(),
drops: vec![drop], drops: vec![drop],
}); });
continue; continue;
} }
if current_bucket_size + *length >= TARGET_BUCKET_SIZE let current_bucket_size = current_bucket_sizes
.entry(chunk.version_name.clone())
.or_insert_with(|| 0);
let c_version_name = chunk.version_name.clone();
let c_game_id = game_id.clone();
let current_bucket = current_buckets
.entry(chunk.version_name.clone())
.or_insert_with(|| DownloadBucket {
game_id: c_game_id,
version: c_version_name,
drops: vec![],
});
if *current_bucket_size + length >= TARGET_BUCKET_SIZE
&& !current_bucket.drops.is_empty() && !current_bucket.drops.is_empty()
{ {
// Move current bucket into list and make a new one // Move current bucket into list and make a new one
buckets.push(current_bucket); buckets.push(current_bucket.clone());
current_bucket = DownloadBucket { *current_bucket = DownloadBucket {
game_id: game_id.clone(), game_id: game_id.clone(),
version: self.version.clone(), version: chunk.version_name.clone(),
drops: Vec::new(), drops: vec![],
}; };
current_bucket_size = 0; *current_bucket_size = 0;
} }
current_bucket.drops.push(drop); current_bucket.drops.push(drop);
current_bucket_size += *length; *current_bucket_size += *length;
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -312,8 +321,10 @@ impl GameDownloadAgent {
} }
} }
if !current_bucket.drops.is_empty() { for (_, bucket) in current_buckets.into_iter() {
buckets.push(current_bucket); if !bucket.drops.is_empty() {
buckets.push(bucket);
}
} }
info!("buckets: {}", buckets.len()); info!("buckets: {}", buckets.len());
@ -348,27 +359,46 @@ impl GameDownloadAgent {
.build() .build()
.unwrap(); .unwrap();
let buckets = self.buckets.lock().unwrap();
let mut download_contexts = HashMap::<String, DownloadContext>::new();
let versions = buckets
.iter()
.map(|e| &e.version)
.collect::<HashSet<_>>()
.into_iter().cloned()
.collect::<Vec<String>>();
info!("downloading across these versions: {versions:?}");
let completed_contexts = Arc::new(boxcar::Vec::new()); let completed_contexts = Arc::new(boxcar::Vec::new());
let completed_indexes_loop_arc = completed_contexts.clone(); let completed_indexes_loop_arc = completed_contexts.clone();
let download_context = DROP_CLIENT_SYNC for version in versions {
.post(generate_url(&["/api/v2/client/context"], &[]).unwrap()) let download_context = DROP_CLIENT_SYNC
.json(&ManifestBody { .post(generate_url(&["/api/v2/client/context"], &[]).unwrap())
game: self.id.clone(), .json(&ManifestBody {
version: self.version.clone(), game: self.id.clone(),
}) version: version.clone(),
.header("Authorization", generate_authorization_header()) })
.send()?; .header("Authorization", generate_authorization_header())
.send()?;
if download_context.status() != 200 { if download_context.status() != 200 {
return Err(RemoteAccessError::InvalidResponse(download_context.json()?)); return Err(RemoteAccessError::InvalidResponse(download_context.json()?));
}
let download_context = download_context.json::<DownloadContext>()?;
info!(
"download context: ({}) {}",
&version, download_context.context
);
download_contexts.insert(version, download_context);
} }
let download_context = &download_context.json::<DownloadContext>()?; let download_contexts = &download_contexts;
info!("download context: {}", download_context.context);
let buckets = self.buckets.lock().unwrap();
pool.scope(|scope| { pool.scope(|scope| {
let context_map = self.context_map.lock().unwrap(); let context_map = self.context_map.lock().unwrap();
for (index, bucket) in buckets.iter().enumerate() { for (index, bucket) in buckets.iter().enumerate() {
@ -400,6 +430,11 @@ impl GameDownloadAgent {
let sender = self.sender.clone(); let sender = self.sender.clone();
let download_context = download_contexts
.get(&bucket.version)
.ok_or(RemoteAccessError::CorruptedState)
.unwrap();
scope.spawn(move |_| { scope.spawn(move |_| {
// 3 attempts // 3 attempts
for i in 0..RETRY_COUNT { for i in 0..RETRY_COUNT {

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize)]
// Drops go in buckets // Drops go in buckets
pub struct DownloadDrop { pub struct DownloadDrop {
pub index: usize, pub index: usize,
@ -14,7 +14,7 @@ pub struct DownloadDrop {
pub permissions: u32, pub permissions: u32,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize)]
pub struct DownloadBucket { pub struct DownloadBucket {
pub game_id: String, pub game_id: String,
pub version: String, pub version: String,

View File

@ -4,6 +4,7 @@
#![feature(duration_millis_float)] #![feature(duration_millis_float)]
#![feature(iterator_try_collect)] #![feature(iterator_try_collect)]
#![deny(clippy::all)] #![deny(clippy::all)]
#![deny(clippy::unwrap_used)]
mod database; mod database;
mod games; mod games;
@ -13,6 +14,7 @@ mod download_manager;
mod error; mod error;
mod process; mod process;
mod remote; mod remote;
mod utils;
use crate::database::scan::scan_install_dirs; use crate::database::scan::scan_install_dirs;
use crate::process::commands::open_process_logs; use crate::process::commands::open_process_logs;
@ -45,7 +47,7 @@ use games::commands::{
}; };
use games::downloads::commands::download_game; use games::downloads::commands::download_game;
use games::library::{Game, update_game_configuration}; use games::library::{Game, update_game_configuration};
use log::{LevelFilter, debug, info, warn}; use log::{LevelFilter, debug, info, warn, error};
use log4rs::Config; use log4rs::Config;
use log4rs::append::console::ConsoleAppender; use log4rs::append::console::ConsoleAppender;
use log4rs::append::file::FileAppender; use log4rs::append::file::FileAppender;
@ -64,7 +66,7 @@ use serde::{Deserialize, Serialize};
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::panic::PanicHookInfo; use std::panic::PanicHookInfo;
use std::path::{Path, PathBuf}; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
@ -109,7 +111,7 @@ fn create_new_compat_info() -> Option<CompatInfo> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
return None; return None;
let has_umu_installed = *UMU_LAUNCHER_EXECUTABLE == PathBuf::new(); let has_umu_installed = UMU_LAUNCHER_EXECUTABLE.is_some();
Some(CompatInfo { Some(CompatInfo {
umu_installed: has_umu_installed, umu_installed: has_umu_installed,
}) })
@ -137,7 +139,7 @@ async fn setup(handle: AppHandle) -> AppState<'static> {
))) )))
.append(false) .append(false)
.build(DATA_ROOT_DIR.join("./drop.log")) .build(DATA_ROOT_DIR.join("./drop.log"))
.unwrap(); .expect("Failed to setup logfile");
let console = ConsoleAppender::builder() let console = ConsoleAppender::builder()
.encoder(Box::new(PatternEncoder::new( .encoder(Box::new(PatternEncoder::new(
@ -157,9 +159,9 @@ async fn setup(handle: AppHandle) -> AppState<'static> {
.appenders(vec!["logfile", "console"]) .appenders(vec!["logfile", "console"])
.build(LevelFilter::from_str(&log_level).expect("Invalid log level")), .build(LevelFilter::from_str(&log_level).expect("Invalid log level")),
) )
.unwrap(); .expect("Failed to build config");
log4rs::init_config(config).unwrap(); log4rs::init_config(config).expect("Failed to initialise log4rs");
let games = HashMap::new(); let games = HashMap::new();
let download_manager = Arc::new(DownloadManagerBuilder::build(handle.clone())); let download_manager = Arc::new(DownloadManagerBuilder::build(handle.clone()));
@ -370,42 +372,57 @@ pub fn run() {
.shadow(false) .shadow(false)
.data_directory(DATA_ROOT_DIR.join(".webview")) .data_directory(DATA_ROOT_DIR.join(".webview"))
.build() .build()
.unwrap(); .expect("Failed to build main window");
app.deep_link().on_open_url(move |event| { app.deep_link().on_open_url(move |event| {
debug!("handling drop:// url"); debug!("handling drop:// url");
let binding = event.urls(); let binding = event.urls();
let url = binding.first().unwrap(); let url = match binding.first() {
if url.host_str().unwrap() == "handshake" { Some(url) => url,
tauri::async_runtime::spawn(recieve_handshake( None => {
handle.clone(), warn!("No value recieved from deep link. Is this a drop server?");
url.path().to_string(), return;
)); }
};
if let Some("handshake") = url.host_str() {
tauri::async_runtime::spawn(recieve_handshake(
handle.clone(),
url.path().to_string(),
));
} }
}); });
let open_menu_item = MenuItem::with_id(app, "open", "Open", true, None::<&str>).expect("Failed to generate open menu item");
let sep = PredefinedMenuItem::separator(app).expect("Failed to generate menu separator item");
let quit_menu_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>).expect("Failed to generate quit menu item");
let menu = Menu::with_items( let menu = Menu::with_items(
app, app,
&[ &[
&MenuItem::with_id(app, "open", "Open", true, None::<&str>).unwrap(), &open_menu_item,
&PredefinedMenuItem::separator(app).unwrap(), &sep,
/* /*
&MenuItem::with_id(app, "show_library", "Library", true, None::<&str>)?, &MenuItem::with_id(app, "show_library", "Library", true, None::<&str>)?,
&MenuItem::with_id(app, "show_settings", "Settings", true, None::<&str>)?, &MenuItem::with_id(app, "show_settings", "Settings", true, None::<&str>)?,
&PredefinedMenuItem::separator(app)?, &PredefinedMenuItem::separator(app)?,
*/ */
&MenuItem::with_id(app, "quit", "Quit", true, None::<&str>).unwrap(), &quit_menu_item,
], ],
) )
.unwrap(); .expect("Failed to generate menu");
run_on_tray(|| { run_on_tray(|| {
TrayIconBuilder::new() TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone()) .icon(app.default_window_icon().expect("Failed to get default window icon").clone())
.menu(&menu) .menu(&menu)
.on_menu_event(|app, event| match event.id.as_ref() { .on_menu_event(|app, event| match event.id.as_ref() {
"open" => { "open" => {
app.webview_windows().get("main").unwrap().show().unwrap(); app.webview_windows()
.get("main")
.expect("Failed to get webview")
.show()
.expect("Failed to show window");
} }
"quit" => { "quit" => {
cleanup_and_exit(app, &app.state()); cleanup_and_exit(app, &app.state());
@ -422,15 +439,19 @@ pub fn run() {
{ {
let mut db_handle = borrow_db_mut_checked(); let mut db_handle = borrow_db_mut_checked();
if let Some(original) = db_handle.prev_database.take() { if let Some(original) = db_handle.prev_database.take() {
let canonicalised = match original.canonicalize() {
Ok(o) => o,
Err(_) => original,
};
warn!( warn!(
"Database corrupted. Original file at {}", "Database corrupted. Original file at {}",
original.canonicalize().unwrap().to_string_lossy() canonicalised.display()
); );
app.dialog() app.dialog()
.message( .message(format!(
"Database corrupted. A copy has been saved at: ".to_string() "Database corrupted. A copy has been saved at: {}",
+ original.to_str().unwrap(), canonicalised.display()
) ))
.title("Database corrupted") .title("Database corrupted")
.show(|_| {}); .show(|_| {});
} }
@ -463,7 +484,7 @@ pub fn run() {
.on_window_event(|window, event| { .on_window_event(|window, event| {
if let WindowEvent::CloseRequested { api, .. } = event { if let WindowEvent::CloseRequested { api, .. } = event {
run_on_tray(|| { run_on_tray(|| {
window.hide().unwrap(); window.hide().expect("Failed to close window in tray");
api.prevent_close(); api.prevent_close();
}); });
} }

View File

@ -5,7 +5,7 @@ use std::{
sync::LazyLock, sync::LazyLock,
}; };
use log::debug; use log::{debug, info};
use crate::{ use crate::{
AppState, AppState,
@ -31,29 +31,29 @@ impl ProcessHandler for NativeGameLauncher {
} }
} }
pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<PathBuf> = LazyLock::new(|| { pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
let x = get_umu_executable(); let x = get_umu_executable();
println!("{:?}", &x); info!("{:?}", &x);
x x
}); });
const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run"; const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run";
const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"]; const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"];
fn get_umu_executable() -> PathBuf { fn get_umu_executable() -> Option<PathBuf> {
if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) { if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) {
return PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE); return Some(PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE));
} }
for dir in UMU_INSTALL_DIRS { for dir in UMU_INSTALL_DIRS {
let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE); let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE);
if check_executable_exists(&p) { if check_executable_exists(&p) {
return p; return Some(p);
} }
} }
PathBuf::new() None
} }
fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool { fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool {
let has_umu_installed = Command::new(exec).stdout(Stdio::null()).spawn(); let has_umu_installed = Command::new(exec).stdout(Stdio::null()).output();
has_umu_installed.is_ok() has_umu_installed.is_ok()
} }
pub struct UMULauncher; pub struct UMULauncher;
@ -79,7 +79,7 @@ impl ProcessHandler for UMULauncher {
}; };
format!( format!(
"GAMEID={game_id} {umu:?} \"{launch}\" {args}", "GAMEID={game_id} {umu:?} \"{launch}\" {args}",
umu = &*UMU_LAUNCHER_EXECUTABLE, umu = UMU_LAUNCHER_EXECUTABLE.as_ref().unwrap(),
launch = launch_command, launch = launch_command,
args = args.join(" ") args = args.join(" ")
) )

View File

@ -14,6 +14,7 @@ use crate::{
AppState, AppStatus, AppState, AppStatus,
database::db::{DATA_ROOT_DIR, borrow_db_mut_checked}, database::db::{DATA_ROOT_DIR, borrow_db_mut_checked},
error::remote_access_error::RemoteAccessError, error::remote_access_error::RemoteAccessError,
state_lock,
}; };
#[derive(Deserialize)] #[derive(Deserialize)]
@ -37,16 +38,40 @@ fn fetch_certificates() -> Vec<Certificate> {
match entry { match entry {
Ok(c) => { Ok(c) => {
let mut buf = Vec::new(); let mut buf = Vec::new();
File::open(c.path()).unwrap().read_to_end(&mut buf).unwrap(); match File::open(c.path()) {
Ok(f) => f,
for cert in Certificate::from_pem_bundle(&buf).unwrap() { Err(e) => {
certs.push(cert); warn!(
"Failed to open file at {} with error {}",
c.path().display(),
e
);
continue;
}
}
.read_to_end(&mut buf)
.expect(&format!(
"Failed to read to end of certificate file {}",
c.path().display()
));
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
),
} }
info!(
"added {} certificate(s) from {}",
certs.len(),
c.file_name().into_string().unwrap()
);
} }
Err(_) => todo!(), Err(_) => todo!(),
} }
@ -65,7 +90,7 @@ pub fn get_client_sync() -> reqwest::blocking::Client {
for cert in DROP_CERT_BUNDLE.iter() { for cert in DROP_CERT_BUNDLE.iter() {
client = client.add_root_certificate(cert.clone()); client = client.add_root_certificate(cert.clone());
} }
client.use_rustls_tls().build().unwrap() client.use_rustls_tls().build().expect("Failed to build synchronous client")
} }
pub fn get_client_async() -> reqwest::Client { pub fn get_client_async() -> reqwest::Client {
let mut client = reqwest::ClientBuilder::new(); let mut client = reqwest::ClientBuilder::new();
@ -73,7 +98,7 @@ pub fn get_client_async() -> reqwest::Client {
for cert in DROP_CERT_BUNDLE.iter() { for cert in DROP_CERT_BUNDLE.iter() {
client = client.add_root_certificate(cert.clone()); client = client.add_root_certificate(cert.clone());
} }
client.use_rustls_tls().build().unwrap() client.use_rustls_tls().build().expect("Failed to build asynchronous client")
} }
pub fn get_client_ws() -> reqwest::Client { pub fn get_client_ws() -> reqwest::Client {
let mut client = reqwest::ClientBuilder::new(); let mut client = reqwest::ClientBuilder::new();
@ -81,7 +106,11 @@ pub fn get_client_ws() -> reqwest::Client {
for cert in DROP_CERT_BUNDLE.iter() { for cert in DROP_CERT_BUNDLE.iter() {
client = client.add_root_certificate(cert.clone()); client = client.add_root_certificate(cert.clone());
} }
client.use_rustls_tls().http1_only().build().unwrap() client
.use_rustls_tls()
.http1_only()
.build()
.expect("Failed to build websocket client")
} }
pub async fn use_remote_logic( pub async fn use_remote_logic(
@ -107,7 +136,7 @@ pub async fn use_remote_logic(
return Err(RemoteAccessError::InvalidEndpoint); return Err(RemoteAccessError::InvalidEndpoint);
} }
let mut app_state = state.lock().unwrap(); let mut app_state = state_lock!(state);
app_state.status = AppStatus::SignedOut; app_state.status = AppStatus::SignedOut;
drop(app_state); drop(app_state);

View File

@ -0,0 +1 @@
pub mod state_lock;

View File

@ -0,0 +1,6 @@
#[macro_export]
macro_rules! state_lock {
($state:expr) => {
$state.lock().expect("Failed to lock onto state")
};
}

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2.0.0", "$schema": "https://schema.tauri.app/config/2.0.0",
"productName": "Drop Desktop Client", "productName": "Drop Desktop Client",
"version": "0.3.2", "version": "0.3.3",
"identifier": "dev.drop.client", "identifier": "dev.drop.client",
"build": { "build": {
"beforeDevCommand": "yarn --cwd main dev --port 1432", "beforeDevCommand": "yarn --cwd main dev --port 1432",