From 6e6d04998cfd55d026a486a3913df2f7213b81af Mon Sep 17 00:00:00 2001 From: Exidex <16986685+Exidex@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:41:39 +0100 Subject: [PATCH] Implement bundled plugins --- Cargo.lock | 23 +++ bundled_plugins/applications/.gitignore | 2 + bundled_plugins/applications/gauntlet.toml | 13 ++ bundled_plugins/applications/package.json | 17 +++ bundled_plugins/applications/src/default.ts | 38 +++++ bundled_plugins/applications/tsconfig.json | 12 ++ bundled_plugins/calculator/.gitignore | 2 + bundled_plugins/calculator/gauntlet.toml | 11 ++ bundled_plugins/calculator/package.json | 17 +++ bundled_plugins/calculator/src/default.tsx | 25 ++++ bundled_plugins/calculator/tsconfig.json | 12 ++ js/build/src/main.ts | 19 +-- package-lock.json | 141 ++++++++++++++++++ package.json | 1 + rust/client/Cargo.toml | 4 +- rust/common/Cargo.toml | 2 +- rust/component_model/Cargo.toml | 2 +- rust/management_client/Cargo.toml | 2 +- rust/server/Cargo.toml | 3 +- rust/server/db_migrations/6_plugin_type.sql | 2 + rust/server/src/lib.rs | 11 +- rust/server/src/plugins/config_reader.rs | 13 +- rust/server/src/plugins/data_db_repository.rs | 34 ++++- rust/server/src/plugins/loader.rs | 42 +++++- rust/server/src/plugins/mod.rs | 19 +++ 25 files changed, 426 insertions(+), 41 deletions(-) create mode 100644 bundled_plugins/applications/.gitignore create mode 100644 bundled_plugins/applications/gauntlet.toml create mode 100644 bundled_plugins/applications/package.json create mode 100644 bundled_plugins/applications/src/default.ts create mode 100644 bundled_plugins/applications/tsconfig.json create mode 100644 bundled_plugins/calculator/.gitignore create mode 100644 bundled_plugins/calculator/gauntlet.toml create mode 100644 bundled_plugins/calculator/package.json create mode 100644 bundled_plugins/calculator/src/default.tsx create mode 100644 bundled_plugins/calculator/tsconfig.json create mode 100644 rust/server/db_migrations/6_plugin_type.sql diff --git a/Cargo.lock b/Cargo.lock index fca7751..00f8dd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/bundled_plugins/applications/.gitignore b/bundled_plugins/applications/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/bundled_plugins/applications/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/bundled_plugins/applications/gauntlet.toml b/bundled_plugins/applications/gauntlet.toml new file mode 100644 index 0000000..92fdade --- /dev/null +++ b/bundled_plugins/applications/gauntlet.toml @@ -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' diff --git a/bundled_plugins/applications/package.json b/bundled_plugins/applications/package.json new file mode 100644 index 0000000..c29b270 --- /dev/null +++ b/bundled_plugins/applications/package.json @@ -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" + } +} diff --git a/bundled_plugins/applications/src/default.ts b/bundled_plugins/applications/src/default.ts new file mode 100644 index 0000000..0a5f494 --- /dev/null +++ b/bundled_plugins/applications/src/default.ts @@ -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') + } + } + ] +} diff --git a/bundled_plugins/applications/tsconfig.json b/bundled_plugins/applications/tsconfig.json new file mode 100644 index 0000000..cbe7961 --- /dev/null +++ b/bundled_plugins/applications/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "types": ["@project-gauntlet/deno"] + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/bundled_plugins/calculator/.gitignore b/bundled_plugins/calculator/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/bundled_plugins/calculator/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/bundled_plugins/calculator/gauntlet.toml b/bundled_plugins/calculator/gauntlet.toml new file mode 100644 index 0000000..7d300a9 --- /dev/null +++ b/bundled_plugins/calculator/gauntlet.toml @@ -0,0 +1,11 @@ +[gauntlet] +name = 'Bundled - Calculator' +description = '' + +[[entrypoint]] +id = 'default' +name = 'Default' +path = 'src/default.tsx' +type = 'inline-view' +description = '' + diff --git a/bundled_plugins/calculator/package.json b/bundled_plugins/calculator/package.json new file mode 100644 index 0000000..926e6fa --- /dev/null +++ b/bundled_plugins/calculator/package.json @@ -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" + } +} diff --git a/bundled_plugins/calculator/src/default.tsx b/bundled_plugins/calculator/src/default.tsx new file mode 100644 index 0000000..29b3d58 --- /dev/null +++ b/bundled_plugins/calculator/src/default.tsx @@ -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 ( + + + + Testing inline view left {text} + + + + + + Testing inline view right + + + + ) +} diff --git a/bundled_plugins/calculator/tsconfig.json b/bundled_plugins/calculator/tsconfig.json new file mode 100644 index 0000000..cbe7961 --- /dev/null +++ b/bundled_plugins/calculator/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "types": ["@project-gauntlet/deno"] + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/js/build/src/main.ts b/js/build/src/main.ts index c9b3e9f..b8cd8ad 100644 --- a/js/build/src/main.ts +++ b/js/build/src/main.ts @@ -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 }); } diff --git a/package-lock.json b/package-lock.json index 26b992a..69dc8ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" + } } } } diff --git a/package.json b/package.json index 6babe36..0a515f9 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "build": "npm run build --workspaces --if-present" }, "workspaces": [ + "bundled_plugins/*", "js/typings", "js/build", "js/api_build", diff --git a/rust/client/Cargo.toml b/rust/client/Cargo.toml index 7af68ef..7859431 100644 --- a/rust/client/Cargo.toml +++ b/rust/client/Cargo.toml @@ -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" diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index ddd8752..78f5a28 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -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" diff --git a/rust/component_model/Cargo.toml b/rust/component_model/Cargo.toml index 6025235..e2e675f 100644 --- a/rust/component_model/Cargo.toml +++ b/rust/component_model/Cargo.toml @@ -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"] } diff --git a/rust/management_client/Cargo.toml b/rust/management_client/Cargo.toml index 3815340..11e22ee 100644 --- a/rust/management_client/Cargo.toml +++ b/rust/management_client/Cargo.toml @@ -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"] } diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index c63eb6f..fe4f9c6 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -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 = [] diff --git a/rust/server/db_migrations/6_plugin_type.sql b/rust/server/db_migrations/6_plugin_type.sql new file mode 100644 index 0000000..96bb8b7 --- /dev/null +++ b/rust/server/db_migrations/6_plugin_type.sql @@ -0,0 +1,2 @@ +ALTER TABLE plugin DROP COLUMN from_config; +ALTER TABLE plugin ADD COLUMN type TEXT NOT NULL DEFAULT ('normal'); diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 0bbc16b..1503b56 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -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); } } diff --git a/rust/server/src/plugins/config_reader.rs b/rust/server/src/plugins/config_reader.rs index 987f48a..7a2928f 100644 --- a/rust/server/src/plugins/config_reader.rs +++ b/rust/server/src/plugins/config_reader.rs @@ -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() } diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 8559aa7..ddb6f2a 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -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, #[sqlx(json)] @@ -70,7 +71,7 @@ pub struct DbWritePlugin { pub entrypoints: Vec, pub asset_data: Vec, pub permissions: DbPluginPermissions, - pub from_config: bool, + pub plugin_type: String, pub preferences: HashMap, pub preferences_user_data: HashMap, } @@ -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, @@ -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) } } diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index 0786d77..b277c52 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -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 { + 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::>>() + .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()) diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index cc7bbf2..f3d7ddd 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -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> { let result = self.db_repository .list_plugins_and_entrypoints()