mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-10-27 13:39:19 +03:00
singleton assets proxy
This commit is contained in:
parent
1300888d82
commit
922046d9c3
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
@ -1520,6 +1520,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.18.0",
|
||||
"tokio-util",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
|
@ -54,6 +54,7 @@ refinery = { version = "0.8", features = [ "rusqlite" ] }
|
||||
sha1 = "0.10.5"
|
||||
tokio-util = "0.7.8"
|
||||
diffy = "0.3.0"
|
||||
url = "2.4.0"
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
|
76
src-tauri/src/assets.rs
Normal file
76
src-tauri/src/assets.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use std::{collections::HashMap, path, sync};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::sync::Semaphore;
|
||||
use url::Url;
|
||||
|
||||
pub struct Proxy {
|
||||
cache_dir: path::PathBuf,
|
||||
|
||||
semaphores: sync::Arc<tokio::sync::Mutex<HashMap<url::Url, Semaphore>>>,
|
||||
}
|
||||
|
||||
const ASSET_SCHEME: &str = "asset";
|
||||
|
||||
impl Proxy {
|
||||
pub fn new<P: AsRef<path::Path>>(cache_dir: P) -> Self {
|
||||
Self {
|
||||
cache_dir: cache_dir.as_ref().to_path_buf(),
|
||||
semaphores: sync::Arc::new(tokio::sync::Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
// takes a url of a remote assets, downloads it into cache and returns a url that points to the cached file
|
||||
pub async fn proxy(&self, src: &Url) -> Result<Url> {
|
||||
if src.scheme() == ASSET_SCHEME {
|
||||
return Ok(src.clone());
|
||||
}
|
||||
|
||||
let hash = md5::compute(src.to_string());
|
||||
let path = path::Path::new(src.path());
|
||||
let ext = path.extension().unwrap().to_str().unwrap();
|
||||
let save_to = self.cache_dir.join(format!("{:X}.{}", hash, ext));
|
||||
|
||||
if save_to.exists() {
|
||||
return Ok(build_asset_url(&save_to.display().to_string()));
|
||||
}
|
||||
|
||||
// only one download per url at a time
|
||||
let mut semaphores = self.semaphores.lock().await;
|
||||
let r = semaphores
|
||||
.entry(src.clone())
|
||||
.or_insert_with(|| Semaphore::new(1));
|
||||
let _permit = r.acquire().await?;
|
||||
|
||||
if save_to.exists() {
|
||||
// check again, maybe url was downloaded
|
||||
return Ok(build_asset_url(&save_to.display().to_string()));
|
||||
}
|
||||
|
||||
log::info!("Downloading image {}", src);
|
||||
|
||||
let resp = reqwest::get(src.clone()).await?;
|
||||
if !resp.status().is_success() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Failed to download image {}: {}",
|
||||
src,
|
||||
resp.status()
|
||||
));
|
||||
}
|
||||
|
||||
let bytes = resp.bytes().await?;
|
||||
std::fs::create_dir_all(&self.cache_dir)?;
|
||||
std::fs::write(&save_to, bytes)?;
|
||||
|
||||
Ok(build_asset_url(&save_to.display().to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn build_asset_url(path: &str) -> Url {
|
||||
Url::parse(&format!(
|
||||
"{}://localhost/{}",
|
||||
ASSET_SCHEME,
|
||||
urlencoding::encode(path)
|
||||
))
|
||||
.unwrap()
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
extern crate scopeguard;
|
||||
|
||||
mod app;
|
||||
mod assets;
|
||||
mod bookmarks;
|
||||
mod database;
|
||||
mod deltas;
|
||||
@ -77,46 +78,6 @@ impl From<anyhow::Error> for Error {
|
||||
|
||||
const IS_DEV: bool = cfg!(debug_assertions);
|
||||
|
||||
fn build_asset_url(path: &str) -> String {
|
||||
format!("asset://localhost/{}", urlencoding::encode(path))
|
||||
}
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
async fn proxy_image(handle: tauri::AppHandle, src: &str) -> Result<String> {
|
||||
if src.starts_with("asset://") {
|
||||
return Ok(src.to_string());
|
||||
}
|
||||
|
||||
let images_dir = handle
|
||||
.path_resolver()
|
||||
.app_cache_dir()
|
||||
.unwrap()
|
||||
.join("images");
|
||||
|
||||
let hash = md5::compute(src);
|
||||
let ext = src.split('.').last().unwrap_or("jpg");
|
||||
let save_to = images_dir.join(format!("{:X}.{}", hash, ext));
|
||||
|
||||
if save_to.exists() {
|
||||
return Ok(build_asset_url(&save_to.display().to_string()));
|
||||
}
|
||||
|
||||
let resp = reqwest::get(src).await?;
|
||||
if !resp.status().is_success() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Failed to download image {}: {}",
|
||||
src,
|
||||
resp.status()
|
||||
));
|
||||
}
|
||||
|
||||
let bytes = resp.bytes().await?;
|
||||
std::fs::create_dir_all(&images_dir)?;
|
||||
std::fs::write(&save_to, bytes)?;
|
||||
|
||||
Ok(build_asset_url(&save_to.display().to_string()))
|
||||
}
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
#[tauri::command(async)]
|
||||
async fn get_project_archive_path(
|
||||
@ -217,19 +178,21 @@ async fn list_sessions(
|
||||
#[tauri::command(async)]
|
||||
async fn get_user(handle: tauri::AppHandle) -> Result<Option<users::User>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let proxy = handle.state::<assets::Proxy>();
|
||||
|
||||
match app.get_user().context("failed to get user")? {
|
||||
Some(user) => {
|
||||
let local_picture = match proxy_image(handle, &user.picture).await {
|
||||
let remote_picture = url::Url::parse(&user.picture).context("invalid picture url")?;
|
||||
let local_picture = match proxy.proxy(&remote_picture).await {
|
||||
Ok(picture) => picture,
|
||||
Err(e) => {
|
||||
log::error!("{:#}", e);
|
||||
user.picture
|
||||
remote_picture
|
||||
}
|
||||
};
|
||||
|
||||
let user = users::User {
|
||||
picture: local_picture,
|
||||
picture: local_picture.to_string(),
|
||||
..user
|
||||
};
|
||||
|
||||
@ -814,14 +777,12 @@ fn main() {
|
||||
// debug_test_consistency(&app_state, "fec3d50c-503f-4021-89fb-e7ec2433ceae")
|
||||
// .expect("FAIL");
|
||||
|
||||
let zipper = zip::Zipper::new(
|
||||
tauri_app
|
||||
.path_resolver()
|
||||
.app_cache_dir()
|
||||
.unwrap()
|
||||
.join("archives"),
|
||||
);
|
||||
let cache_dir = tauri_app.path_resolver().app_cache_dir().unwrap();
|
||||
let zipper = zip::Zipper::new(cache_dir.join("archives"));
|
||||
let proxy = assets::Proxy::new(cache_dir.join("images"));
|
||||
|
||||
tauri_app.manage(zipper);
|
||||
tauri_app.manage(proxy);
|
||||
tauri_app.manage(app);
|
||||
|
||||
let app_handle = tauri_app.handle();
|
||||
|
Loading…
Reference in New Issue
Block a user