packaged plugins

This commit is contained in:
Jan Prochazka
2021-04-15 10:52:02 +02:00
parent 4d5cc119f2
commit 5dd03484ea
9 changed files with 700 additions and 87 deletions

View File

@@ -23,7 +23,7 @@
"build:app": "cd app && yarn install && yarn build",
"build:api": "yarn workspace dbgate-api build",
"build:web:docker": "yarn workspace dbgate-web build",
"build:plugins:frontend": "yarn workspace dbgate-plugin-csv build:frontend && yarn workspace dbgate-plugin-excel build:frontend && yarn workspace dbgate-plugin-mysql build:frontend && yarn workspace dbgate-plugin-mssql build:frontend && yarn workspace dbgate-plugin-mongo build:frontend && yarn workspace dbgate-plugin-postgres build:frontend &&",
"build:plugins:frontend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:frontend",
"build:app:local": "cd app && yarn build:local",
"start:app:local": "cd app && yarn start:local",
"setCurrentVersion": "node setCurrentVersion",
@@ -39,7 +39,7 @@
"ts:api": "yarn workspace dbgate-api ts",
"ts:web": "yarn workspace dbgate-web ts",
"ts": "yarn ts:api && yarn ts:web",
"postinstall": "patch-package && yarn fillNativeModules"
"postinstall": "patch-package && yarn fillNativeModules && yarn build:plugins:frontend"
},
"dependencies": {
"concurrently": "^5.1.0",
@@ -48,6 +48,7 @@
},
"devDependencies": {
"copyfiles": "^2.2.0",
"prettier": "^2.2.1"
"prettier": "^2.2.1",
"workspaces-run": "^1.0.1"
}
}

View File

@@ -33,7 +33,7 @@
"express-basic-auth": "^1.2.0",
"express-fileupload": "^1.2.0",
"find-free-port": "^2.0.0",
"fs-extra": "^8.1.0",
"fs-extra": "^9.1.0",
"http": "^0.0.0",
"json-stable-stringify": "^1.0.1",
"line-reader": "^0.4.0",
@@ -56,6 +56,7 @@
"build": "webpack"
},
"devDependencies": {
"@types/fs-extra": "^9.0.11",
"@types/lodash": "^4.14.149",
"dbgate-types": "^4.0.0",
"env-cmd": "^10.1.0",
@@ -68,4 +69,4 @@
"optionalDependencies": {
"msnodesqlv8": "^2.0.10"
}
}
}

View File

@@ -2,12 +2,13 @@ const fs = require('fs-extra');
const axios = require('axios');
const path = require('path');
const { extractPackageName } = require('dbgate-tools');
const { pluginsdir, datadir } = require('../utility/directories');
const { pluginsdir, datadir, packagedPluginsDir } = require('../utility/directories');
const socket = require('../utility/socket');
const compareVersions = require('compare-versions');
const requirePlugin = require('../shell/requirePlugin');
const downloadPackage = require('../utility/downloadPackage');
const hasPermission = require('../utility/hasPermission');
const _ = require('lodash');
// async function loadPackageInfo(dir) {
// const readmeFile = path.join(dir, 'README.md');
@@ -27,19 +28,22 @@ const hasPermission = require('../utility/hasPermission');
// };
// }
const preinstallPluginMinimalVersions = {
'dbgate-plugin-mssql': '1.2.2',
'dbgate-plugin-mysql': '1.2.2',
'dbgate-plugin-postgres': '1.2.2',
'dbgate-plugin-mongo': '1.0.1',
'dbgate-plugin-csv': '1.0.9',
'dbgate-plugin-excel': '1.0.8',
};
// const preinstallPluginMinimalVersions = {
// 'dbgate-plugin-mssql': '1.2.2',
// 'dbgate-plugin-mysql': '1.2.2',
// 'dbgate-plugin-postgres': '1.2.2',
// 'dbgate-plugin-mongo': '1.0.1',
// 'dbgate-plugin-csv': '1.0.9',
// 'dbgate-plugin-excel': '1.0.8',
// };
module.exports = {
script_meta: 'get',
async script({ packageName }) {
const file = path.join(pluginsdir(), packageName, 'dist', 'frontend.js');
const file1 = path.join(packagedPluginsDir(), packageName, 'dist', 'frontend.js');
const file2 = path.join(pluginsdir(), packageName, 'dist', 'frontend.js');
// @ts-ignore
const file = (await fs.exists(file1)) ? file1 : file2;
const data = await fs.readFile(file, {
encoding: 'utf-8',
});
@@ -63,10 +67,12 @@ module.exports = {
const { latest } = infoResp.data['dist-tags'];
const manifest = infoResp.data.versions[latest];
const { readme } = infoResp.data;
const isPackaged = await fs.pathExists(path.join(packagedPluginsDir(), packageName));
return {
readme,
manifest,
isPackaged,
};
} catch (err) {
return {
@@ -88,14 +94,22 @@ module.exports = {
installed_meta: 'get',
async installed() {
const files = await fs.readdir(pluginsdir());
const files1 = await fs.readdir(packagedPluginsDir());
const files2 = await fs.readdir(pluginsdir());
const res = [];
for (const packageName of files) {
const manifest = await fs.readFile(path.join(pluginsdir(), packageName, 'package.json')).then(x => JSON.parse(x));
for (const packageName of _.union(files1, files2)) {
const isPackaged = files1.includes(packageName);
const manifest = await fs
.readFile(path.join(isPackaged ? packagedPluginsDir() : pluginsdir(), packageName, 'package.json'), {
encoding: 'utf-8',
})
.then(x => JSON.parse(x));
const readmeFile = path.join(pluginsdir(), packageName, 'README.md');
if (await fs.exists(readmeFile)) {
if (await fs.pathExists(readmeFile)) {
manifest.readme = await fs.readFile(readmeFile, { encoding: 'utf-8' });
}
manifest.isPackaged = isPackaged;
res.push(manifest);
}
return res;
@@ -106,20 +120,20 @@ module.exports = {
// );
},
async saveRemovePlugins() {
await fs.writeFile(path.join(datadir(), 'removed-plugins'), this.removedPlugins.join('\n'));
},
// async saveRemovePlugins() {
// await fs.writeFile(path.join(datadir(), 'removed-plugins'), this.removedPlugins.join('\n'));
// },
install_meta: 'post',
async install({ packageName }) {
if (!hasPermission(`plugins/install`)) return;
const dir = path.join(pluginsdir(), packageName);
if (!(await fs.exists(dir))) {
if (!(await fs.pathExists(dir))) {
await downloadPackage(packageName, dir);
}
socket.emitChanged(`installed-plugins-changed`);
this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
await this.saveRemovePlugins();
// this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
// await this.saveRemovePlugins();
},
uninstall_meta: 'post',
@@ -128,7 +142,7 @@ module.exports = {
const dir = path.join(pluginsdir(), packageName);
await fs.rmdir(dir, { recursive: true });
socket.emitChanged(`installed-plugins-changed`);
this.removedPlugins.push(packageName);
// this.removedPlugins.push(packageName);
await this.saveRemovePlugins();
},
@@ -136,7 +150,7 @@ module.exports = {
async upgrade({ packageName }) {
if (!hasPermission(`plugins/install`)) return;
const dir = path.join(pluginsdir(), packageName);
if (await fs.exists(dir)) {
if (await fs.pathExists(dir)) {
await fs.rmdir(dir, { recursive: true });
await downloadPackage(packageName, dir);
}
@@ -158,46 +172,46 @@ module.exports = {
return content.driver.getAuthTypes() || null;
},
async _init() {
const installed = await this.installed();
try {
this.removedPlugins = (await fs.readFile(path.join(datadir(), 'removed-plugins'), { encoding: 'utf-8' })).split(
'\n'
);
} catch (err) {
this.removedPlugins = [];
}
// async _init() {
// const installed = await this.installed();
// try {
// this.removedPlugins = (await fs.readFile(path.join(datadir(), 'removed-plugins'), { encoding: 'utf-8' })).split(
// '\n'
// );
// } catch (err) {
// this.removedPlugins = [];
// }
for (const packageName of Object.keys(preinstallPluginMinimalVersions)) {
const installedVersion = installed.find(x => x.name == packageName);
if (installedVersion) {
// plugin installed, test, whether upgrade
const requiredVersion = preinstallPluginMinimalVersions[packageName];
if (compareVersions(installedVersion.version, requiredVersion) < 0) {
console.log(
`Upgrading preinstalled plugin ${packageName}, found ${installedVersion.version}, required version ${requiredVersion}`
);
await this.upgrade({ packageName });
} else {
console.log(
`Plugin ${packageName} not upgraded, found ${installedVersion.version}, required version ${requiredVersion}`
);
}
// for (const packageName of Object.keys(preinstallPluginMinimalVersions)) {
// const installedVersion = installed.find(x => x.name == packageName);
// if (installedVersion) {
// // plugin installed, test, whether upgrade
// const requiredVersion = preinstallPluginMinimalVersions[packageName];
// if (compareVersions(installedVersion.version, requiredVersion) < 0) {
// console.log(
// `Upgrading preinstalled plugin ${packageName}, found ${installedVersion.version}, required version ${requiredVersion}`
// );
// await this.upgrade({ packageName });
// } else {
// console.log(
// `Plugin ${packageName} not upgraded, found ${installedVersion.version}, required version ${requiredVersion}`
// );
// }
continue;
}
// continue;
// }
if (this.removedPlugins.includes(packageName)) {
// plugin was remvoed, don't install again
continue;
}
// if (this.removedPlugins.includes(packageName)) {
// // plugin was remvoed, don't install again
// continue;
// }
try {
console.log('Preinstalling plugin', packageName);
await this.install({ packageName });
} catch (err) {
console.error('Error preinstalling plugin', packageName, err);
}
}
},
// try {
// console.log('Preinstalling plugin', packageName);
// await this.install({ packageName });
// } catch (err) {
// console.error('Error preinstalling plugin', packageName, err);
// }
// }
// },
};

View File

@@ -1,6 +1,8 @@
const path = require('path');
const { pluginsdir } = require('../utility/directories');
const fs = require('fs');
const { pluginsdir, packagedPluginsDir } = require('../utility/directories');
const nativeModules = require('../nativeModules');
const _isRunOnSource = require('../utility/_isRunOnSource');
const loadedPlugins = {};
@@ -9,13 +11,25 @@ const dbgateEnv = {
nativeModules,
};
function getModulePath(packageName) {
const packagedModulePath = _isRunOnSource()
? path.join(packagedPluginsDir(), packageName, 'src', 'backend', 'index.js')
: path.join(packagedPluginsDir(), packageName, 'dist', 'backend.js');
if (fs.existsSync(packagedModulePath)) {
return packagedModulePath;
}
return path.join(pluginsdir(), packageName, 'dist', 'backend.js');
}
function requirePlugin(packageName, requiredPlugin = null) {
if (!packageName) throw new Error('Missing packageName in plugin');
if (loadedPlugins[packageName]) return loadedPlugins[packageName];
if (requiredPlugin == null) {
let module;
const modulePath = path.join(pluginsdir(), packageName, 'dist', 'backend.js');
const modulePath = getModulePath(packageName);
console.log(`Loading module ${packageName} from ${modulePath}`);
try {
// @ts-ignore

View File

@@ -0,0 +1,5 @@
function _isRunOnSource() {
return __filename.endsWith('_isRunOnSource.js');
}
module.exports = _isRunOnSource;

View File

@@ -2,6 +2,7 @@ const os = require('os');
const path = require('path');
const fs = require('fs');
const cleanDirectory = require('./cleanDirectory');
const _isRunOnSource = require('./_isRunOnSource');
const createDirectories = {};
const ensureDirectory = (dir, clean) => {
@@ -39,6 +40,13 @@ const pluginsdir = dirFunc('plugins');
const archivedir = dirFunc('archive');
const filesdir = dirFunc('files');
function packagedPluginsDir() {
if (_isRunOnSource()) {
return path.resolve(__dirname, '../../../../plugins');
}
return path.resolve(__dirname, '../plugins');
}
module.exports = {
datadir,
jsldir,
@@ -48,4 +56,5 @@ module.exports = {
ensureDirectory,
pluginsdir,
filesdir,
packagedPluginsDir,
};

View File

@@ -23,7 +23,11 @@
<div class="ml-2">
<div class="flex">
<div class="bold">{packageManifest.name}</div>
<div class="ml-1">{packageManifest.version}</div>
{#if packageManifest.isPackaged}
<div class="ml-1 builtin">(builtin)</div>
{:else}
<div class="ml-1">{packageManifest.version}</div>
{/if}
</div>
<div>
{packageManifest.description}
@@ -48,4 +52,8 @@
width: 50px;
height: 50px;
}
.builtin {
color: var(--theme-font-3);
}
</style>

View File

@@ -5,7 +5,7 @@
<script lang="ts">
import compareVersions from 'compare-versions';
import FormStyledButton from '../elements/FormStyledButton.svelte';
import Markdown from '../elements/Markdown.svelte';
import Markdown from '../elements/Markdown.svelte';
import { extractPluginAuthor, extractPluginIcon } from '../plugins/manifestExtractors';
import axiosInstance from '../utility/axiosInstance';
@@ -24,6 +24,7 @@ import Markdown from '../elements/Markdown.svelte';
});
$: readme = $info?.readme;
$: manifest = $info?.manifest;
$: isPackaged = $info?.isPackaged;
const handleInstall = async () => {
axiosInstance.post('plugins/install', { packageName });
@@ -59,17 +60,21 @@ import Markdown from '../elements/Markdown.svelte';
<span> | </span>
<span>{installedFound ? installedFound.version : manifest.version}</span>
</div>
<div class="mt-1">
{#if hasPermission('plugins/install') && !installedFound}
<FormStyledButton type="button" value="Install" on:click={handleInstall} />
{/if}
{#if hasPermission('plugins/install') && installedFound}
<FormStyledButton type="button" value="Uninstall" on:click={handleUninstall} />
{/if}
{#if hasPermission('plugins/install') && installedFound && onlineFound && compareVersions(onlineFound.version, installedFound.version) > 0}
<FormStyledButton type="button" value="Upgrade" on:click={handleUpgrade} />
{/if}
</div>
{#if isPackaged}
<div class="mt-2">Plugin is part of DbGate installation</div>
{:else}
<div class="mt-1">
{#if hasPermission('plugins/install') && !installedFound}
<FormStyledButton type="button" value="Install" on:click={handleInstall} />
{/if}
{#if hasPermission('plugins/install') && installedFound}
<FormStyledButton type="button" value="Uninstall" on:click={handleUninstall} />
{/if}
{#if hasPermission('plugins/install') && installedFound && onlineFound && compareVersions(onlineFound.version, installedFound.version) > 0}
<FormStyledButton type="button" value="Upgrade" on:click={handleUpgrade} />
{/if}
</div>
{/if}
</div>
</div>
<Markdown source={readme} />

570
yarn.lock

File diff suppressed because it is too large Load Diff