mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-11 17:29:44 +00:00
fix: implemented file server for custom UI commands on Windows (#24)
This commit is contained in:
parent
7865d18580
commit
c7003326db
@ -113,8 +113,12 @@ pub fn run() {
|
|||||||
Some(ext) => {
|
Some(ext) => {
|
||||||
// let app_state = app_handle.state::<tauri_plugin_jarvis::model::app_state::AppState>();
|
// let app_state = app_handle.state::<tauri_plugin_jarvis::model::app_state::AppState>();
|
||||||
// let extension_path = app_state.extension_path.lock().unwrap().clone();
|
// let extension_path = app_state.extension_path.lock().unwrap().clone();
|
||||||
// tauri_file_server(app_handle, request, extension_path)
|
tauri_file_server(
|
||||||
tauri_file_server(app_handle, request, ext.path.clone(), ext.dist.clone())
|
app_handle,
|
||||||
|
request,
|
||||||
|
ext.info.path.clone(),
|
||||||
|
ext.info.dist.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => tauri::http::Response::builder()
|
None => tauri::http::Response::builder()
|
||||||
.status(tauri::http::StatusCode::NOT_FOUND)
|
.status(tauri::http::StatusCode::NOT_FOUND)
|
||||||
|
@ -2,10 +2,12 @@ import { appState } from "@/stores"
|
|||||||
import { winExtMap } from "@/stores/winExtMap"
|
import { winExtMap } from "@/stores/winExtMap"
|
||||||
import { trimSlash } from "@/utils/url"
|
import { trimSlash } from "@/utils/url"
|
||||||
import { constructExtensionSupportDir } from "@kksh/api"
|
import { constructExtensionSupportDir } from "@kksh/api"
|
||||||
|
import { spawnExtensionFileServer } from "@kksh/api/commands"
|
||||||
import { CustomUiCmd, ExtPackageJsonExtra, TemplateUiCmd } from "@kksh/api/models"
|
import { CustomUiCmd, ExtPackageJsonExtra, TemplateUiCmd } from "@kksh/api/models"
|
||||||
import { launchNewExtWindow } from "@kksh/extension"
|
import { launchNewExtWindow } from "@kksh/extension"
|
||||||
import { convertFileSrc } from "@tauri-apps/api/core"
|
import { convertFileSrc } from "@tauri-apps/api/core"
|
||||||
import * as fs from "@tauri-apps/plugin-fs"
|
import * as fs from "@tauri-apps/plugin-fs"
|
||||||
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
|
|
||||||
export async function createExtSupportDir(extPath: string) {
|
export async function createExtSupportDir(extPath: string) {
|
||||||
@ -44,28 +46,43 @@ export async function onCustomUiCmdSelect(
|
|||||||
// console.log("onCustomUiCmdSelect", ext, cmd, isDev, hmr)
|
// console.log("onCustomUiCmdSelect", ext, cmd, isDev, hmr)
|
||||||
await createExtSupportDir(ext.extPath)
|
await createExtSupportDir(ext.extPath)
|
||||||
let url = cmd.main
|
let url = cmd.main
|
||||||
|
const useDevMain = hmr && isDev && cmd.devMain
|
||||||
if (hmr && isDev && cmd.devMain) {
|
if (useDevMain) {
|
||||||
url = cmd.devMain
|
url = cmd.devMain
|
||||||
} else {
|
} else {
|
||||||
url = decodeURIComponent(convertFileSrc(`${trimSlash(cmd.main)}`, "ext"))
|
url = decodeURIComponent(convertFileSrc(`${trimSlash(cmd.main)}`, "ext"))
|
||||||
}
|
}
|
||||||
const url2 = `/extension/ui-iframe?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}`
|
let url2 = `/extension/ui-iframe?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
if (cmd.window) {
|
if (cmd.window) {
|
||||||
const winLabel = await winExtMap.registerExtensionWithWindow({
|
const winLabel = await winExtMap.registerExtensionWithWindow({
|
||||||
extPath: ext.extPath,
|
extPath: ext.extPath,
|
||||||
dist: cmd.dist
|
dist: cmd.dist
|
||||||
})
|
})
|
||||||
console.log("Launch new window, ", winLabel)
|
if (platform() === "windows" && !useDevMain) {
|
||||||
|
const addr = await spawnExtensionFileServer(winLabel)
|
||||||
|
const newUrl = `http://${addr}`
|
||||||
|
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
|
}
|
||||||
|
console.log("URL 2", url2)
|
||||||
const window = launchNewExtWindow(winLabel, url2, cmd.window)
|
const window = launchNewExtWindow(winLabel, url2, cmd.window)
|
||||||
window.onCloseRequested(async (event) => {
|
window.onCloseRequested(async (event) => {
|
||||||
await winExtMap.unregisterExtensionFromWindow(winLabel)
|
await winExtMap.unregisterExtensionFromWindow(winLabel)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log("Launch main window")
|
console.log("Launch main window")
|
||||||
return winExtMap
|
const winLabel = await winExtMap.registerExtensionWithWindow({
|
||||||
.registerExtensionWithWindow({ windowLabel: "main", extPath: ext.extPath, dist: cmd.dist })
|
windowLabel: "main",
|
||||||
.then(() => goto(url2))
|
extPath: ext.extPath,
|
||||||
|
dist: cmd.dist
|
||||||
|
})
|
||||||
|
if (platform() === "windows" && !useDevMain) {
|
||||||
|
const addr = await spawnExtensionFileServer(winLabel) // addr has format "127.0.0.1:<port>"
|
||||||
|
console.log("Extension file server address: ", addr)
|
||||||
|
const newUrl = `http://${addr}`
|
||||||
|
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
|
}
|
||||||
|
console.log("URL 2", url2)
|
||||||
|
goto(url2)
|
||||||
}
|
}
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
import { cn, commandScore } from "@kksh/ui/utils"
|
import { cn, commandScore } from "@kksh/ui/utils"
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
import { exit } from "@tauri-apps/plugin-process"
|
import { exit } from "@tauri-apps/plugin-process"
|
||||||
import { CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
||||||
|
|
||||||
let inputEle: HTMLInputElement | null = null
|
let inputEle: HTMLInputElement | null = null
|
||||||
function onKeyDown(event: KeyboardEvent) {
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
@ -100,7 +100,6 @@
|
|||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content class="w-80">
|
<DropdownMenu.Content class="w-80">
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Item onclick={() => exit()}>
|
<DropdownMenu.Item onclick={() => exit()}>
|
||||||
<CircleXIcon class="h-4 w-4 text-red-500" />
|
<CircleXIcon class="h-4 w-4 text-red-500" />
|
||||||
Quit
|
Quit
|
||||||
@ -116,12 +115,12 @@
|
|||||||
<DropdownMenu.Item onclick={toggleDevTools}>
|
<DropdownMenu.Item onclick={toggleDevTools}>
|
||||||
<Icon icon="mingcute:code-fill" class="mr-2 h-5 w-5 text-green-500" />
|
<Icon icon="mingcute:code-fill" class="mr-2 h-5 w-5 text-green-500" />
|
||||||
Toggle Devtools
|
Toggle Devtools
|
||||||
<DropdownMenu.Shortcut>⌃+Shift+I</DropdownMenu.Shortcut>
|
<DropdownMenu.Shortcut><span class="flex items-center">⌃+<ArrowBigUpIcon class="w-4 h-4" />+I</span></DropdownMenu.Shortcut>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item onclick={() => location.reload()}>
|
<DropdownMenu.Item onclick={() => location.reload()}>
|
||||||
<RefreshCcwIcon class="mr-2 h-4 w-4 text-green-500" />
|
<RefreshCcwIcon class="mr-2 h-4 w-4 text-green-500" />
|
||||||
Reload Window
|
Reload Window
|
||||||
<DropdownMenu.Shortcut>⌃+Shift+R</DropdownMenu.Shortcut>
|
<DropdownMenu.Shortcut><span class="flex items-center">⌃+<ArrowBigUpIcon class="w-4 h-4" />+R</span></DropdownMenu.Shortcut>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
// navigateTo(localePath("/"))
|
// navigateTo(localePath("/"))
|
||||||
// },
|
// },
|
||||||
goBack: async () => {
|
goBack: async () => {
|
||||||
|
console.log("goBack iframe ui API called")
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goto("/")
|
goto("/")
|
||||||
} else {
|
} else {
|
||||||
@ -132,6 +133,7 @@
|
|||||||
} satisfies IApp
|
} satisfies IApp
|
||||||
|
|
||||||
function onBackBtnClicked() {
|
function onBackBtnClicked() {
|
||||||
|
console.log("onBackBtnClicked")
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goHome()
|
goHome()
|
||||||
} else {
|
} else {
|
||||||
@ -173,13 +175,12 @@
|
|||||||
class={cn("absolute", positionToTailwindClasses(uiControl.backBtnPosition))}
|
class={cn("absolute", positionToTailwindClasses(uiControl.backBtnPosition))}
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
data-tauri-drag-region
|
|
||||||
onclick={onBackBtnClicked}
|
onclick={onBackBtnClicked}
|
||||||
>
|
>
|
||||||
{#if appWin.label === "main"}
|
{#if appWin.label === "main"}
|
||||||
<ArrowLeftIcon class="w-4" data-tauri-drag-region />
|
<ArrowLeftIcon class="w-4" />
|
||||||
{:else}
|
{:else}
|
||||||
<XIcon class="w-4" data-tauri-drag-region />
|
<XIcon class="w-4" />
|
||||||
{/if}
|
{/if}
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
@ -198,7 +199,6 @@
|
|||||||
class={cn("absolute", positionToTailwindClasses(uiControl.refreshBtnPosition))}
|
class={cn("absolute", positionToTailwindClasses(uiControl.refreshBtnPosition))}
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
data-tauri-drag-region
|
|
||||||
onclick={iframeUiAPI.reloadPage}
|
onclick={iframeUiAPI.reloadPage}
|
||||||
>
|
>
|
||||||
<RefreshCcwIcon class="w-4" />
|
<RefreshCcwIcon class="w-4" />
|
||||||
|
@ -47,9 +47,9 @@
|
|||||||
<Switch bind:checked={$appConfig} />
|
<Switch bind:checked={$appConfig} />
|
||||||
</li> -->
|
</li> -->
|
||||||
</ul>
|
</ul>
|
||||||
{#if dev}
|
<!-- {#if dev}
|
||||||
<Shiki class="w-full overflow-x-auto" lang="json" code={JSON.stringify($appConfig, null, 2)} />
|
<Shiki class="w-full overflow-x-auto" lang="json" code={JSON.stringify($appConfig, null, 2)} />
|
||||||
{/if}
|
{/if} -->
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -31,6 +31,12 @@ export function unregisterExtensionWindow(label: string): Promise<void> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function spawnExtensionFileServer(windowLabel: string): Promise<string> {
|
||||||
|
return invoke<string>(generateJarvisPluginCommand("spawn_extension_file_server"), {
|
||||||
|
windowLabel
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function registerExtensionSpawnedProcess(windowLabel: string, pid: number): Promise<void> {
|
export function registerExtensionSpawnedProcess(windowLabel: string, pid: number): Promise<void> {
|
||||||
return invoke(generateJarvisPluginCommand("register_extension_spawned_process"), {
|
return invoke(generateJarvisPluginCommand("register_extension_spawned_process"), {
|
||||||
windowLabel,
|
windowLabel,
|
||||||
|
@ -62,6 +62,7 @@ const COMMANDS: &[&str] = &[
|
|||||||
"register_extension_spawned_process",
|
"register_extension_spawned_process",
|
||||||
"unregister_extension_spawned_process",
|
"unregister_extension_spawned_process",
|
||||||
"unregister_extension_window",
|
"unregister_extension_window",
|
||||||
|
"spawn_extension_file_server",
|
||||||
"get_ext_label_map",
|
"get_ext_label_map",
|
||||||
// "ext_store_wrapper_set",
|
// "ext_store_wrapper_set",
|
||||||
// "ext_store_wrapper_get",
|
// "ext_store_wrapper_get",
|
||||||
|
@ -58,6 +58,7 @@ commands.allow = [
|
|||||||
"is_window_label_registered",
|
"is_window_label_registered",
|
||||||
"register_extension_window",
|
"register_extension_window",
|
||||||
"unregister_extension_window",
|
"unregister_extension_window",
|
||||||
|
"spawn_extension_file_server",
|
||||||
"register_extension_spawned_process",
|
"register_extension_spawned_process",
|
||||||
"unregister_extension_spawned_process",
|
"unregister_extension_spawned_process",
|
||||||
"get_ext_label_map",
|
"get_ext_label_map",
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-spawn-extension-file-server"
|
||||||
|
description = "Enables the spawn_extension_file_server command without any pre-configured scope."
|
||||||
|
commands.allow = ["spawn_extension_file_server"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-spawn-extension-file-server"
|
||||||
|
description = "Denies the spawn_extension_file_server command without any pre-configured scope."
|
||||||
|
commands.deny = ["spawn_extension_file_server"]
|
@ -1610,6 +1610,32 @@ Denies the sleep_displays command without any pre-configured scope.
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
`jarvis:allow-spawn-extension-file-server`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Enables the spawn_extension_file_server command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`jarvis:deny-spawn-extension-file-server`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Denies the spawn_extension_file_server command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
`jarvis:allow-start-server`
|
`jarvis:allow-start-server`
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
@ -909,6 +909,16 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "deny-sleep-displays"
|
"const": "deny-sleep-displays"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the spawn_extension_file_server command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "allow-spawn-extension-file-server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the spawn_extension_file_server command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "deny-spawn-extension-file-server"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the start_server command without any pre-configured scope.",
|
"description": "Enables the start_server command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
use crate::JarvisState;
|
use crate::JarvisState;
|
||||||
use crate::{
|
use crate::{
|
||||||
model::{
|
model::{
|
||||||
extension::Extension,
|
extension::{Extension, ExtensionInfo},
|
||||||
manifest::{ExtPackageJsonExtra, MANIFEST_FILE_NAME},
|
manifest::{ExtPackageJsonExtra, MANIFEST_FILE_NAME},
|
||||||
},
|
},
|
||||||
utils::manifest::load_jarvis_ext_manifest,
|
utils::manifest::load_jarvis_ext_manifest,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::{fmt::format, path::PathBuf};
|
use std::net::{SocketAddr, TcpListener};
|
||||||
use tauri::{command, AppHandle, Runtime, State, Window};
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
use tauri::{AppHandle, Runtime, State, Window};
|
||||||
|
use tower_http::cors::CorsLayer;
|
||||||
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
/// manifest_path can be folder of package.json
|
/// manifest_path can be folder of package.json
|
||||||
/// If it's a folder, join it with package.json
|
/// If it's a folder, join it with package.json
|
||||||
@ -61,6 +67,9 @@ pub async fn is_window_label_registered<R: Runtime>(
|
|||||||
.contains_key(label.as_str()))
|
.contains_key(label.as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When ui iframe ext loaded, register the window containing the iframe with the extension info
|
||||||
|
/// Extension info contains path to extension command and dist path, so Kunkun's custom file server knows where to load
|
||||||
|
/// static files from when a request is received from a window.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn register_extension_window<R: Runtime>(
|
pub async fn register_extension_window<R: Runtime>(
|
||||||
_app: AppHandle<R>,
|
_app: AppHandle<R>,
|
||||||
@ -74,17 +83,14 @@ pub async fn register_extension_window<R: Runtime>(
|
|||||||
None => format!("main:ext:{}", uuid::Uuid::new_v4()),
|
None => format!("main:ext:{}", uuid::Uuid::new_v4()),
|
||||||
};
|
};
|
||||||
let mut label_ext_map = state.window_label_ext_map.lock().unwrap();
|
let mut label_ext_map = state.window_label_ext_map.lock().unwrap();
|
||||||
// if label_ext_map.contains_key(window_label_2.as_str()) {
|
|
||||||
// return Err(format!(
|
|
||||||
// "Window with label {} is already registered",
|
|
||||||
// &window_label_2
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
let ext = Extension {
|
let ext = Extension {
|
||||||
path: extension_path,
|
info: ExtensionInfo {
|
||||||
processes: vec![],
|
path: extension_path,
|
||||||
dist: dist,
|
processes: vec![],
|
||||||
// identifier: manifest.kunkun.identifier,
|
dist: dist,
|
||||||
|
},
|
||||||
|
shutdown_handle: Arc::new(Mutex::new(None)),
|
||||||
|
server_handle: Arc::new(Mutex::new(None)),
|
||||||
};
|
};
|
||||||
label_ext_map.insert(window_label_2.clone(), ext);
|
label_ext_map.insert(window_label_2.clone(), ext);
|
||||||
Ok(window_label_2)
|
Ok(window_label_2)
|
||||||
@ -106,7 +112,7 @@ pub async fn register_extension_spawned_process<R: Runtime>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let ext = label_ext_map.get_mut(window_label.as_str()).unwrap();
|
let ext = label_ext_map.get_mut(window_label.as_str()).unwrap();
|
||||||
ext.processes.push(pid);
|
ext.info.processes.push(pid);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +127,7 @@ pub async fn unregister_extension_spawned_process<R: Runtime>(
|
|||||||
label_ext_map
|
label_ext_map
|
||||||
.get_mut(window_label.as_str())
|
.get_mut(window_label.as_str())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.info
|
||||||
.processes
|
.processes
|
||||||
.retain(|p| *p != pid);
|
.retain(|p| *p != pid);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -131,20 +138,95 @@ pub async fn get_ext_label_map<R: Runtime>(
|
|||||||
_app: AppHandle<R>,
|
_app: AppHandle<R>,
|
||||||
_window: Window<R>,
|
_window: Window<R>,
|
||||||
state: State<'_, JarvisState>,
|
state: State<'_, JarvisState>,
|
||||||
) -> Result<HashMap<String, Extension>, String> {
|
) -> Result<HashMap<String, ExtensionInfo>, String> {
|
||||||
Ok(state.window_label_ext_map.lock().unwrap().clone())
|
let label_ext_map = state.window_label_ext_map.lock().unwrap();
|
||||||
|
// turn label_ext_map from HashMap<String, Extension> to HashMap<String, ExtensionInfo>
|
||||||
|
let label_ext_map_info: HashMap<String, ExtensionInfo> = label_ext_map
|
||||||
|
.iter()
|
||||||
|
.map(|(label, ext)| (label.clone(), ext.info.clone()))
|
||||||
|
.collect();
|
||||||
|
Ok(label_ext_map_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// unregister extension window
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn unregister_extension_window<R: Runtime>(
|
pub async fn unregister_extension_window<R: Runtime>(
|
||||||
_app: AppHandle<R>,
|
_app: AppHandle<R>,
|
||||||
state: State<'_, JarvisState>,
|
state: State<'_, JarvisState>,
|
||||||
label: String,
|
label: String,
|
||||||
) -> Result<bool, String> {
|
) -> Result<(), String> {
|
||||||
Ok(state
|
// find extension, if there is shutdown handle, shutdown it
|
||||||
.window_label_ext_map
|
let mut label_ext_map = state.window_label_ext_map.lock().unwrap();
|
||||||
.lock()
|
// find extension info with window_label
|
||||||
.unwrap()
|
let ext = label_ext_map.get(label.as_str()).cloned();
|
||||||
.remove(label.as_str())
|
// drop(label_ext_map);
|
||||||
.is_some())
|
match ext {
|
||||||
|
Some(ext) => {
|
||||||
|
let shutdown_handle = ext.shutdown_handle.lock().unwrap();
|
||||||
|
if let Some(shutdown_handle) = shutdown_handle.as_ref() {
|
||||||
|
shutdown_handle.shutdown();
|
||||||
|
log::info!("Shutdown extension file server with label {}", label);
|
||||||
|
}
|
||||||
|
let server_handle = ext.server_handle.lock().unwrap();
|
||||||
|
if let Some(server_handle) = server_handle.as_ref() {
|
||||||
|
server_handle.abort();
|
||||||
|
log::info!("Abort extension file server with label {}", label);
|
||||||
|
}
|
||||||
|
label_ext_map.remove(label.as_str());
|
||||||
|
log::info!("Unregistered extension window with label {}", label);
|
||||||
|
}
|
||||||
|
None => return Err(format!("Extension with label {} not found", label)),
|
||||||
|
}
|
||||||
|
// state
|
||||||
|
// .window_label_ext_map
|
||||||
|
// .lock()
|
||||||
|
// .unwrap()
|
||||||
|
// .remove(label.as_str())
|
||||||
|
// .is_some()
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// spawn extension file server, only for Windows
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn spawn_extension_file_server<R: Runtime>(
|
||||||
|
_app: AppHandle<R>,
|
||||||
|
state: State<'_, JarvisState>,
|
||||||
|
window_label: String,
|
||||||
|
) -> Result<SocketAddr, String> {
|
||||||
|
let mut label_ext_map = state.window_label_ext_map.lock().unwrap();
|
||||||
|
// find extension info with window_label
|
||||||
|
let ext = label_ext_map.get(window_label.as_str());
|
||||||
|
if ext.is_none() {
|
||||||
|
return Err(format!("Extension with label {} not found", window_label));
|
||||||
|
}
|
||||||
|
let ext = ext.unwrap();
|
||||||
|
let mut ext_path = ext.info.path.clone();
|
||||||
|
if let Some(dist) = ext.info.dist.clone() {
|
||||||
|
ext_path = ext_path.join(dist);
|
||||||
|
}
|
||||||
|
// TODO: spawn file server
|
||||||
|
let shutdown_handle = axum_server::Handle::new();
|
||||||
|
let shutdown_handle_clone = shutdown_handle.clone();
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let addr = listener.local_addr().unwrap();
|
||||||
|
let rest_router = axum::Router::new()
|
||||||
|
.layer(CorsLayer::permissive())
|
||||||
|
.nest_service("/", ServeDir::new(ext_path));
|
||||||
|
|
||||||
|
let server_handle = tauri::async_runtime::spawn(async move {
|
||||||
|
// axum_server::bind(addr)
|
||||||
|
axum_server::from_tcp(listener)
|
||||||
|
.handle(shutdown_handle_clone)
|
||||||
|
.serve(rest_router.into_make_service())
|
||||||
|
// .serve(combined_router.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
// add server handle and shutdown handle to extension
|
||||||
|
let mut ext = label_ext_map.get_mut(window_label.as_str()).unwrap();
|
||||||
|
ext.server_handle.lock().unwrap().replace(server_handle);
|
||||||
|
ext.shutdown_handle.lock().unwrap().replace(shutdown_handle);
|
||||||
|
|
||||||
|
// TODO: add server handle and shutdown handle
|
||||||
|
Ok(addr)
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
commands::extension::register_extension_spawned_process,
|
commands::extension::register_extension_spawned_process,
|
||||||
commands::extension::unregister_extension_spawned_process,
|
commands::extension::unregister_extension_spawned_process,
|
||||||
commands::extension::get_ext_label_map,
|
commands::extension::get_ext_label_map,
|
||||||
|
commands::extension::spawn_extension_file_server,
|
||||||
/* ---------------------- extension storage API wrapper --------------------- */
|
/* ---------------------- extension storage API wrapper --------------------- */
|
||||||
// commands::storage::ext_store_wrapper_set,
|
// commands::storage::ext_store_wrapper_set,
|
||||||
// commands::storage::ext_store_wrapper_get,
|
// commands::storage::ext_store_wrapper_get,
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
use super::manifest::Permissions;
|
use super::manifest::Permissions;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::{
|
||||||
|
net::SocketAddr,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Extension {
|
pub struct ExtensionInfo {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub processes: Vec<u32>,
|
pub processes: Vec<u32>,
|
||||||
pub dist: Option<String>,
|
pub dist: Option<String>,
|
||||||
// pub identifier: String,
|
// pub identifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Extension {
|
||||||
|
pub info: ExtensionInfo,
|
||||||
|
pub shutdown_handle: Arc<Mutex<Option<axum_server::Handle>>>,
|
||||||
|
pub server_handle: Arc<std::sync::Mutex<Option<tauri::async_runtime::JoinHandle<()>>>>,
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user