Implement bundled plugins

This commit is contained in:
Exidex 2024-03-27 19:41:39 +01:00
parent db91d65700
commit 6e6d04998c
25 changed files with 426 additions and 41 deletions

23
Cargo.lock generated
View file

@ -231,6 +231,9 @@ name = "anyhow"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
dependencies = [
"backtrace",
]
[[package]]
name = "approx"
@ -4448,6 +4451,25 @@ dependencies = [
"hashbrown 0.12.3",
]
[[package]]
name = "include_dir"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
dependencies = [
"include_dir_macros",
]
[[package]]
name = "include_dir_macros"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
dependencies = [
"proc-macro2 1.0.78",
"quote 1.0.35",
]
[[package]]
name = "indexmap"
version = "1.9.3"
@ -7116,6 +7138,7 @@ dependencies = [
"deno_runtime",
"directories",
"gix",
"include_dir",
"once_cell",
"regex",
"serde",

View file

@ -0,0 +1,2 @@
dist
node_modules

View file

@ -0,0 +1,13 @@
[gauntlet]
name = 'Bundled - Applications'
description = ''
[[entrypoint]]
id = 'default'
name = 'Default'
path = 'src/default.ts'
type = 'command-generator'
description = ''
[[supported_system]]
os = 'linux'

View file

@ -0,0 +1,17 @@
{
"name": "@project-gauntlet/bundled-plugin-application",
"private": true,
"scripts": {
"build": "gauntlet build",
"dev": "gauntlet dev"
},
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "0.3.0",
"typescript": "^5.3.3"
}
}

View file

@ -0,0 +1,38 @@
interface GeneratedCommand { // TODO Add this type to api
id: string
name: string
fn: () => void
}
export default function Default(): GeneratedCommand[] {
return [
{
id: 'generated-test-1',
name: 'Application 1',
fn: () => {
console.log('opening application 1')
}
},
{
id: 'generated-test-2',
name: 'Application 2',
fn: () => {
console.log('opening application 2')
}
},
{
id: 'generated-test-3',
name: 'Application 3',
fn: () => {
console.log('opening application 3')
}
},
{
id: 'generated-test-4',
name: 'Application 4',
fn: () => {
console.log('opening application 4')
}
}
]
}

View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"strict": true,
"module": "ES2022",
"esModuleInterop": true,
"target": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"types": ["@project-gauntlet/deno"]
},
"lib": ["ES2020"]
}

2
bundled_plugins/calculator/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
dist
node_modules

View file

@ -0,0 +1,11 @@
[gauntlet]
name = 'Bundled - Calculator'
description = ''
[[entrypoint]]
id = 'default'
name = 'Default'
path = 'src/default.tsx'
type = 'inline-view'
description = ''

View file

@ -0,0 +1,17 @@
{
"name": "@project-gauntlet/bundled-plugin-calculator",
"private": true,
"scripts": {
"build": "gauntlet build",
"dev": "gauntlet dev"
},
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "0.3.0",
"typescript": "^5.3.3"
}
}

View file

@ -0,0 +1,25 @@
import { Content, Inline } from "@project-gauntlet/api/components";
import { ReactNode } from "react";
export default function Default(props: { text: string }): ReactNode | undefined {
const text = props.text;
if (!text.startsWith("inline")) {
return undefined
}
return (
<Inline>
<Inline.Left>
<Content.Paragraph>
Testing inline view left {text}
</Content.Paragraph>
</Inline.Left>
<Inline.Separator/>
<Inline.Right>
<Content.Paragraph>
Testing inline view right
</Content.Paragraph>
</Inline.Right>
</Inline>
)
}

View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"strict": true,
"module": "ES2022",
"esModuleInterop": true,
"target": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"types": ["@project-gauntlet/deno"]
},
"lib": ["ES2020"]
}

View file

@ -25,26 +25,15 @@ program.command('build')
await program.parseAsync(process.argv);
function build(projectRoot: string, check: boolean) {
console.log("Building @project-gauntlet/api-build...")
execSync('npm run build --workspace @project-gauntlet/api-build', { stdio: "inherit", cwd: projectRoot});
console.log("Building @project-gauntlet/api...")
execSync('npm run build --workspace @project-gauntlet/api', { stdio: "inherit", cwd: projectRoot});
console.log("Building @project-gauntlet/deno...")
execSync('npm run build --workspace @project-gauntlet/deno', { stdio: "inherit", cwd: projectRoot });
console.log("Building @project-gauntlet/react...")
execSync('npm run build --workspace @project-gauntlet/react', { stdio: "inherit", cwd: projectRoot });
console.log("Building @project-gauntlet/react-renderer...")
execSync('npm run build --workspace @project-gauntlet/react-renderer', { stdio: "inherit", cwd: projectRoot });
console.log("Building @project-gauntlet/core...")
execSync('npm run build --workspace @project-gauntlet/core', { stdio: "inherit", cwd: projectRoot });
console.log("Building js...")
execSync('npm run build', { stdio: "inherit", cwd: projectRoot});
if (check) {
console.log("Check application...")
console.log("Checking rust...")
execSync('cargo check', { stdio: "inherit", cwd: projectRoot });
}
console.log("Building application...")
console.log("Building rust...")
execSync('cargo build --release', { stdio: "inherit", cwd: projectRoot });
}

141
package-lock.json generated
View file

@ -6,6 +6,7 @@
"": {
"name": "project-gauntlet",
"workspaces": [
"bundled_plugins/*",
"js/typings",
"js/build",
"js/api_build",
@ -16,6 +17,28 @@
"js/react_renderer"
]
},
"bundled_plugins/applications": {
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "0.3.0",
"@types/react": "^18.2.14",
"typescript": "^5.3.3"
}
},
"bundled_plugins/calculator": {
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "0.3.0",
"@types/react": "^18.2.14",
"typescript": "^5.3.3"
}
},
"js/api": {
"name": "@project-gauntlet/api",
"version": "0.1.0",
@ -106,6 +129,27 @@
"js/typings": {
"name": "@project-gauntlet/typings"
},
"node_modules/@dbus-types/dbus": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@dbus-types/dbus/-/dbus-0.0.4.tgz",
"integrity": "sha512-KRD48WayYXEmFfVyR4zrCpZ5tviBSU2NXddMOQ4AW1zPfLtEkg7gvVvZUL1/DlThUoFwMC+RRR+EK0yGtZq3Jw==",
"dev": true
},
"node_modules/@homebridge/long": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@homebridge/long/-/long-5.2.1.tgz",
"integrity": "sha512-i5Df8R63XNPCn+Nj1OgAoRdw9e+jHUQb3CNUbvJneI2iu3j4+OtzQj+5PA1Ce+747NR1SPqZSvyvD483dOT3AA==",
"dev": true
},
"node_modules/@homebridge/put": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/@homebridge/put/-/put-0.0.8.tgz",
"integrity": "sha512-mwxLHHqKebOmOSU0tsPEWQSBHGApPhuaqtNpCe7U+AMdsduweANiu64E9SXXUtdpyTjsOpgSMLhD1+kbLHD2gA==",
"dev": true,
"engines": {
"node": ">=0.3.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
@ -460,6 +504,14 @@
"resolved": "js/build",
"link": true
},
"node_modules/@project-gauntlet/bundled-plugin-application": {
"resolved": "bundled_plugins/applications",
"link": true
},
"node_modules/@project-gauntlet/bundled-plugin-calculator": {
"resolved": "bundled_plugins/calculator",
"link": true
},
"node_modules/@project-gauntlet/core": {
"resolved": "js/core",
"link": true
@ -476,6 +528,28 @@
"resolved": "js/react_renderer",
"link": true
},
"node_modules/@project-gauntlet/tools": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@project-gauntlet/tools/-/tools-0.3.0.tgz",
"integrity": "sha512-1meJKEWRAF5oVf3322aurxNRY3htPHeyWkPXAzGMo5MD+ZYpklCxP5MwJnWE1aWZmfAaIkr21tpDjZeuYjch+A==",
"dev": true,
"dependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.5",
"chalk": "^5.3.0",
"commander": "^11.1.0",
"dbus-ts": "^0.0.7",
"rollup": "^4.3.0",
"simple-git": "^3.22.0",
"toml": "^3.0.0",
"tslib": "^2.6.2",
"zod": "^3.22.4"
},
"bin": {
"gauntlet": "bin/main.js"
}
},
"node_modules/@project-gauntlet/typings": {
"resolved": "js/typings",
"link": true
@ -897,6 +971,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/chalk": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"dev": true,
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@ -925,6 +1011,18 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
},
"node_modules/dbus-ts": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/dbus-ts/-/dbus-ts-0.0.7.tgz",
"integrity": "sha512-2Iig5znVcrIMSAgTxy5DcUm2mIkgbFGP01YUSxSakkzQzrx+OH24LMIiopDAEWS8LR6V3/eVywO72us438eX0Q==",
"dev": true,
"dependencies": {
"@dbus-types/dbus": "^0.0.4",
"@homebridge/long": "^5.2.1",
"@homebridge/put": "^0.0.8",
"xml2js": "^0.4.23"
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -1363,6 +1461,12 @@
}
]
},
"node_modules/sax": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
"dev": true
},
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@ -1422,6 +1526,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"dev": true
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
@ -1465,10 +1575,41 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"dev": true,
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/zod": {
"version": "3.22.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
"integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View file

@ -5,6 +5,7 @@
"build": "npm run build --workspaces --if-present"
},
"workspaces": [
"bundled_plugins/*",
"js/typings",
"js/build",
"js/api_build",

View file

@ -4,7 +4,7 @@ edition = "2021"
[dependencies]
tokio = "1.28.1"
anyhow = "1"
anyhow = { version = "1", features = ["backtrace"] }
thiserror = "1"
#iced = { version = "0.12.0", features = ["tokio", "lazy", "advanced"] }
iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet", features = ["tokio", "lazy", "advanced", "multi-window", "image"] }
@ -20,5 +20,5 @@ itertools = "0.12.1"
[build-dependencies]
component_model = { path = "../component_model" }
anyhow = "1"
anyhow = { version = "1", features = ["backtrace"] }
convert_case = "0.6.0"

View file

@ -4,7 +4,7 @@ edition = "2021"
[dependencies]
gix = { version = "0.52.0", features = ["blocking-http-transport-curl"] }
anyhow = "1.0.75"
anyhow = { version = "1", features = ["backtrace"] }
tonic = "0.11.0"
prost = "0.12.3"

View file

@ -5,5 +5,5 @@ edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
anyhow = "1.0.75"
anyhow = { version = "1", features = ["backtrace"] }
indexmap = { version = "2.1.0", features = ["serde"] }

View file

@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
tokio = "1.28.1"
clap = { version = "4.3.22", features = ["derive"] }
anyhow = "1.0.75"
anyhow = { version = "1", features = ["backtrace"] }
thiserror = "1.0.48"
#iced = { version = "0.12.0", features = ["tokio", "lazy", "advanced"] }
iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet", features = ["tokio", "lazy", "advanced"] }

View file

@ -15,7 +15,7 @@ once_cell = "1.18.0"
gix = { version = "0.52.0", features = ["blocking-http-transport-curl"] }
tempfile = "3"
async-stream = "0.3.5"
anyhow = "1.0.75"
anyhow = { version = "1", features = ["backtrace"] }
thiserror = "1.0.48"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
@ -26,6 +26,7 @@ component_model = { path = "../component_model" }
tonic = "0.11.0"
client = { path = "../client" }
walkdir = "2.4.0"
include_dir = "0.7.3"
[features]
dev = []

View file

@ -0,0 +1,2 @@
ALTER TABLE plugin DROP COLUMN from_config;
ALTER TABLE plugin ADD COLUMN type TEXT NOT NULL DEFAULT ('normal');

View file

@ -32,16 +32,17 @@ async fn run_server() -> anyhow::Result<()> {
let search_index = SearchIndex::create_index()?;
let mut application_manager = ApplicationManager::create(search_index.clone()).await?;
if let Err(err) = application_manager.load_builtin_plugins().await {
tracing::error!("error loading bundled plugin(s): {:?}", err);
}
if cfg!(feature = "dev") {
let plugin_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../dev_plugin/dist").to_owned();
let plugin_path = std::fs::canonicalize(plugin_path).expect("valid path");
let plugin_path = plugin_path.to_str().expect("valid utf8");
let result = application_manager.save_local_plugin(plugin_path)
.await;
if let Err(err) = result {
tracing::error!("error loading dev plugin: {}", err);
if let Err(err) = application_manager.save_local_plugin(plugin_path).await {
tracing::error!("error loading dev plugin: {:?}", err);
}
}

View file

@ -1,5 +1,4 @@
use serde::Deserialize;
use tracing::{error, info};
use crate::dirs::Dirs;
use crate::plugins::data_db_repository::{DataDbRepository, DbWritePendingPlugin};
@ -42,17 +41,15 @@ impl ConfigReader {
match config_content {
Ok(config_content) => {
match toml::from_str(&config_content) {
Ok(config) => config,
Err(err) => {
error!("Unable to parse config, error: {error}", error = err.message());
toml::from_str(&config_content)
.unwrap_or_else(|err| {
tracing::error!("Unable to parse config, error: {:?}", err);
ApplicationConfig::default()
}
}
})
}
Err(_) => {
info!("No config found, using default configuration");
tracing::info!("No config found, using default configuration");
ApplicationConfig::default()
}

View file

@ -30,7 +30,8 @@ pub struct DbReadPlugin {
pub code: DbCode,
#[sqlx(json)]
pub permissions: DbPluginPermissions,
pub from_config: bool,
#[sqlx(rename = "type")]
pub plugin_type: String,
#[sqlx(json)]
pub preferences: HashMap<String, DbPluginPreference>,
#[sqlx(json)]
@ -70,7 +71,7 @@ pub struct DbWritePlugin {
pub entrypoints: Vec<DbWritePluginEntrypoint>,
pub asset_data: Vec<DbWritePluginAssetData>,
pub permissions: DbPluginPermissions,
pub from_config: bool,
pub plugin_type: String,
pub preferences: HashMap<String, DbPluginPreference>,
pub preferences_user_data: HashMap<String, DbPluginPreferenceUserData>,
}
@ -99,6 +100,13 @@ pub enum DbPluginEntrypointType {
CommandGenerator,
}
#[derive(Debug, Clone)]
pub enum DbPluginType {
Normal,
Config,
Bundled,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct DbPluginPermissions {
pub environment: Vec<String>,
@ -524,10 +532,10 @@ impl DataDbRepository {
.bind(plugin.enabled)
.bind(Json(plugin.code))
.bind(Json(plugin.permissions))
.bind(false)
.bind(Json(plugin.preferences))
.bind(Json(plugin.preferences_user_data))
.bind(plugin.description)
.bind(plugin.plugin_type)
.execute(&mut *tx)
.await?;
@ -580,6 +588,24 @@ pub fn db_entrypoint_from_str(value: &str) -> DbPluginEntrypointType {
"view" => DbPluginEntrypointType::View,
"inline-view" => DbPluginEntrypointType::InlineView,
"command-generator" => DbPluginEntrypointType::CommandGenerator,
_ => panic!("index contains illegal entrypoint_type: {}", value)
_ => panic!("illegal entrypoint_type: {}", value)
}
}
pub fn db_plugin_type_to_str(value: DbPluginType) -> &'static str {
match value {
DbPluginType::Normal => "normal",
DbPluginType::Config => "config",
DbPluginType::Bundled => "bundled"
}
}
pub fn db_plugin_type_from_str(value: &str) -> DbPluginType {
match value {
"normal" => DbPluginType::Normal,
"config" => DbPluginType::Config,
"bundled" => DbPluginType::Bundled,
_ => panic!("illegal plugin_type: {}", value)
}
}

View file

@ -2,17 +2,19 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::DirEntry;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::thread;
use anyhow::{anyhow, Context};
use include_dir::Dir;
use serde::{Deserialize, Serialize};
use walkdir::WalkDir;
use common::model::{DownloadStatus, PluginId};
use crate::plugins::data_db_repository::{DataDbRepository, db_entrypoint_to_str, DbCode, DbPluginAction, DbPluginActionShortcutKind, DbPluginEntrypointType, DbPluginPermissions, DbPluginPreference, DbPluginPreferenceUserData, DbPreferenceEnumValue, DbWritePlugin, DbWritePluginAssetData, DbWritePluginEntrypoint};
use crate::plugins::data_db_repository::{DataDbRepository, db_entrypoint_to_str, db_plugin_type_to_str, DbCode, DbPluginAction, DbPluginActionShortcutKind, DbPluginEntrypointType, DbPluginPermissions, DbPluginPreference, DbPluginPreferenceUserData, DbPluginType, DbPreferenceEnumValue, DbWritePlugin, DbWritePluginAssetData, DbWritePluginEntrypoint};
use crate::plugins::download_status::DownloadStatusHolder;
use crate::plugins::js::asset_data;
@ -57,7 +59,7 @@ impl PluginLoader {
entrypoints: plugin_data.entrypoints,
asset_data: plugin_data.asset_data,
permissions: plugin_data.permissions,
from_config: false,
plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(),
preferences: plugin_data.preferences,
preferences_user_data: HashMap::new(),
}).await?;
@ -96,7 +98,37 @@ impl PluginLoader {
entrypoints: plugin_data.entrypoints,
asset_data: plugin_data.asset_data,
permissions: plugin_data.permissions,
from_config: false,
plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(),
preferences: plugin_data.preferences,
preferences_user_data: HashMap::new()
}).await?;
Ok(plugin_id)
}
pub async fn save_builtin_plugin(&self, id: &str, dir: &Dir<'_>) -> anyhow::Result<PluginId> {
let plugin_id = PluginId::from_string(format!("builtin://{id}"));
let temp_dir = tempfile::tempdir()?;
dir.extract(&temp_dir)?;
let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id.clone())
.await
.context("Unable to read plugin directory")?;
// TODO instead of overwrite just update the code and assets
self.db_repository.remove_plugin(&plugin_data.id).await?;
self.db_repository.save_plugin(DbWritePlugin {
id: plugin_data.id,
name: plugin_data.name,
description: plugin_data.description,
enabled: true,
code: plugin_data.code,
entrypoints: plugin_data.entrypoints,
asset_data: plugin_data.asset_data,
permissions: plugin_data.permissions,
plugin_type: db_plugin_type_to_str(DbPluginType::Bundled).to_owned(),
preferences: plugin_data.preferences,
preferences_user_data: HashMap::new()
}).await?;
@ -162,6 +194,10 @@ impl PluginLoader {
let asset_data = WalkDir::new(&assets)
.into_iter()
.collect::<walkdir::Result<Vec<walkdir::DirEntry>>>()
.or_else(|err| match err.io_error() {
Some(err) if matches!(err.kind(), ErrorKind::NotFound) => Ok(vec![]),
_ => Err(err),
})
.context("Unable to get list of plugin asset data files")?
.into_iter()
.filter(|dir_entry| dir_entry.file_type().is_file())

View file

@ -1,4 +1,5 @@
use std::collections::HashMap;
use include_dir::{Dir, include_dir};
use common::model::{DownloadStatus, EntrypointId, PluginId, PropertyValue};
use common::rpc::{plugin_preference_user_data_from_npb, plugin_preference_user_data_to_npb, rpc_ui_property_value, RpcEntrypoint, RpcEntrypointTypeSettings, RpcEnumValue, RpcNoProtoBufPluginPreferenceUserData, RpcPlugin, RpcPluginPreference, RpcPluginPreferenceUserData, RpcPluginPreferenceValueType, RpcUiPropertyValue};
@ -19,6 +20,12 @@ mod loader;
mod run_status;
mod download_status;
static BUILTIN_PLUGINS: [(&str, Dir); 2] = [
("applications", include_dir!("$CARGO_MANIFEST_DIR/../../bundled_plugins/applications/dist")),
("calculator", include_dir!("$CARGO_MANIFEST_DIR/../../bundled_plugins/calculator/dist")),
];
pub struct ApplicationManager {
config_reader: ConfigReader,
search_index: SearchIndex,
@ -69,6 +76,18 @@ impl ApplicationManager {
Ok(())
}
pub async fn load_builtin_plugins(&self) -> anyhow::Result<()> {
for (id, dir) in &BUILTIN_PLUGINS {
tracing::info!(target = "plugin", "Saving builtin plugin with id: {:?}", id);
let plugin_id = self.plugin_downloader.save_builtin_plugin(id, dir).await?;
self.reload_plugin(plugin_id).await?;
}
Ok(())
}
pub async fn plugins(&self) -> anyhow::Result<Vec<RpcPlugin>> {
let result = self.db_repository
.list_plugins_and_entrypoints()