Skip to content

Commit

Permalink
use custom protocol to load image
Browse files Browse the repository at this point in the history
  • Loading branch information
mariotaku committed Dec 25, 2024
1 parent b60ccec commit e1be5c8
Show file tree
Hide file tree
Showing 8 changed files with 979 additions and 556 deletions.
1,202 changes: 776 additions & 426 deletions Cargo.lock

Large diffs are not rendered by default.

226 changes: 116 additions & 110 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ serde_json = "1.0"
serde_repr = "0.1.19"
log = "0.4.22"
rand = "0.8.5"
vt100 = "0.15"
tokio = "1.40"
vt100 = "0.15.2"
tokio = { version = "1.42.0", features = ["rt", "rt-multi-thread", "macros"] }
uuid = { version = "1.10.0", features = ["v1"] }
hex = "0.4.3"
path-slash = "0.2.1"
Expand Down
3 changes: 1 addition & 2 deletions src-tauri/src/conn_pool/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::Path;
use std::sync::Mutex;
use std::time::Duration;

use libssh_rs::{AuthStatus, LogLevel, Session, SshKey, SshOption};
use libssh_rs::{AuthStatus, Session, SshKey, SshOption};
use regex::Regex;
use uuid::Uuid;

Expand All @@ -21,7 +21,6 @@ impl DeviceConnection {
session.set_option(SshOption::Hostname(device.host.clone()))?;
session.set_option(SshOption::Port(device.port.clone()))?;
session.set_option(SshOption::User(Some(device.username.clone())))?;
session.set_option(SshOption::LogLevel(LogLevel::Protocol))?;

session.connect()?;

Expand Down
5 changes: 5 additions & 0 deletions src-tauri/src/device_manager/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ impl DeviceManager {
Ok(devices)
}

pub async fn find(&self, name: &str) -> Result<Option<Device>, Error> {
let devices = self.list().await?;
Ok(devices.into_iter().find(|d| d.name == name))
}

pub async fn set_default(&self, name: &str) -> Result<Option<Device>, Error> {
let conf_dir = self.ensure_conf_dir()?;
let mut devices = read(&conf_dir).await?;
Expand Down
11 changes: 8 additions & 3 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::shell_manager::ShellManager;
use crate::spawn_manager::SpawnManager;

mod app_dirs;
mod byte_string;
mod conn_pool;
mod device_manager;
mod error;
Expand All @@ -30,16 +31,19 @@ mod remote_files;
mod session_manager;
mod shell_manager;
mod spawn_manager;
mod byte_string;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
#[tokio::main]
pub async fn run() {
#[cfg(target_os = "android")]
{
android_logger::init_once(Config::default().with_max_level(log::LevelFilter::Debug));
}

tauri::async_runtime::set(tokio::runtime::Handle::current());

let mut builder = tauri::Builder::default();
#[cfg(feature = "single-instance")]
#[cfg(feature = "tauri-plugin-single-instance")]
{
builder = builder.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
if let Some(wnd) = app.get_window("main") {
Expand All @@ -63,6 +67,7 @@ pub fn run() {
.manage(SessionManager::default())
.manage(SpawnManager::default())
.manage(ShellManager::default())
.register_asynchronous_uri_scheme_protocol("remote-file", plugins::file::protocol)
.on_page_load(|wnd, payload| {
if payload.event() == PageLoadEvent::Started {
let spawns = wnd.state::<SpawnManager>();
Expand Down
71 changes: 67 additions & 4 deletions src-tauri/src/plugins/file.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use std::env::temp_dir;
use std::fmt::format;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;

use flate2::read::GzDecoder;
use libssh_rs::OpenFlags;
use serde::Serialize;
use tauri::{AppHandle, Manager, Runtime};
use tauri::ipc::{Channel, IpcResponse};
use tauri::ipc::Channel;
use tauri::plugin::{Builder, TauriPlugin};
use tauri::{http, AppHandle, Manager, Runtime, UriSchemeContext, UriSchemeResponder};
use uuid::Uuid;

use crate::device_manager::Device;
use crate::device_manager::{Device, DeviceManager};
use crate::error::Error;
use crate::remote_files::{FileItem, PermInfo};
use crate::remote_files::serve;
use crate::remote_files::{FileItem, PermInfo};
use crate::session_manager::SessionManager;

#[derive(Copy, Clone, Serialize)]
Expand Down Expand Up @@ -247,3 +248,65 @@ pub fn plugin<R: Runtime>(name: &'static str) -> TauriPlugin<R> {
])
.build()
}

pub fn protocol<R: Runtime>(
ctx: UriSchemeContext<'_, R>,
req: http::Request<Vec<u8>>,
resp: UriSchemeResponder,
) {
let app = ctx.app_handle().clone();
let Some((device_name, path)) = req.uri().path()[1..].split_once('/') else {
resp.respond(http::Response::builder().status(404).body(vec![]).unwrap());
return;
};
let device_name = device_name.to_string();
let path = format!("/{path}");
tokio::task::spawn(async move {
let devices = app.state::<DeviceManager>();
let Some(device) = devices.find(&device_name).await.ok().flatten() else {
resp.respond(
http::Response::builder()
.status(404)
.body(format!("Device {device_name} not found!").into_bytes())
.unwrap(),
);
return;
};
let app = app.clone();
match tokio::task::spawn_blocking(move || {
let sessions = app.state::<SessionManager>();
return sessions.with_session(device, |session| {
let sftp = session.sftp()?;
let mut file = sftp.open(&path, OpenFlags::READ_ONLY, 0)?;
let mut buf = Vec::<u8>::new();
file.read_to_end(&mut buf)?;
return Ok(buf);
});
})
.await
{
Ok(Ok(data)) => {
resp.respond(http::Response::builder().status(200).body(data).unwrap());
return;
}
Ok(Err(e)) => {
resp.respond(
http::Response::builder()
.status(500)
.body(format!("{e}").into_bytes())
.unwrap(),
);
return;
}
Err(e) => {
resp.respond(
http::Response::builder()
.status(500)
.body(format!("Internal error: {e:?}").into_bytes())
.unwrap(),
);
return;
}
}
});
}
13 changes: 4 additions & 9 deletions src/app/core/services/app-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,12 @@ export class AppManagerService {
throw e;
})
.then(resp => resp['apps'] as RawPackageInfo[])
.then((result) => Promise.all(result.map(item => this.completeIcon(device, item))));
.then((result) => result.map(item => this.completeIcon(device, item)));
}

private async completeIcon(device: Device, info: RawPackageInfo): Promise<PackageInfo> {
const data = await this.file.read(device, path.join(info.folderPath, info.icon))
.then(d => d.length > 0 ? d : undefined)
.catch((e) => {
console.warn('failed to fetch app icon', e);
return undefined;
});
return {iconUri: data && `data:application/octet-stream;base64,${data.toString('base64')}`, ...info}
private completeIcon(device: Device, info: RawPackageInfo): PackageInfo {
const iconPath = path.join(info.folderPath, info.icon);
return {iconUri: `http://remote-file.localhost/${device.name}${iconPath}`, ...info};
}

async info(device: Device, id: string): Promise<PackageInfo | null> {
Expand Down

0 comments on commit e1be5c8

Please sign in to comment.