Process manager fixes (#71)
* fix: launching on linux * feat: #70 * feat: add dummy store page * feat: add store redir and refresh button to library * feat: cache first object fetching * feat: Remove let_chains feature and update to Rust 2024 Signed-off-by: quexeky <git@quexeky.dev> * feat: Check for if process was manually stopped Signed-off-by: quexeky <git@quexeky.dev> * fix: use bitcode instead of serde * chore: remove logs * fix: clippy * fix: clippy 2 * fix: swap to stop icon --------- Signed-off-by: quexeky <git@quexeky.dev> Co-authored-by: quexeky <git@quexeky.dev>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, Mutex,
|
||||
mpsc::{Receiver, Sender, channel},
|
||||
},
|
||||
thread::{spawn, JoinHandle},
|
||||
thread::{JoinHandle, spawn},
|
||||
};
|
||||
|
||||
use log::{debug, error, info, warn};
|
||||
@@ -212,13 +212,13 @@ impl DownloadManagerBuilder {
|
||||
if self.current_download_agent.is_some()
|
||||
&& self.download_queue.read().front().unwrap()
|
||||
== &self.current_download_agent.as_ref().unwrap().metadata()
|
||||
{
|
||||
debug!(
|
||||
"Current download agent: {:?}",
|
||||
self.current_download_agent.as_ref().unwrap().metadata()
|
||||
);
|
||||
return;
|
||||
}
|
||||
{
|
||||
debug!(
|
||||
"Current download agent: {:?}",
|
||||
self.current_download_agent.as_ref().unwrap().metadata()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("current download queue: {:?}", self.download_queue.read());
|
||||
|
||||
@@ -295,11 +295,12 @@ impl DownloadManagerBuilder {
|
||||
}
|
||||
fn manage_completed_signal(&mut self, meta: DownloadableMetadata) {
|
||||
debug!("got signal Completed");
|
||||
if let Some(interface) = &self.current_download_agent {
|
||||
if interface.metadata() == meta {
|
||||
self.remove_and_cleanup_front_download(&meta);
|
||||
}
|
||||
if let Some(interface) = &self.current_download_agent
|
||||
&& interface.metadata() == meta
|
||||
{
|
||||
self.remove_and_cleanup_front_download(&meta);
|
||||
}
|
||||
|
||||
self.push_ui_queue_update();
|
||||
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
}
|
||||
|
||||
@@ -148,9 +148,7 @@ impl DownloadManager {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
debug!(
|
||||
"moving download at index {current_index} to index {new_index}"
|
||||
);
|
||||
debug!("moving download at index {current_index} to index {new_index}");
|
||||
|
||||
let mut queue = self.edit();
|
||||
let to_move = queue.remove(current_index).unwrap();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod commands;
|
||||
pub mod download_manager_frontend;
|
||||
pub mod download_manager_builder;
|
||||
pub mod download_manager_frontend;
|
||||
pub mod downloadable;
|
||||
pub mod util;
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use serde_with::SerializeDisplay;
|
||||
|
||||
use super::{remote_access_error::RemoteAccessError};
|
||||
use super::remote_access_error::RemoteAccessError;
|
||||
|
||||
// TODO: Rename / separate from downloads
|
||||
#[derive(Debug, SerializeDisplay)]
|
||||
|
||||
@@ -13,6 +13,7 @@ pub enum ProcessError {
|
||||
IOError(Error),
|
||||
FormatError(String), // String errors supremacy
|
||||
InvalidPlatform,
|
||||
OpenerError(tauri_plugin_opener::Error)
|
||||
}
|
||||
|
||||
impl Display for ProcessError {
|
||||
@@ -22,12 +23,13 @@ impl Display for ProcessError {
|
||||
ProcessError::NotInstalled => "Game not installed",
|
||||
ProcessError::AlreadyRunning => "Game already running",
|
||||
ProcessError::NotDownloaded => "Game not downloaded",
|
||||
ProcessError::InvalidID => "Invalid Game ID",
|
||||
ProcessError::InvalidVersion => "Invalid Game version",
|
||||
ProcessError::InvalidID => "Invalid game ID",
|
||||
ProcessError::InvalidVersion => "Invalid game version",
|
||||
ProcessError::IOError(error) => &error.to_string(),
|
||||
ProcessError::InvalidPlatform => "This Game cannot be played on the current platform",
|
||||
ProcessError::InvalidPlatform => "This game cannot be played on the current platform",
|
||||
ProcessError::FormatError(e) => &format!("Failed to format template: {e}"),
|
||||
};
|
||||
ProcessError::OpenerError(error) => &format!("Failed to open directory: {error}"),
|
||||
};
|
||||
write!(f, "{s}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,14 +37,13 @@ pub fn fetch_game(
|
||||
game_id: String,
|
||||
state: tauri::State<'_, Mutex<AppState>>,
|
||||
) -> Result<FetchGameStruct, RemoteAccessError> {
|
||||
let res = offline!(
|
||||
offline!(
|
||||
state,
|
||||
fetch_game_logic,
|
||||
fetch_game_logic_offline,
|
||||
game_id,
|
||||
state
|
||||
);
|
||||
res
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -5,7 +5,9 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
database::{db::borrow_db_checked, models::data::GameDownloadStatus},
|
||||
download_manager::{download_manager_frontend::DownloadManagerSignal, downloadable::Downloadable},
|
||||
download_manager::{
|
||||
download_manager_frontend::DownloadManagerSignal, downloadable::Downloadable,
|
||||
},
|
||||
error::download_manager_error::DownloadManagerError,
|
||||
AppState,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::remote::auth::generate_authorization_header;
|
||||
use crate::remote::cache::{cache_object, get_cached_object, get_cached_object_db};
|
||||
use crate::remote::requests::make_request;
|
||||
use crate::AppState;
|
||||
use bitcode::{Encode, Decode};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FetchGameStruct {
|
||||
@@ -27,7 +28,7 @@ pub struct FetchGameStruct {
|
||||
version: Option<GameVersion>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, Encode, Decode)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Game {
|
||||
id: String,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![feature(fn_traits)]
|
||||
#![feature(duration_constructors)]
|
||||
#![deny(clippy::all)]
|
||||
|
||||
mod database;
|
||||
@@ -10,6 +11,7 @@ mod error;
|
||||
mod process;
|
||||
mod remote;
|
||||
|
||||
use crate::process::commands::open_process_logs;
|
||||
use crate::{database::db::DatabaseImpls, games::downloads::commands::resume_download};
|
||||
use client::commands::fetch_state;
|
||||
use client::{
|
||||
@@ -25,8 +27,8 @@ use database::models::data::GameDownloadStatus;
|
||||
use download_manager::commands::{
|
||||
cancel_game, move_download_in_queue, pause_downloads, resume_downloads,
|
||||
};
|
||||
use download_manager::download_manager_frontend::DownloadManager;
|
||||
use download_manager::download_manager_builder::DownloadManagerBuilder;
|
||||
use download_manager::download_manager_frontend::DownloadManager;
|
||||
use games::collections::commands::{
|
||||
add_game_to_collection, create_collection, delete_collection, delete_game_in_collection,
|
||||
fetch_collection, fetch_collections,
|
||||
@@ -69,6 +71,7 @@ use tauri::tray::TrayIconBuilder;
|
||||
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
use bitcode::{Encode, Decode};
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Eq, PartialEq)]
|
||||
pub enum AppStatus {
|
||||
@@ -81,7 +84,7 @@ pub enum AppStatus {
|
||||
ServerUnavailable,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize, Encode, Decode)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct User {
|
||||
id: String,
|
||||
@@ -225,7 +228,8 @@ pub fn custom_panic_handler(e: &PanicHookInfo) -> Option<()> {
|
||||
.as_secs()
|
||||
));
|
||||
let mut file = File::create_new(crash_file).ok()?;
|
||||
file.write_all(format!("Drop crashed with the following panic:\n{e}").as_bytes()).ok()?;
|
||||
file.write_all(format!("Drop crashed with the following panic:\n{e}").as_bytes())
|
||||
.ok()?;
|
||||
drop(file);
|
||||
|
||||
Some(())
|
||||
@@ -235,11 +239,11 @@ pub fn custom_panic_handler(e: &PanicHookInfo) -> Option<()> {
|
||||
pub fn run() {
|
||||
panic::set_hook(Box::new(|e| {
|
||||
let _ = custom_panic_handler(e);
|
||||
let dft = panic::take_hook();
|
||||
dft.call((e,));
|
||||
println!("{e}");
|
||||
}));
|
||||
|
||||
let mut builder = tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_dialog::init());
|
||||
|
||||
@@ -299,6 +303,7 @@ pub fn run() {
|
||||
kill_game,
|
||||
toggle_autostart,
|
||||
get_autostart_enabled,
|
||||
open_process_logs
|
||||
])
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
|
||||
@@ -38,3 +38,13 @@ pub fn kill_game(
|
||||
.kill_game(game_id)
|
||||
.map_err(ProcessError::IOError)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn open_process_logs(
|
||||
game_id: String,
|
||||
state: tauri::State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), ProcessError> {
|
||||
let state_lock = state.lock().unwrap();
|
||||
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||
process_manager_lock.open_process_logs(game_id)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::OpenOptions,
|
||||
fs::{OpenOptions, create_dir_all},
|
||||
io::{self},
|
||||
path::PathBuf,
|
||||
process::{Command, ExitStatus},
|
||||
str::FromStr,
|
||||
sync::{Arc, Mutex},
|
||||
thread::spawn,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use dynfmt::Format;
|
||||
@@ -14,11 +15,13 @@ use dynfmt::SimpleCurlyFormat;
|
||||
use log::{debug, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shared_child::SharedChild;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
|
||||
use crate::{
|
||||
AppState, DB,
|
||||
database::{
|
||||
db::{borrow_db_mut_checked, DATA_ROOT_DIR},
|
||||
db::{DATA_ROOT_DIR, borrow_db_mut_checked},
|
||||
models::data::{
|
||||
ApplicationTransientStatus, DownloadType, DownloadableMetadata, GameDownloadStatus,
|
||||
GameVersion,
|
||||
@@ -26,13 +29,18 @@ use crate::{
|
||||
},
|
||||
error::process_error::ProcessError,
|
||||
games::{library::push_game_update, state::GameStatusManager},
|
||||
AppState, DB,
|
||||
};
|
||||
|
||||
pub struct RunningProcess {
|
||||
handle: Arc<SharedChild>,
|
||||
start: SystemTime,
|
||||
manually_killed: bool,
|
||||
}
|
||||
|
||||
pub struct ProcessManager<'a> {
|
||||
current_platform: Platform,
|
||||
log_output_dir: PathBuf,
|
||||
processes: HashMap<String, Arc<SharedChild>>,
|
||||
processes: HashMap<String, RunningProcess>,
|
||||
app_handle: AppHandle,
|
||||
game_launchers: HashMap<(Platform, Platform), &'a (dyn ProcessHandler + Sync + Send + 'static)>,
|
||||
}
|
||||
@@ -77,10 +85,11 @@ impl ProcessManager<'_> {
|
||||
}
|
||||
|
||||
pub fn kill_game(&mut self, game_id: String) -> Result<(), io::Error> {
|
||||
match self.processes.get(&game_id) {
|
||||
Some(child) => {
|
||||
child.kill()?;
|
||||
child.wait()?;
|
||||
match self.processes.get_mut(&game_id) {
|
||||
Some(process) => {
|
||||
process.manually_killed = true;
|
||||
process.handle.kill()?;
|
||||
process.handle.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(io::Error::new(
|
||||
@@ -90,15 +99,26 @@ impl ProcessManager<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_process_logs(&mut self, game_id: String) -> Result<(), ProcessError> {
|
||||
let dir = self.log_output_dir.join(game_id);
|
||||
self.app_handle
|
||||
.opener()
|
||||
.open_path(dir.to_str().unwrap(), None::<&str>)
|
||||
.map_err(ProcessError::OpenerError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_process_finish(&mut self, game_id: String, result: Result<ExitStatus, std::io::Error>) {
|
||||
if !self.processes.contains_key(&game_id) {
|
||||
warn!("process on_finish was called, but game_id is no longer valid. finished with result: {result:?}");
|
||||
warn!(
|
||||
"process on_finish was called, but game_id is no longer valid. finished with result: {result:?}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("process for {:?} exited with {:?}", &game_id, result);
|
||||
|
||||
self.processes.remove(&game_id);
|
||||
let process = self.processes.remove(&game_id).unwrap();
|
||||
|
||||
let mut db_handle = borrow_db_mut_checked();
|
||||
let meta = db_handle
|
||||
@@ -114,26 +134,32 @@ impl ProcessManager<'_> {
|
||||
version_name,
|
||||
install_dir,
|
||||
}) = current_state
|
||||
&& let Ok(exit_code) = result
|
||||
&& exit_code.success()
|
||||
{
|
||||
if let Ok(exit_code) = result {
|
||||
if exit_code.success() {
|
||||
db_handle.applications.game_statuses.insert(
|
||||
game_id.clone(),
|
||||
GameDownloadStatus::Installed {
|
||||
version_name: version_name.to_string(),
|
||||
install_dir: install_dir.to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
db_handle.applications.game_statuses.insert(
|
||||
game_id.clone(),
|
||||
GameDownloadStatus::Installed {
|
||||
version_name: version_name.to_string(),
|
||||
install_dir: install_dir.to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
drop(db_handle);
|
||||
|
||||
let elapsed = process.start.elapsed().unwrap_or(Duration::ZERO);
|
||||
// If we started and ended really quickly, something might've gone wrong
|
||||
// Or if the status isn't 0
|
||||
// Or if it's an error
|
||||
if !process.manually_killed
|
||||
&& (elapsed.as_secs() <= 2 || result.is_err() || !result.unwrap().success())
|
||||
{
|
||||
warn!("drop detected that the game {game_id} may have failed to launch properly");
|
||||
let _ = self.app_handle.emit("launch_external_error", &game_id);
|
||||
}
|
||||
|
||||
let status = GameStatusManager::fetch_state(&game_id);
|
||||
|
||||
push_game_update(&self.app_handle, &game_id, None, status);
|
||||
|
||||
// TODO better management
|
||||
}
|
||||
|
||||
pub fn valid_platform(&self, platform: &Platform) -> Result<bool, String> {
|
||||
@@ -156,7 +182,7 @@ impl ProcessManager<'_> {
|
||||
{
|
||||
Some(GameDownloadStatus::Installed { version_name, .. }) => version_name,
|
||||
Some(GameDownloadStatus::SetupRequired { .. }) => {
|
||||
return Err(ProcessError::SetupRequired)
|
||||
return Err(ProcessError::SetupRequired);
|
||||
}
|
||||
_ => return Err(ProcessError::NotInstalled),
|
||||
};
|
||||
@@ -202,18 +228,17 @@ impl ProcessManager<'_> {
|
||||
.get(version_name)
|
||||
.ok_or(ProcessError::InvalidVersion)?;
|
||||
|
||||
// TODO: refactor this path with open_process_logs
|
||||
let game_log_folder = &self.log_output_dir.join(game_id);
|
||||
create_dir_all(game_log_folder).map_err(ProcessError::IOError)?;
|
||||
|
||||
let current_time = chrono::offset::Local::now();
|
||||
let log_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.read(true)
|
||||
.create(true)
|
||||
.open(self.log_output_dir.join(format!(
|
||||
"{}-{}-{}.log",
|
||||
&game_id,
|
||||
&version,
|
||||
current_time.timestamp()
|
||||
)))
|
||||
.open(game_log_folder.join(format!("{}-{}.log", &version, current_time.timestamp())))
|
||||
.map_err(ProcessError::IOError)?;
|
||||
|
||||
let error_file = OpenOptions::new()
|
||||
@@ -221,9 +246,8 @@ impl ProcessManager<'_> {
|
||||
.truncate(true)
|
||||
.read(true)
|
||||
.create(true)
|
||||
.open(self.log_output_dir.join(format!(
|
||||
"{}-{}-{}-error.log",
|
||||
&game_id,
|
||||
.open(game_log_folder.join(format!(
|
||||
"{}-{}-error.log",
|
||||
&version,
|
||||
current_time.timestamp()
|
||||
)))
|
||||
@@ -282,7 +306,7 @@ impl ProcessManager<'_> {
|
||||
#[cfg(unix)]
|
||||
let mut command: Command = Command::new("sh");
|
||||
#[cfg(unix)]
|
||||
command.arg("-c").arg(launch_string);
|
||||
command.args(vec!["-c", &launch_string]);
|
||||
|
||||
command
|
||||
.stderr(error_file)
|
||||
@@ -325,7 +349,14 @@ impl ProcessManager<'_> {
|
||||
drop(app_state_handle);
|
||||
});
|
||||
|
||||
self.processes.insert(meta.id, wait_thread_handle);
|
||||
self.processes.insert(
|
||||
meta.id,
|
||||
RunningProcess {
|
||||
handle: wait_thread_handle,
|
||||
start: SystemTime::now(),
|
||||
manually_killed: false,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use std::{
|
||||
fmt::Display,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
database::{db::borrow_db_checked, models::data::Database},
|
||||
error::remote_access_error::RemoteAccessError,
|
||||
};
|
||||
use bitcode::{Decode, DecodeOwned, Encode};
|
||||
use cacache::Integrity;
|
||||
use http::{header::CONTENT_TYPE, response::Builder as ResponseBuilder, Response};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_binary::binary_stream::Endian;
|
||||
use http::{Response, header::CONTENT_TYPE, response::Builder as ResponseBuilder};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! offline {
|
||||
@@ -19,31 +23,48 @@ macro_rules! offline {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cache_object<K: AsRef<str>, D: Serialize + DeserializeOwned>(
|
||||
pub fn cache_object<K: AsRef<str>, D: Encode>(
|
||||
key: K,
|
||||
data: &D,
|
||||
) -> Result<Integrity, RemoteAccessError> {
|
||||
let bytes = serde_binary::to_vec(data, Endian::Little).unwrap();
|
||||
let bytes = bitcode::encode(data);
|
||||
cacache::write_sync(&borrow_db_checked().cache_dir, key, bytes)
|
||||
.map_err(RemoteAccessError::Cache)
|
||||
}
|
||||
pub fn get_cached_object<K: AsRef<str>, D: Serialize + DeserializeOwned>(
|
||||
pub fn get_cached_object<K: AsRef<str> + Display, D: Encode + DecodeOwned>(
|
||||
key: K,
|
||||
) -> Result<D, RemoteAccessError> {
|
||||
get_cached_object_db::<K, D>(key, &borrow_db_checked())
|
||||
}
|
||||
pub fn get_cached_object_db<K: AsRef<str>, D: Serialize + DeserializeOwned>(
|
||||
pub fn get_cached_object_db<K: AsRef<str> + Display, D: DecodeOwned>(
|
||||
key: K,
|
||||
db: &Database,
|
||||
) -> Result<D, RemoteAccessError> {
|
||||
let bytes = cacache::read_sync(&db.cache_dir, key).map_err(RemoteAccessError::Cache)?;
|
||||
let data = serde_binary::from_slice::<D>(&bytes, Endian::Little).unwrap();
|
||||
let bytes = cacache::read_sync(&db.cache_dir, &key).map_err(RemoteAccessError::Cache)?;
|
||||
let data = bitcode::decode::<D>(&bytes).map_err(|_| {
|
||||
RemoteAccessError::Cache(cacache::Error::EntryNotFound(
|
||||
db.cache_dir.clone(),
|
||||
key.to_string(),
|
||||
))
|
||||
})?;
|
||||
Ok(data)
|
||||
}
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct ObjectCache {
|
||||
content_type: String,
|
||||
body: Vec<u8>,
|
||||
expiry: u128,
|
||||
}
|
||||
|
||||
impl ObjectCache {
|
||||
pub fn has_expired(&self) -> bool {
|
||||
let duration = Duration::from_millis(self.expiry.try_into().unwrap());
|
||||
SystemTime::UNIX_EPOCH
|
||||
.checked_add(duration)
|
||||
.unwrap()
|
||||
.elapsed()
|
||||
.is_err()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Response<Vec<u8>>> for ObjectCache {
|
||||
@@ -57,6 +78,12 @@ impl From<Response<Vec<u8>>> for ObjectCache {
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
body: value.body().clone(),
|
||||
expiry: SystemTime::now()
|
||||
.checked_add(Duration::from_days(1))
|
||||
.unwrap()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,3 +93,9 @@ impl From<ObjectCache> for Response<Vec<u8>> {
|
||||
resp_builder.body(value.body).unwrap()
|
||||
}
|
||||
}
|
||||
impl From<&ObjectCache> for Response<Vec<u8>> {
|
||||
fn from(value: &ObjectCache) -> Self {
|
||||
let resp_builder = ResponseBuilder::new().header(CONTENT_TYPE, value.content_type.clone());
|
||||
resp_builder.body(value.body.clone()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,14 @@ pub fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeRespond
|
||||
// Drop leading /
|
||||
let object_id = &request.uri().path()[1..];
|
||||
|
||||
let cache_result = get_cached_object::<&str, ObjectCache>(object_id);
|
||||
if let Ok(cache_result) = &cache_result
|
||||
&& !cache_result.has_expired()
|
||||
{
|
||||
responder.respond(cache_result.into());
|
||||
return;
|
||||
}
|
||||
|
||||
let header = generate_authorization_header();
|
||||
let client: reqwest::blocking::Client = reqwest::blocking::Client::new();
|
||||
let response = make_request(&client, &["/api/v1/client/object/", object_id], &[], |f| {
|
||||
@@ -20,10 +28,8 @@ pub fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeRespond
|
||||
.unwrap()
|
||||
.send();
|
||||
if response.is_err() {
|
||||
let data = get_cached_object::<&str, ObjectCache>(object_id);
|
||||
|
||||
match data {
|
||||
Ok(data) => responder.respond(data.into()),
|
||||
match cache_result {
|
||||
Ok(cache_result) => responder.respond(cache_result.into()),
|
||||
Err(e) => {
|
||||
warn!("{e}")
|
||||
}
|
||||
@@ -38,7 +44,9 @@ pub fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeRespond
|
||||
);
|
||||
let data = Vec::from(response.bytes().unwrap());
|
||||
let resp = resp_builder.body(data).unwrap();
|
||||
cache_object::<&str, ObjectCache>(object_id, &resp.clone().into()).unwrap();
|
||||
if cache_result.is_err() || cache_result.unwrap().has_expired() {
|
||||
cache_object::<&str, ObjectCache>(object_id, &resp.clone().into()).unwrap();
|
||||
}
|
||||
|
||||
responder.respond(resp);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@ pub mod auth;
|
||||
pub mod cache;
|
||||
pub mod commands;
|
||||
pub mod fetch_object;
|
||||
pub mod utils;
|
||||
pub mod requests;
|
||||
pub mod server_proto;
|
||||
pub mod utils;
|
||||
|
||||
@@ -33,10 +33,7 @@ pub fn handle_server_proto(request: Request<Vec<u8>>, responder: UriSchemeRespon
|
||||
|
||||
let whitelist_prefix = ["/store", "/api", "/_", "/fonts"];
|
||||
|
||||
if whitelist_prefix
|
||||
.iter()
|
||||
.all(|f| !path.starts_with(f))
|
||||
{
|
||||
if whitelist_prefix.iter().all(|f| !path.starts_with(f)) {
|
||||
webbrowser::open(&new_uri.to_string()).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user