mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-22 01:19:39 +01:00
NodeJS Plugin Runtime (#53)
This commit is contained in:
25
src-tauri/tauri-plugin-plugin-runtime/src/lib.rs
Normal file
25
src-tauri/tauri-plugin-plugin-runtime/src/lib.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
extern crate core;
|
||||
|
||||
use tauri::plugin::{Builder, TauriPlugin};
|
||||
use tauri::{Manager, Runtime};
|
||||
|
||||
use crate::manager::PluginManager;
|
||||
|
||||
pub mod manager;
|
||||
mod nodejs;
|
||||
|
||||
pub mod plugin_runtime {
|
||||
tonic::include_proto!("yaak.plugins.runtime");
|
||||
}
|
||||
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("plugin_runtime")
|
||||
.setup(|app, _| {
|
||||
tauri::async_runtime::block_on(async move {
|
||||
let manager = PluginManager::new(&app).await;
|
||||
app.manage(manager);
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.build()
|
||||
}
|
||||
84
src-tauri/tauri-plugin-plugin-runtime/src/manager.rs
Normal file
84
src-tauri/tauri-plugin-plugin-runtime/src/manager.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use log::{debug, info};
|
||||
use tauri::{AppHandle, Manager, Runtime};
|
||||
use tokio::sync::Mutex;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::nodejs::node_start;
|
||||
use crate::plugin_runtime::plugin_runtime_client::PluginRuntimeClient;
|
||||
use crate::plugin_runtime::{HookExportRequest, HookImportRequest, HookResponse, HookResponseFilterRequest};
|
||||
|
||||
pub struct PluginManager {
|
||||
client: Mutex<PluginRuntimeClient<Channel>>,
|
||||
}
|
||||
|
||||
impl PluginManager {
|
||||
pub async fn new<R: Runtime>(app_handle: &AppHandle<R>) -> PluginManager {
|
||||
let temp_dir = app_handle.path().temp_dir().unwrap();
|
||||
|
||||
let addr = node_start(app_handle, &temp_dir).await;
|
||||
info!("Connecting to gRPC client at {addr}");
|
||||
|
||||
let client = match PluginRuntimeClient::connect(addr.clone()).await {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
panic!("{}", err.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
PluginManager {
|
||||
client: Mutex::new(client),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_import(&self, data: &str) -> Result<HookResponse, String> {
|
||||
let response = self
|
||||
.client
|
||||
.lock()
|
||||
.await
|
||||
.hook_import(tonic::Request::new(HookImportRequest {
|
||||
data: data.to_string(),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| e.message().to_string())?;
|
||||
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
pub async fn run_export_curl(&self, request: &str) -> Result<HookResponse, String> {
|
||||
let response = self
|
||||
.client
|
||||
.lock()
|
||||
.await
|
||||
.hook_export(tonic::Request::new(HookExportRequest {
|
||||
request: request.to_string(),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| e.message().to_string())?;
|
||||
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
pub async fn run_response_filter(
|
||||
&self,
|
||||
filter: &str,
|
||||
body: &str,
|
||||
content_type: &str,
|
||||
) -> Result<HookResponse, String> {
|
||||
debug!("Running plugin filter");
|
||||
let response = self
|
||||
.client
|
||||
.lock()
|
||||
.await
|
||||
.hook_response_filter(tonic::Request::new(HookResponseFilterRequest {
|
||||
filter: filter.to_string(),
|
||||
body: body.to_string(),
|
||||
content_type: content_type.to_string(),
|
||||
}))
|
||||
.await
|
||||
.map_err(|e| e.message().to_string())?;
|
||||
|
||||
let result = response.into_inner();
|
||||
debug!("Ran plugin response filter {}", result.data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
72
src-tauri/tauri-plugin-plugin-runtime/src/nodejs.rs
Normal file
72
src-tauri/tauri-plugin-plugin-runtime/src/nodejs.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use log::{debug, info};
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use serde;
|
||||
use serde::Deserialize;
|
||||
use tauri::path::BaseDirectory;
|
||||
use tauri::{AppHandle, Manager, Runtime};
|
||||
use tauri_plugin_shell::process::CommandEvent;
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use tokio::fs;
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
struct PortFile {
|
||||
port: i32,
|
||||
}
|
||||
|
||||
pub async fn node_start<R: Runtime>(app: &AppHandle<R>, temp_dir: &PathBuf) -> String {
|
||||
let port_file_path = temp_dir.join(Alphanumeric.sample_string(&mut rand::thread_rng(), 10));
|
||||
|
||||
let plugins_dir = app
|
||||
.path()
|
||||
.resolve("plugins", BaseDirectory::Resource)
|
||||
.expect("failed to resolve plugin directory resource");
|
||||
|
||||
info!(
|
||||
"Starting plugin runtime port_file={} plugins_dir={}",
|
||||
port_file_path.to_string_lossy(),
|
||||
plugins_dir.to_string_lossy(),
|
||||
);
|
||||
|
||||
let (mut rx, _child) = app
|
||||
.shell()
|
||||
.sidecar("yaakplugins")
|
||||
.unwrap()
|
||||
.env("GRPC_PORT_FILE_PATH", port_file_path.clone())
|
||||
.env("PLUGINS_DIR", plugins_dir)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// read events such as stdout
|
||||
while let Some(event) = rx.recv().await {
|
||||
if let CommandEvent::Stdout(line) = event {
|
||||
println!("{}", String::from_utf8_lossy(line.as_slice()));
|
||||
} else if let CommandEvent::Stderr(line) = event {
|
||||
println!("{}", String::from_utf8_lossy(line.as_slice()));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let port_file_contents = loop {
|
||||
if start.elapsed().as_millis() > 30000 {
|
||||
panic!("Failed to read port file in time");
|
||||
}
|
||||
|
||||
match fs::read_to_string(port_file_path.clone()).await {
|
||||
Ok(s) => break s,
|
||||
Err(err) => {
|
||||
debug!("Failed to read port file {}", err.to_string());
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let port_file: PortFile = serde_json::from_str(port_file_contents.as_str()).unwrap();
|
||||
info!("Started plugin runtime on :{}", port_file.port);
|
||||
format!("http://localhost:{}", port_file.port)
|
||||
}
|
||||
Reference in New Issue
Block a user