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 283 additions and 94 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>

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,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,14 +359,28 @@ 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();
for version in versions {
let download_context = DROP_CLIENT_SYNC let download_context = DROP_CLIENT_SYNC
.post(generate_url(&["/api/v2/client/context"], &[]).unwrap()) .post(generate_url(&["/api/v2/client/context"], &[]).unwrap())
.json(&ManifestBody { .json(&ManifestBody {
game: self.id.clone(), game: self.id.clone(),
version: self.version.clone(), version: version.clone(),
}) })
.header("Authorization", generate_authorization_header()) .header("Authorization", generate_authorization_header())
.send()?; .send()?;
@ -364,11 +389,16 @@ impl GameDownloadAgent {
return Err(RemoteAccessError::InvalidResponse(download_context.json()?)); return Err(RemoteAccessError::InvalidResponse(download_context.json()?));
} }
let download_context = &download_context.json::<DownloadContext>()?; let download_context = download_context.json::<DownloadContext>()?;
info!(
"download context: ({}) {}",
&version, download_context.context
);
download_contexts.insert(version, download_context);
}
info!("download context: {}", download_context.context); let download_contexts = &download_contexts;
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

@ -172,7 +172,7 @@ pub fn download_game_bucket(
let raw_res = response.text().map_err(|e| { let raw_res = response.text().map_err(|e| {
ApplicationDownloadError::Communication(RemoteAccessError::FetchError(e.into())) ApplicationDownloadError::Communication(RemoteAccessError::FetchError(e.into()))
})?; })?;
info!("{}", raw_res); info!("{raw_res}");
if let Ok(err) = serde_json::from_str::<DropServerError>(&raw_res) { if let Ok(err) = serde_json::from_str::<DropServerError>(&raw_res) {
return Err(ApplicationDownloadError::Communication( return Err(ApplicationDownloadError::Communication(
RemoteAccessError::InvalidResponse(err), RemoteAccessError::InvalidResponse(err),
@ -196,8 +196,7 @@ pub fn download_game_bucket(
let length = raw_length.parse::<usize>().unwrap_or(0); let length = raw_length.parse::<usize>().unwrap_or(0);
let Some(drop) = bucket.drops.get(i) else { let Some(drop) = bucket.drops.get(i) else {
warn!( warn!(
"invalid number of Content-Lengths recieved: {}, {}", "invalid number of Content-Lengths recieved: {i}, {lengths}"
i, lengths
); );
return Err(ApplicationDownloadError::DownloadError); return Err(ApplicationDownloadError::DownloadError);
}; };

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;
@ -65,7 +67,6 @@ use std::fs::File;
use std::io::Write; use std::io::Write;
use std::panic::PanicHookInfo; use std::panic::PanicHookInfo;
use std::path::Path; use std::path::Path;
use std::process::{Command, Stdio};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
@ -110,13 +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 = Command::new(UMU_LAUNCHER_EXECUTABLE) let has_umu_installed = UMU_LAUNCHER_EXECUTABLE.is_some();
.stdout(Stdio::null())
.spawn();
if let Err(umu_error) = &has_umu_installed {
warn!("disabling windows support with error: {umu_error}");
}
let has_umu_installed = has_umu_installed.is_ok();
Some(CompatInfo { Some(CompatInfo {
umu_installed: has_umu_installed, umu_installed: has_umu_installed,
}) })
@ -144,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(
@ -164,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()));
@ -377,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,
None => {
warn!("No value recieved from deep link. Is this a drop server?");
return;
}
};
if let Some("handshake") = url.host_str() {
tauri::async_runtime::spawn(recieve_handshake( tauri::async_runtime::spawn(recieve_handshake(
handle.clone(), handle.clone(),
url.path().to_string(), 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());
@ -429,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(|_| {});
} }
@ -470,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

@ -1,4 +1,11 @@
use log::debug; use std::{
ffi::OsStr,
path::PathBuf,
process::{Command, Stdio},
sync::LazyLock,
};
use log::{debug, info};
use crate::{ use crate::{
AppState, AppState,
@ -24,7 +31,31 @@ impl ProcessHandler for NativeGameLauncher {
} }
} }
pub const UMU_LAUNCHER_EXECUTABLE: &str = "umu-run"; pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
let x = get_umu_executable();
info!("{:?}", &x);
x
});
const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run";
const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"];
fn get_umu_executable() -> Option<PathBuf> {
if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) {
return Some(PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE));
}
for dir in UMU_INSTALL_DIRS {
let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE);
if check_executable_exists(&p) {
return Some(p);
}
}
None
}
fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool {
let has_umu_installed = Command::new(exec).stdout(Stdio::null()).output();
has_umu_installed.is_ok()
}
pub struct UMULauncher; pub struct UMULauncher;
impl ProcessHandler for UMULauncher { impl ProcessHandler for UMULauncher {
fn create_launch_process( fn create_launch_process(
@ -47,8 +78,8 @@ impl ProcessHandler for UMULauncher {
None => game_version.game_id.clone(), None => game_version.game_id.clone(),
}; };
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(" ")
) )
@ -80,7 +111,10 @@ impl ProcessHandler for AsahiMuvmLauncher {
game_version, game_version,
current_dir, current_dir,
); );
let mut args_cmd = umu_string.split("umu-run").collect::<Vec<&str>>().into_iter(); let mut args_cmd = umu_string
.split("umu-run")
.collect::<Vec<&str>>()
.into_iter();
let args = args_cmd.next().unwrap().trim(); let args = args_cmd.next().unwrap().trim();
let cmd = format!("umu-run{}", args_cmd.next().unwrap()); let cmd = format!("umu-run{}", args_cmd.next().unwrap());

View File

@ -130,7 +130,7 @@ pub fn auth_initiate_code(app: AppHandle) -> Result<String, RemoteAccessError> {
let code = auth_initiate_logic("code".to_string())?; let code = auth_initiate_logic("code".to_string())?;
let header_code = code.clone(); let header_code = code.clone();
println!("using code: {} to sign in", code); println!("using code: {code} to sign in");
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
let load = async || -> Result<(), RemoteAccessError> { let load = async || -> Result<(), RemoteAccessError> {

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,17 +38,41 @@ 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,
Err(e) => {
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()
));
for cert in Certificate::from_pem_bundle(&buf).unwrap() { match Certificate::from_pem_bundle(&buf) {
Ok(certificates) => {
for cert in certificates {
certs.push(cert); certs.push(cert);
} }
info!( info!(
"added {} certificate(s) from {}", "added {} certificate(s) from {}",
certs.len(), certs.len(),
c.file_name().into_string().unwrap() c.file_name().display()
); );
} }
Err(e) => warn!(
"Invalid certificate file {} with error {}",
c.path().display(),
e
),
}
}
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",