mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-28 04:11:16 +01:00
Merge pull request #227
* Search and install plugins PoC * Checksum * Tab sidebar for settings * Fix nested tabs, and tweaks * Table for plugin results * Deep links working * Focus window during deep links * Merge branch 'master' into plugin-directory * More stuff
This commit is contained in:
@@ -43,18 +43,6 @@ pub async fn store_launch_history<R: Runtime>(app_handle: &AppHandle<R>) -> Laun
|
||||
info
|
||||
}
|
||||
|
||||
pub fn get_os() -> &'static str {
|
||||
if cfg!(target_os = "windows") {
|
||||
"windows"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"macos"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"linux"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_num_launches<R: Runtime>(app_handle: &AppHandle<R>) -> i32 {
|
||||
app_handle.db().get_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, 0)
|
||||
}
|
||||
|
||||
105
src-tauri/src/import.rs
Normal file
105
src-tauri/src/import.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::error::Result;
|
||||
use log::info;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::read_to_string;
|
||||
use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use yaak_models::models::{
|
||||
Environment, Folder, GrpcRequest, HttpRequest, WebsocketRequest, Workspace,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, maybe_gen_id, maybe_gen_id_opt};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
pub(crate) async fn import_data<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
file_path: &str,
|
||||
) -> Result<BatchUpsertResult> {
|
||||
let plugin_manager = window.state::<PluginManager>();
|
||||
let file =
|
||||
read_to_string(file_path).unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
|
||||
let file_contents = file.as_str();
|
||||
let import_result = plugin_manager.import_data(window, file_contents).await?;
|
||||
|
||||
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
|
||||
|
||||
let resources = import_result.resources;
|
||||
|
||||
let workspaces: Vec<Workspace> = resources
|
||||
.workspaces
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Workspace>(v.id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let environments: Vec<Environment> = resources
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Environment>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let folders: Vec<Folder> = resources
|
||||
.folders
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Folder>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let http_requests: Vec<HttpRequest> = resources
|
||||
.http_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<HttpRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let grpc_requests: Vec<GrpcRequest> = resources
|
||||
.grpc_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<GrpcRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let websocket_requests: Vec<WebsocketRequest> = resources
|
||||
.websocket_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<WebsocketRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("Importing data");
|
||||
|
||||
let upserted = window.with_tx(|tx| {
|
||||
tx.batch_upsert(
|
||||
workspaces,
|
||||
environments,
|
||||
folders,
|
||||
http_requests,
|
||||
grpc_requests,
|
||||
websocket_requests,
|
||||
&UpdateSource::Import,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(upserted)
|
||||
}
|
||||
@@ -3,14 +3,15 @@ use crate::encoding::read_response_body;
|
||||
use crate::error::Error::GenericError;
|
||||
use crate::grpc::{build_metadata, metadata_to_map, resolve_grpc_request};
|
||||
use crate::http_request::send_http_request;
|
||||
use crate::import::import_data;
|
||||
use crate::notifications::YaakNotifier;
|
||||
use crate::render::{render_grpc_request, render_template};
|
||||
use crate::updates::{UpdateMode, UpdateTrigger, YaakUpdater};
|
||||
use crate::uri_scheme::handle_uri_scheme;
|
||||
use crate::uri_scheme::handle_deep_link;
|
||||
use error::Result as YaakResult;
|
||||
use eventsource_client::{EventParser, SSE};
|
||||
use log::{debug, error, info, warn};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@@ -19,24 +20,21 @@ use std::{fs, panic};
|
||||
use tauri::{AppHandle, Emitter, RunEvent, State, WebviewWindow, is_dev};
|
||||
use tauri::{Listener, Runtime};
|
||||
use tauri::{Manager, WindowEvent};
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tauri_plugin_log::fern::colors::ColoredLevelConfig;
|
||||
use tauri_plugin_log::{Builder, Target, TargetKind};
|
||||
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||
use tokio::fs::read_to_string;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::block_in_place;
|
||||
use yaak_common::window::WorkspaceWindowTrait;
|
||||
use yaak_grpc::manager::{DynamicMessage, GrpcHandle};
|
||||
use yaak_grpc::{Code, ServiceDefinition, deserialize_message, serialize_message};
|
||||
use yaak_models::models::{
|
||||
CookieJar, Environment, Folder, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType,
|
||||
GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, WebsocketRequest, Workspace,
|
||||
WorkspaceMeta,
|
||||
CookieJar, Environment, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType,
|
||||
GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, Workspace, WorkspaceMeta,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::{
|
||||
BatchUpsertResult, UpdateSource, get_workspace_export_resources, maybe_gen_id, maybe_gen_id_opt,
|
||||
};
|
||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
|
||||
use yaak_plugins::events::{
|
||||
BootResponse, CallHttpRequestActionRequest, FilterResponse,
|
||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||
@@ -55,6 +53,7 @@ mod error;
|
||||
mod grpc;
|
||||
mod history;
|
||||
mod http_request;
|
||||
mod import;
|
||||
mod notifications;
|
||||
mod plugin_events;
|
||||
mod render;
|
||||
@@ -778,98 +777,9 @@ async fn cmd_get_sse_events(file_path: &str) -> YaakResult<Vec<ServerSentEvent>>
|
||||
#[tauri::command]
|
||||
async fn cmd_import_data<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
app_handle: AppHandle<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
file_path: &str,
|
||||
) -> YaakResult<BatchUpsertResult> {
|
||||
let file = read_to_string(file_path)
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
|
||||
let file_contents = file.as_str();
|
||||
let import_result = plugin_manager.import_data(&window, file_contents).await?;
|
||||
|
||||
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
|
||||
|
||||
let resources = import_result.resources;
|
||||
|
||||
let workspaces: Vec<Workspace> = resources
|
||||
.workspaces
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Workspace>(v.id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let environments: Vec<Environment> = resources
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Environment>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let folders: Vec<Folder> = resources
|
||||
.folders
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Folder>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let http_requests: Vec<HttpRequest> = resources
|
||||
.http_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<HttpRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let grpc_requests: Vec<GrpcRequest> = resources
|
||||
.grpc_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<GrpcRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let websocket_requests: Vec<WebsocketRequest> = resources
|
||||
.websocket_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<WebsocketRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("Importing data");
|
||||
|
||||
let upserted = app_handle.with_tx(|tx| {
|
||||
tx.batch_upsert(
|
||||
workspaces,
|
||||
environments,
|
||||
folders,
|
||||
http_requests,
|
||||
grpc_requests,
|
||||
websocket_requests,
|
||||
&UpdateSource::Import,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(upserted)
|
||||
import_data(&window, file_path).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1066,7 +976,7 @@ async fn cmd_install_plugin<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
window: WebviewWindow<R>,
|
||||
) -> YaakResult<Plugin> {
|
||||
plugin_manager.add_plugin_by_dir(&PluginWindowContext::new(&window), &directory, true).await?;
|
||||
plugin_manager.add_plugin_by_dir(&PluginWindowContext::new(&window), &directory).await?;
|
||||
|
||||
Ok(app_handle.db().upsert_plugin(
|
||||
&Plugin {
|
||||
@@ -1255,6 +1165,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_updater::Builder::default().build())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
@@ -1272,6 +1183,21 @@ pub fn run() {
|
||||
|
||||
builder
|
||||
.setup(|app| {
|
||||
{
|
||||
let app_handle = app.app_handle().clone();
|
||||
app.deep_link().on_open_url(move |event| {
|
||||
info!("Handling deep link open");
|
||||
let app_handle = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
for url in event.urls() {
|
||||
if let Err(e) = handle_deep_link(&app_handle, &url).await {
|
||||
warn!("Failed to handle deep link {}: {e:?}", url.to_string());
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
let app_data_dir = app.path().app_data_dir().unwrap();
|
||||
create_dir_all(app_data_dir.clone()).expect("Problem creating App directory!");
|
||||
|
||||
@@ -1332,7 +1258,6 @@ pub fn run() {
|
||||
crate::commands::cmd_secure_template,
|
||||
crate::commands::cmd_show_workspace_key,
|
||||
])
|
||||
.register_uri_scheme_protocol("yaak", handle_uri_scheme)
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while running tauri application")
|
||||
.run(|app_handle, event| {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::history::{get_num_launches, get_os};
|
||||
use crate::history::get_num_launches;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use log::debug;
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow};
|
||||
use yaak_common::platform::get_os;
|
||||
use yaak_license::{LicenseCheckStatus, check_license};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
|
||||
@@ -126,7 +126,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
InternalEventPayload::ReloadResponse(_) => {
|
||||
InternalEventPayload::ReloadResponse(r) => {
|
||||
let plugins = app_handle.db().list_plugins().unwrap();
|
||||
for plugin in plugins {
|
||||
if plugin.directory != plugin_handle.dir {
|
||||
@@ -142,7 +142,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
&window_context,
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
||||
message: format!("Reloaded plugin {}@{}", r.name, r.version),
|
||||
icon: Some(Icon::Info),
|
||||
..Default::default()
|
||||
}),
|
||||
|
||||
@@ -1,25 +1,74 @@
|
||||
use crate::error::Result;
|
||||
use crate::import::import_data;
|
||||
use log::{info, warn};
|
||||
use tauri::{Manager, Runtime, UriSchemeContext};
|
||||
use std::collections::HashMap;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, Url};
|
||||
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};
|
||||
use yaak_plugins::api::get_plugin;
|
||||
use yaak_plugins::events::{Color, ShowToastRequest};
|
||||
use yaak_plugins::install::download_and_install;
|
||||
|
||||
pub(crate) fn handle_uri_scheme<R: Runtime>(
|
||||
a: UriSchemeContext<R>,
|
||||
req: http::Request<Vec<u8>>,
|
||||
) -> http::Response<Vec<u8>> {
|
||||
println!("------------- Yaak URI scheme invoked!");
|
||||
let uri = req.uri();
|
||||
let window = a
|
||||
.app_handle()
|
||||
.get_webview_window(a.webview_label())
|
||||
.expect("Failed to get webview window for URI scheme event");
|
||||
info!("Yaak URI scheme invoked with {uri:?} {window:?}");
|
||||
pub(crate) async fn handle_deep_link<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
url: &Url,
|
||||
) -> Result<()> {
|
||||
let command = url.domain().unwrap_or_default();
|
||||
info!("Yaak URI scheme invoked {}?{}", command, url.query().unwrap_or_default());
|
||||
|
||||
let path = uri.path();
|
||||
if path == "/data/import" {
|
||||
warn!("TODO: import data")
|
||||
} else if path == "/plugins/install" {
|
||||
warn!("TODO: install plugin")
|
||||
let query_map: HashMap<String, String> = url.query_pairs().into_owned().collect();
|
||||
let windows = app_handle.webview_windows();
|
||||
let (_, window) = windows.iter().next().unwrap();
|
||||
|
||||
match command {
|
||||
"install-plugin" => {
|
||||
let name = query_map.get("name").unwrap();
|
||||
let version = query_map.get("version").cloned();
|
||||
let plugin_version = get_plugin(&app_handle, &name, version).await?;
|
||||
_ = window.set_focus();
|
||||
let confirmed_install = app_handle
|
||||
.dialog()
|
||||
.message(format!(
|
||||
"Install plugin {}@{}?",
|
||||
plugin_version.name, plugin_version.version
|
||||
))
|
||||
.kind(MessageDialogKind::Info)
|
||||
.buttons(MessageDialogButtons::OkCustom("Install".to_string()))
|
||||
.blocking_show();
|
||||
if !confirmed_install {
|
||||
// Cancelled installation
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
download_and_install(window, &plugin_version).await?;
|
||||
app_handle.emit(
|
||||
"show_toast",
|
||||
ShowToastRequest {
|
||||
message: format!(
|
||||
"Installed {}@{}",
|
||||
plugin_version.name, plugin_version.version
|
||||
),
|
||||
color: Some(Color::Success),
|
||||
icon: None,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
"import-data" => {
|
||||
let file_path = query_map.get("path").unwrap();
|
||||
let results = import_data(window, file_path).await?;
|
||||
_ = window.set_focus();
|
||||
window.emit(
|
||||
"show_toast",
|
||||
ShowToastRequest {
|
||||
message: format!("Imported data for {} workspaces", results.workspaces.len()),
|
||||
color: Some(Color::Success),
|
||||
icon: None,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
warn!("Unknown deep link command: {command}");
|
||||
}
|
||||
}
|
||||
|
||||
let msg = format!("No handler found for {path}");
|
||||
tauri::http::Response::builder().status(404).body(msg.as_bytes().to_vec()).unwrap()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user