mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-10 20:42:10 +10:00
Cache-first object fetching (#76)
* fix: submillisecond cache hits * fix: async object loading to hand control back to renderer * fix: clippy
This commit is contained in:
1
app.vue
1
app.vue
@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<LoadingIndicator />
|
||||||
<NuxtLayout class="select-none w-screen h-screen">
|
<NuxtLayout class="select-none w-screen h-screen">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
<ModalStack />
|
<ModalStack />
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
<MenuItems
|
<MenuItems
|
||||||
class="absolute bg-zinc-900 right-0 top-10 z-50 w-56 origin-top-right focus:outline-none shadow-md"
|
class="absolute bg-zinc-900 right-0 top-10 z-50 w-56 origin-top-right focus:outline-none shadow-md"
|
||||||
>
|
>
|
||||||
<PanelWidget class="flex-col gap-y-2">
|
<div class="flex-col gap-y-2">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/id/me"
|
to="/id/me"
|
||||||
class="transition inline-flex items-center w-full py-3 px-4 hover:bg-zinc-800"
|
class="transition inline-flex items-center w-full py-3 px-4 hover:bg-zinc-800"
|
||||||
@ -65,7 +65,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
</PanelWidget>
|
</div>
|
||||||
</MenuItems>
|
</MenuItems>
|
||||||
</transition>
|
</transition>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
7
components/LoadingIndicator.vue
Normal file
7
components/LoadingIndicator.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template></template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const loading = useLoadingIndicator();
|
||||||
|
|
||||||
|
watch(loading.isLoading, console.log);
|
||||||
|
</script>
|
||||||
@ -76,7 +76,7 @@
|
|||||||
<TransitionGroup name="slide" tag="div" class="h-full">
|
<TransitionGroup name="slide" tag="div" class="h-full">
|
||||||
<img
|
<img
|
||||||
v-for="(url, index) in mediaUrls"
|
v-for="(url, index) in mediaUrls"
|
||||||
:key="url"
|
:key="index"
|
||||||
:src="url"
|
:src="url"
|
||||||
class="absolute inset-0 w-full h-full object-cover"
|
class="absolute inset-0 w-full h-full object-cover"
|
||||||
v-show="index === currentImageIndex"
|
v-show="index === currentImageIndex"
|
||||||
@ -157,8 +157,8 @@
|
|||||||
<template #default>
|
<template #default>
|
||||||
<div class="sm:flex sm:items-start">
|
<div class="sm:flex sm:items-start">
|
||||||
<div class="mt-3 text-center sm:mt-0 sm:text-left">
|
<div class="mt-3 text-center sm:mt-0 sm:text-left">
|
||||||
<h3 class="text-base font-semibold text-zinc-100"
|
<h3 class="text-base font-semibold text-zinc-100">
|
||||||
>Install {{ game.mName }}?
|
Install {{ game.mName }}?
|
||||||
</h3>
|
</h3>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<p class="text-sm text-zinc-400">
|
<p class="text-sm text-zinc-400">
|
||||||
@ -350,9 +350,7 @@
|
|||||||
<template #buttons>
|
<template #buttons>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
@click="() => install()"
|
@click="() => install()"
|
||||||
:disabled="
|
:disabled="!(versionOptions && versionOptions.length > 0)"
|
||||||
!(versionOptions && versionOptions.length > 0)
|
|
||||||
"
|
|
||||||
:loading="installLoading"
|
:loading="installLoading"
|
||||||
type="submit"
|
type="submit"
|
||||||
class="ml-2 w-full sm:w-fit"
|
class="ml-2 w-full sm:w-fit"
|
||||||
@ -370,7 +368,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</ModalTemplate>
|
</ModalTemplate>
|
||||||
|
|
||||||
<GameOptionsModal v-if="status.type === GameStatusEnum.Installed" v-model="configureModalOpen" :game-id="game.id" />
|
<GameOptionsModal
|
||||||
|
v-if="status.type === GameStatusEnum.Installed"
|
||||||
|
v-model="configureModalOpen"
|
||||||
|
:game-id="game.id"
|
||||||
|
/>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
enter="transition ease-out duration-300"
|
enter="transition ease-out duration-300"
|
||||||
@ -420,7 +422,7 @@
|
|||||||
<img
|
<img
|
||||||
v-for="(url, index) in mediaUrls"
|
v-for="(url, index) in mediaUrls"
|
||||||
v-show="currentImageIndex === index"
|
v-show="currentImageIndex === index"
|
||||||
:key="url"
|
:key="index"
|
||||||
:src="url"
|
:src="url"
|
||||||
class="max-h-[90vh] max-w-[90vw] object-contain"
|
class="max-h-[90vh] max-w-[90vw] object-contain"
|
||||||
:alt="`${game.mName} screenshot ${index + 1}`"
|
:alt="`${game.mName} screenshot ${index + 1}`"
|
||||||
@ -441,10 +443,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
TransitionChild,
|
|
||||||
TransitionRoot,
|
|
||||||
Listbox,
|
Listbox,
|
||||||
ListboxButton,
|
ListboxButton,
|
||||||
ListboxLabel,
|
ListboxLabel,
|
||||||
@ -482,7 +480,10 @@ const bannerUrl = await useObject(game.value.mBannerObjectId);
|
|||||||
|
|
||||||
// Get all available images
|
// Get all available images
|
||||||
const mediaUrls = await Promise.all(
|
const mediaUrls = await Promise.all(
|
||||||
game.value.mImageCarouselObjectIds.map((id) => useObject(id))
|
game.value.mImageCarouselObjectIds.map(async (v) => {
|
||||||
|
const src = await useObject(v);
|
||||||
|
return src;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const htmlDescription = micromark(game.value.mDescription);
|
const htmlDescription = micromark(game.value.mDescription);
|
||||||
@ -496,7 +497,6 @@ const currentImageIndex = ref(0);
|
|||||||
|
|
||||||
const configureModalOpen = ref(false);
|
const configureModalOpen = ref(false);
|
||||||
|
|
||||||
|
|
||||||
async function installFlow() {
|
async function installFlow() {
|
||||||
installFlowOpen.value = true;
|
installFlowOpen.value = true;
|
||||||
versionOptions.value = undefined;
|
versionOptions.value = undefined;
|
||||||
@ -535,12 +535,11 @@ async function install() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resumeDownload() {
|
async function resumeDownload() {
|
||||||
try {
|
try {
|
||||||
await invoke("resume_download", { gameId: game.value.id })
|
await invoke("resume_download", { gameId: game.value.id });
|
||||||
}
|
} catch (e) {
|
||||||
catch(e) {
|
console.error(e);
|
||||||
console.error(e)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launch() {
|
async function launch() {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ pub enum RemoteAccessError {
|
|||||||
UnparseableResponse(String),
|
UnparseableResponse(String),
|
||||||
ManifestDownloadFailed(StatusCode, String),
|
ManifestDownloadFailed(StatusCode, String),
|
||||||
OutOfSync,
|
OutOfSync,
|
||||||
Cache(cacache::Error),
|
Cache(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RemoteAccessError {
|
impl Display for RemoteAccessError {
|
||||||
|
|||||||
@ -113,7 +113,7 @@ pub fn fetch_library_logic(
|
|||||||
}
|
}
|
||||||
// We should always have a cache of the object
|
// We should always have a cache of the object
|
||||||
// Pass db_handle because otherwise we get a gridlock
|
// Pass db_handle because otherwise we get a gridlock
|
||||||
let game = get_cached_object_db::<String, Game>(meta.id.clone(), &db_handle)?;
|
let game = get_cached_object_db::<Game>(&meta.id.clone(), &db_handle)?;
|
||||||
games.push(game);
|
games.push(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ pub fn fetch_game_logic(
|
|||||||
version,
|
version,
|
||||||
};
|
};
|
||||||
|
|
||||||
cache_object(id, game)?;
|
cache_object(&id, game)?;
|
||||||
|
|
||||||
return Ok(data);
|
return Ok(data);
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ pub fn fetch_game_logic(
|
|||||||
version,
|
version,
|
||||||
};
|
};
|
||||||
|
|
||||||
cache_object(id, &game)?;
|
cache_object(&id, &game)?;
|
||||||
|
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
@ -239,7 +239,7 @@ pub fn fetch_game_logic_offline(
|
|||||||
drop(handle);
|
drop(handle);
|
||||||
|
|
||||||
let status = GameStatusManager::fetch_state(&id);
|
let status = GameStatusManager::fetch_state(&id);
|
||||||
let game = get_cached_object::<String, Game>(id)?;
|
let game = get_cached_object::<Game>(&id)?;
|
||||||
|
|
||||||
Ok(FetchGameStruct {
|
Ok(FetchGameStruct {
|
||||||
game,
|
game,
|
||||||
|
|||||||
@ -13,6 +13,7 @@ mod remote;
|
|||||||
|
|
||||||
use crate::process::commands::open_process_logs;
|
use crate::process::commands::open_process_logs;
|
||||||
use crate::{database::db::DatabaseImpls, games::downloads::commands::resume_download};
|
use crate::{database::db::DatabaseImpls, games::downloads::commands::resume_download};
|
||||||
|
use bitcode::{Decode, Encode};
|
||||||
use client::commands::fetch_state;
|
use client::commands::fetch_state;
|
||||||
use client::{
|
use client::{
|
||||||
autostart::{get_autostart_enabled, sync_autostart_on_startup, toggle_autostart},
|
autostart::{get_autostart_enabled, sync_autostart_on_startup, toggle_autostart},
|
||||||
@ -22,7 +23,7 @@ use database::commands::{
|
|||||||
add_download_dir, delete_download_dir, fetch_download_dir_stats, fetch_settings,
|
add_download_dir, delete_download_dir, fetch_download_dir_stats, fetch_settings,
|
||||||
fetch_system_data, update_settings,
|
fetch_system_data, update_settings,
|
||||||
};
|
};
|
||||||
use database::db::{borrow_db_checked, borrow_db_mut_checked, DatabaseInterface, DATA_ROOT_DIR};
|
use database::db::{DATA_ROOT_DIR, DatabaseInterface, borrow_db_checked, borrow_db_mut_checked};
|
||||||
use database::models::data::GameDownloadStatus;
|
use database::models::data::GameDownloadStatus;
|
||||||
use download_manager::commands::{
|
use download_manager::commands::{
|
||||||
cancel_game, move_download_in_queue, pause_downloads, resume_downloads,
|
cancel_game, move_download_in_queue, pause_downloads, resume_downloads,
|
||||||
@ -37,13 +38,13 @@ use games::commands::{
|
|||||||
fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, uninstall_game,
|
fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, uninstall_game,
|
||||||
};
|
};
|
||||||
use games::downloads::commands::download_game;
|
use games::downloads::commands::download_game;
|
||||||
use games::library::{update_game_configuration, Game};
|
use games::library::{Game, update_game_configuration};
|
||||||
use log::{debug, info, warn, LevelFilter};
|
use log::{LevelFilter, debug, info, warn};
|
||||||
|
use log4rs::Config;
|
||||||
use log4rs::append::console::ConsoleAppender;
|
use log4rs::append::console::ConsoleAppender;
|
||||||
use log4rs::append::file::FileAppender;
|
use log4rs::append::file::FileAppender;
|
||||||
use log4rs::config::{Appender, Root};
|
use log4rs::config::{Appender, Root};
|
||||||
use log4rs::encode::pattern::PatternEncoder;
|
use log4rs::encode::pattern::PatternEncoder;
|
||||||
use log4rs::Config;
|
|
||||||
use process::commands::{kill_game, launch_game};
|
use process::commands::{kill_game, launch_game};
|
||||||
use process::process_manager::ProcessManager;
|
use process::process_manager::ProcessManager;
|
||||||
use remote::auth::{self, recieve_handshake};
|
use remote::auth::{self, recieve_handshake};
|
||||||
@ -51,7 +52,7 @@ use remote::commands::{
|
|||||||
auth_initiate, fetch_drop_object, gen_drop_url, manual_recieve_handshake, retry_connect,
|
auth_initiate, fetch_drop_object, gen_drop_url, manual_recieve_handshake, retry_connect,
|
||||||
sign_out, use_remote,
|
sign_out, use_remote,
|
||||||
};
|
};
|
||||||
use remote::fetch_object::{fetch_object, fetch_object_offline};
|
use remote::fetch_object::fetch_object;
|
||||||
use remote::server_proto::{handle_server_proto, handle_server_proto_offline};
|
use remote::server_proto::{handle_server_proto, handle_server_proto_offline};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -71,7 +72,6 @@ use tauri::tray::TrayIconBuilder;
|
|||||||
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
||||||
use tauri_plugin_deep_link::DeepLinkExt;
|
use tauri_plugin_deep_link::DeepLinkExt;
|
||||||
use tauri_plugin_dialog::DialogExt;
|
use tauri_plugin_dialog::DialogExt;
|
||||||
use bitcode::{Encode, Decode};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Eq, PartialEq)]
|
#[derive(Clone, Copy, Serialize, Eq, PartialEq)]
|
||||||
pub enum AppStatus {
|
pub enum AppStatus {
|
||||||
@ -401,15 +401,10 @@ pub fn run() {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.register_asynchronous_uri_scheme_protocol("object", move |ctx, request, responder| {
|
.register_asynchronous_uri_scheme_protocol("object", move |_ctx, request, responder| {
|
||||||
let state: tauri::State<'_, Mutex<AppState>> = ctx.app_handle().state();
|
tauri::async_runtime::spawn(async move {
|
||||||
offline!(
|
fetch_object(request, responder).await;
|
||||||
state,
|
});
|
||||||
fetch_object,
|
|
||||||
fetch_object_offline,
|
|
||||||
request,
|
|
||||||
responder
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.register_asynchronous_uri_scheme_protocol("server", move |ctx, request, responder| {
|
.register_asynchronous_uri_scheme_protocol("server", move |ctx, request, responder| {
|
||||||
let state: tauri::State<'_, Mutex<AppState>> = ctx.app_handle().state();
|
let state: tauri::State<'_, Mutex<AppState>> = ctx.app_handle().state();
|
||||||
|
|||||||
@ -212,7 +212,7 @@ pub fn setup() -> (AppStatus, Option<User>) {
|
|||||||
let user_result = match fetch_user() {
|
let user_result = match fetch_user() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(RemoteAccessError::FetchError(_)) => {
|
Err(RemoteAccessError::FetchError(_)) => {
|
||||||
let user = get_cached_object::<_, User>("user").unwrap();
|
let user = get_cached_object::<User>("user").unwrap();
|
||||||
return (AppStatus::Offline, Some(user));
|
return (AppStatus::Offline, Some(user));
|
||||||
}
|
}
|
||||||
Err(_) => return (AppStatus::SignedInNeedsReauth, None),
|
Err(_) => return (AppStatus::SignedInNeedsReauth, None),
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fs::File,
|
||||||
time::{Duration, SystemTime},
|
io::{self, Write},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -8,8 +10,8 @@ use crate::{
|
|||||||
error::remote_access_error::RemoteAccessError,
|
error::remote_access_error::RemoteAccessError,
|
||||||
};
|
};
|
||||||
use bitcode::{Decode, DecodeOwned, Encode};
|
use bitcode::{Decode, DecodeOwned, Encode};
|
||||||
use cacache::Integrity;
|
|
||||||
use http::{Response, header::CONTENT_TYPE, response::Builder as ResponseBuilder};
|
use http::{Response, header::CONTENT_TYPE, response::Builder as ResponseBuilder};
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! offline {
|
macro_rules! offline {
|
||||||
@ -23,47 +25,67 @@ macro_rules! offline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cache_object<K: AsRef<str>, D: Encode>(
|
fn get_sys_time_in_secs() -> u64 {
|
||||||
key: K,
|
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
||||||
data: &D,
|
Ok(n) => n.as_secs(),
|
||||||
) -> Result<Integrity, RemoteAccessError> {
|
Err(_) => panic!("SystemTime before UNIX EPOCH!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cache_path(base: &Path, key: &str) -> PathBuf {
|
||||||
|
let key_hash = hex::encode(md5::compute(key.as_bytes()).0);
|
||||||
|
base.join(key_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_sync(base: &Path, key: &str, data: Vec<u8>) -> io::Result<()> {
|
||||||
|
let cache_path = get_cache_path(base, key);
|
||||||
|
let mut file = File::create(cache_path)?;
|
||||||
|
file.write_all(&data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_sync(base: &Path, key: &str) -> io::Result<Vec<u8>> {
|
||||||
|
let cache_path = get_cache_path(base, key);
|
||||||
|
let file = std::fs::read(cache_path)?;
|
||||||
|
Ok(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cache_object<D: Encode>(key: &str, data: &D) -> Result<(), RemoteAccessError> {
|
||||||
let bytes = bitcode::encode(data);
|
let bytes = bitcode::encode(data);
|
||||||
cacache::write_sync(&borrow_db_checked().cache_dir, key, bytes)
|
write_sync(&borrow_db_checked().cache_dir, key, bytes).map_err(RemoteAccessError::Cache)
|
||||||
.map_err(RemoteAccessError::Cache)
|
|
||||||
}
|
}
|
||||||
pub fn get_cached_object<K: AsRef<str> + Display, D: Encode + DecodeOwned>(
|
pub fn get_cached_object<D: Encode + DecodeOwned>(key: &str) -> Result<D, RemoteAccessError> {
|
||||||
key: K,
|
get_cached_object_db::<D>(key, &borrow_db_checked())
|
||||||
) -> Result<D, RemoteAccessError> {
|
|
||||||
get_cached_object_db::<K, D>(key, &borrow_db_checked())
|
|
||||||
}
|
}
|
||||||
pub fn get_cached_object_db<K: AsRef<str> + Display, D: DecodeOwned>(
|
pub fn get_cached_object_db<D: DecodeOwned>(
|
||||||
key: K,
|
key: &str,
|
||||||
db: &Database,
|
db: &Database,
|
||||||
) -> Result<D, RemoteAccessError> {
|
) -> Result<D, RemoteAccessError> {
|
||||||
let bytes = cacache::read_sync(&db.cache_dir, &key).map_err(RemoteAccessError::Cache)?;
|
let start = SystemTime::now();
|
||||||
let data = bitcode::decode::<D>(&bytes).map_err(|_| {
|
let bytes = read_sync(&db.cache_dir, key).map_err(RemoteAccessError::Cache)?;
|
||||||
RemoteAccessError::Cache(cacache::Error::EntryNotFound(
|
let read = start.elapsed().unwrap();
|
||||||
db.cache_dir.clone(),
|
let data =
|
||||||
key.to_string(),
|
bitcode::decode::<D>(&bytes).map_err(|e| RemoteAccessError::Cache(io::Error::other(e)))?;
|
||||||
))
|
let decode = start.elapsed().unwrap();
|
||||||
})?;
|
debug!(
|
||||||
|
"cache object took: r:{}, d:{}, b:{}",
|
||||||
|
read.as_millis(),
|
||||||
|
read.abs_diff(decode).as_millis(),
|
||||||
|
bytes.len()
|
||||||
|
);
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
#[derive(Encode, Decode)]
|
#[derive(Encode, Decode)]
|
||||||
pub struct ObjectCache {
|
pub struct ObjectCache {
|
||||||
content_type: String,
|
content_type: String,
|
||||||
body: Vec<u8>,
|
body: Vec<u8>,
|
||||||
expiry: u128,
|
expiry: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectCache {
|
impl ObjectCache {
|
||||||
pub fn has_expired(&self) -> bool {
|
pub fn has_expired(&self) -> bool {
|
||||||
let duration = Duration::from_millis(self.expiry.try_into().unwrap());
|
let current = get_sys_time_in_secs();
|
||||||
SystemTime::UNIX_EPOCH
|
self.expiry < current
|
||||||
.checked_add(duration)
|
|
||||||
.unwrap()
|
|
||||||
.elapsed()
|
|
||||||
.is_err()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,12 +100,7 @@ impl From<Response<Vec<u8>>> for ObjectCache {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
body: value.body().clone(),
|
body: value.body().clone(),
|
||||||
expiry: SystemTime::now()
|
expiry: get_sys_time_in_secs() + 60 * 60 * 24,
|
||||||
.checked_add(Duration::from_days(1))
|
|
||||||
.unwrap()
|
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ pub fn fetch_drop_object(path: String) -> Result<Vec<u8>, RemoteAccessError> {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("{e}");
|
debug!("{e}");
|
||||||
get_cached_object::<&str, Vec<u8>>(&path)
|
get_cached_object::<Vec<u8>>(&path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,18 @@ use http::{header::CONTENT_TYPE, response::Builder as ResponseBuilder};
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use tauri::UriSchemeResponder;
|
use tauri::UriSchemeResponder;
|
||||||
|
|
||||||
|
use crate::{DB, database::db::DatabaseImpls};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
auth::generate_authorization_header,
|
auth::generate_authorization_header,
|
||||||
cache::{cache_object, get_cached_object, ObjectCache},
|
cache::{ObjectCache, cache_object, get_cached_object},
|
||||||
requests::make_request,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeResponder) {
|
pub async fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeResponder) {
|
||||||
// Drop leading /
|
// Drop leading /
|
||||||
let object_id = &request.uri().path()[1..];
|
let object_id = &request.uri().path()[1..];
|
||||||
|
|
||||||
let cache_result = get_cached_object::<&str, ObjectCache>(object_id);
|
let cache_result = get_cached_object::<ObjectCache>(object_id);
|
||||||
if let Ok(cache_result) = &cache_result
|
if let Ok(cache_result) = &cache_result
|
||||||
&& !cache_result.has_expired()
|
&& !cache_result.has_expired()
|
||||||
{
|
{
|
||||||
@ -21,12 +22,10 @@ pub fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeRespond
|
|||||||
}
|
}
|
||||||
|
|
||||||
let header = generate_authorization_header();
|
let header = generate_authorization_header();
|
||||||
let client: reqwest::blocking::Client = reqwest::blocking::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let response = make_request(&client, &["/api/v1/client/object/", object_id], &[], |f| {
|
let url = format!("{}api/v1/client/object/{object_id}", DB.fetch_base_url());
|
||||||
f.header("Authorization", header)
|
let response = client.get(url).header("Authorization", header).send().await;
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
.send();
|
|
||||||
if response.is_err() {
|
if response.is_err() {
|
||||||
match cache_result {
|
match cache_result {
|
||||||
Ok(cache_result) => responder.respond(cache_result.into()),
|
Ok(cache_result) => responder.respond(cache_result.into()),
|
||||||
@ -42,20 +41,11 @@ pub fn fetch_object(request: http::Request<Vec<u8>>, responder: UriSchemeRespond
|
|||||||
CONTENT_TYPE,
|
CONTENT_TYPE,
|
||||||
response.headers().get("Content-Type").unwrap(),
|
response.headers().get("Content-Type").unwrap(),
|
||||||
);
|
);
|
||||||
let data = Vec::from(response.bytes().unwrap());
|
let data = Vec::from(response.bytes().await.unwrap());
|
||||||
let resp = resp_builder.body(data).unwrap();
|
let resp = resp_builder.body(data).unwrap();
|
||||||
if cache_result.is_err() || cache_result.unwrap().has_expired() {
|
if cache_result.is_err() || cache_result.unwrap().has_expired() {
|
||||||
cache_object::<&str, ObjectCache>(object_id, &resp.clone().into()).unwrap();
|
cache_object::<ObjectCache>(object_id, &resp.clone().into()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
responder.respond(resp);
|
responder.respond(resp);
|
||||||
}
|
}
|
||||||
pub fn fetch_object_offline(request: http::Request<Vec<u8>>, responder: UriSchemeResponder) {
|
|
||||||
let object_id = &request.uri().path()[1..];
|
|
||||||
let data = get_cached_object::<&str, ObjectCache>(object_id);
|
|
||||||
|
|
||||||
match data {
|
|
||||||
Ok(data) => responder.respond(data.into()),
|
|
||||||
Err(e) => warn!("{e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null,
|
"csp": "",
|
||||||
"assetProtocol": {
|
"assetProtocol": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"scope": {}
|
"scope": {}
|
||||||
|
|||||||
Reference in New Issue
Block a user