keeweb/app/scripts/plugins/plugin-manager.js

235 lines
7.4 KiB
JavaScript
Raw Normal View History

2019-09-18 21:15:48 +02:00
import { Model } from 'framework/model';
2019-10-26 22:56:36 +02:00
import { RuntimeInfo } from 'const/runtime-info';
2019-09-15 14:16:32 +02:00
import { SettingsStore } from 'comp/settings/settings-store';
2019-09-18 21:15:48 +02:00
import { Plugin, PluginStatus } from 'plugins/plugin';
2019-09-15 14:16:32 +02:00
import { PluginCollection } from 'plugins/plugin-collection';
import { PluginGallery } from 'plugins/plugin-gallery';
import { SignatureVerifier } from 'util/data/signature-verifier';
import { Logger } from 'util/logger';
2019-09-18 07:08:23 +02:00
import { noop } from 'util/fn';
2017-02-18 23:46:59 +01:00
2019-09-18 21:15:48 +02:00
const logger = new Logger('plugin-mgr');
2017-05-20 12:46:21 +02:00
2019-09-18 21:15:48 +02:00
class PluginManager extends Model {
static UpdateInterval = 1000 * 60 * 60 * 24 * 7;
2017-02-18 23:46:59 +01:00
2019-09-18 21:15:48 +02:00
constructor() {
super({
plugins: new PluginCollection()
});
}
2017-02-19 18:51:52 +01:00
init() {
2019-09-18 21:15:48 +02:00
const ts = logger.ts();
2020-06-01 16:53:51 +02:00
return SettingsStore.load('plugins').then((state) => {
2017-05-20 12:46:21 +02:00
if (!state) {
return;
}
this.set({
autoUpdateAppVersion: state.autoUpdateAppVersion,
autoUpdateDate: state.autoUpdateDate
});
2017-02-19 20:09:09 +01:00
if (!state || !state.plugins || !state.plugins.length) {
2017-02-19 18:51:52 +01:00
return;
}
2020-06-01 16:53:51 +02:00
return PluginGallery.getCachedGallery().then((gallery) => {
const promises = state.plugins.map((plugin) => this.loadPlugin(plugin, gallery));
return Promise.all(promises).then((loadedPlugins) => {
this.plugins.push(...loadedPlugins.filter((plugin) => plugin));
2019-09-18 21:15:48 +02:00
logger.info(`Loaded ${this.plugins.length} plugins`, logger.ts(ts));
2017-06-06 20:40:27 +02:00
});
2017-02-19 18:51:52 +01:00
});
});
2019-09-18 21:15:48 +02:00
}
2017-02-19 18:51:52 +01:00
2017-12-02 21:20:19 +01:00
install(url, expectedManifest, skipSignatureValidation) {
2019-09-18 21:15:48 +02:00
this.emit('change');
2019-08-16 23:05:39 +02:00
return Plugin.loadFromUrl(url, expectedManifest)
2020-06-01 16:53:51 +02:00
.then((plugin) => {
2019-08-16 23:05:39 +02:00
return this.uninstall(plugin.id).then(() => {
if (skipSignatureValidation) {
2019-09-18 21:15:48 +02:00
plugin.skipSignatureValidation = true;
2019-08-16 23:05:39 +02:00
}
return plugin.install(true, false).then(() => {
2019-09-18 21:15:48 +02:00
this.plugins.push(plugin);
this.emit('change');
2019-08-16 23:05:39 +02:00
this.saveState();
});
2017-02-19 10:29:18 +01:00
});
2019-08-16 23:05:39 +02:00
})
2020-06-01 16:53:51 +02:00
.catch((e) => {
2019-09-18 21:15:48 +02:00
this.emit('change');
2019-08-16 23:05:39 +02:00
throw e;
2017-04-08 23:35:26 +02:00
});
2019-09-18 21:15:48 +02:00
}
2017-02-19 12:40:21 +01:00
installIfNew(url, expectedManifest, skipSignatureValidation) {
2020-06-01 16:53:51 +02:00
const plugin = this.plugins.find((p) => p.url === url);
2019-09-18 21:15:48 +02:00
if (plugin && plugin.status !== 'invalid') {
return Promise.resolve();
}
return this.install(url, expectedManifest, skipSignatureValidation);
2019-09-18 21:15:48 +02:00
}
2017-05-16 21:26:25 +02:00
2017-02-19 12:40:21 +01:00
uninstall(id) {
2019-09-18 21:15:48 +02:00
const plugin = this.plugins.get(id);
2017-02-19 12:40:21 +01:00
if (!plugin) {
return Promise.resolve();
}
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-02-19 12:40:21 +01:00
return plugin.uninstall().then(() => {
2019-09-18 21:15:48 +02:00
this.plugins.remove(id);
this.emit('change');
2017-04-26 22:06:07 +02:00
this.saveState();
});
2019-09-18 21:15:48 +02:00
}
2017-04-26 22:06:07 +02:00
disable(id) {
2019-09-18 21:15:48 +02:00
const plugin = this.plugins.get(id);
if (!plugin || plugin.status !== PluginStatus.STATUS_ACTIVE) {
2017-04-26 22:06:07 +02:00
return Promise.resolve();
}
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-04-26 22:06:07 +02:00
return plugin.disable().then(() => {
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-04-26 22:06:07 +02:00
this.saveState();
});
2019-09-18 21:15:48 +02:00
}
2017-04-26 22:06:07 +02:00
activate(id) {
2019-09-18 21:15:48 +02:00
const plugin = this.plugins.get(id);
if (!plugin || plugin.status === PluginStatus.STATUS_ACTIVE) {
2017-04-26 22:06:07 +02:00
return Promise.resolve();
}
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-05-14 18:38:35 +02:00
return plugin.install(true, true).then(() => {
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-02-19 19:14:09 +01:00
this.saveState();
2017-02-19 12:40:21 +01:00
});
2019-09-18 21:15:48 +02:00
}
2017-02-19 18:51:52 +01:00
2017-04-08 23:35:26 +02:00
update(id) {
2019-09-18 21:15:48 +02:00
const oldPlugin = this.plugins.get(id);
2019-08-16 23:05:39 +02:00
const validStatuses = [
2019-09-18 21:15:48 +02:00
PluginStatus.STATUS_ACTIVE,
PluginStatus.STATUS_INACTIVE,
PluginStatus.STATUS_NONE,
PluginStatus.STATUS_ERROR,
PluginStatus.STATUS_INVALID
2019-08-16 23:05:39 +02:00
];
2019-09-18 21:15:48 +02:00
if (!oldPlugin || validStatuses.indexOf(oldPlugin.status) < 0) {
2017-04-08 23:35:26 +02:00
return Promise.reject();
}
2019-09-18 21:15:48 +02:00
const url = oldPlugin.url;
this.emit('change');
2019-08-16 23:05:39 +02:00
return Plugin.loadFromUrl(url)
2020-06-01 16:53:51 +02:00
.then((newPlugin) => {
2019-08-16 23:05:39 +02:00
return oldPlugin
.update(newPlugin)
.then(() => {
2019-09-18 21:15:48 +02:00
this.emit('change');
2019-08-16 23:05:39 +02:00
this.saveState();
})
2020-06-01 16:53:51 +02:00
.catch((e) => {
2019-09-18 21:15:48 +02:00
this.emit('change');
2019-08-16 23:05:39 +02:00
throw e;
});
})
2020-06-01 16:53:51 +02:00
.catch((e) => {
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-04-08 23:35:26 +02:00
throw e;
});
2019-09-18 21:15:48 +02:00
}
2017-04-08 23:35:26 +02:00
2017-05-20 11:27:28 +02:00
setAutoUpdate(id, enabled) {
2019-09-18 21:15:48 +02:00
const plugin = this.plugins.get(id);
if (!plugin || plugin.autoUpdate === enabled) {
2017-05-20 11:27:28 +02:00
return;
}
plugin.setAutoUpdate(enabled);
2019-09-18 21:15:48 +02:00
this.emit('change');
2017-05-20 11:27:28 +02:00
this.saveState();
2019-09-18 21:15:48 +02:00
}
2017-05-20 11:27:28 +02:00
2017-05-20 12:46:21 +02:00
runAutoUpdate() {
2020-06-01 16:53:51 +02:00
const queue = this.plugins.filter((p) => p.autoUpdate).map((p) => p.id);
2017-05-20 12:46:21 +02:00
if (!queue.length) {
return Promise.resolve();
}
2019-09-18 21:15:48 +02:00
const anotherVersion = this.autoUpdateAppVersion !== RuntimeInfo.version;
2019-08-18 08:05:38 +02:00
const wasLongAgo =
2019-09-18 21:15:48 +02:00
!this.autoUpdateDate || Date.now() - this.autoUpdateDate > PluginManager.UpdateInterval;
2017-05-20 12:46:21 +02:00
const autoUpdateRequired = anotherVersion || wasLongAgo;
if (!autoUpdateRequired) {
return;
}
2019-09-18 21:15:48 +02:00
logger.info('Auto-updating plugins', queue.join(', '));
2017-05-20 12:46:21 +02:00
this.set({
autoUpdateAppVersion: RuntimeInfo.version,
autoUpdateDate: Date.now()
});
this.saveState();
const updateNext = () => {
const pluginId = queue.shift();
if (pluginId) {
2020-06-01 16:53:51 +02:00
return this.update(pluginId).catch(noop).then(updateNext);
2017-05-20 12:46:21 +02:00
}
};
return updateNext();
2019-09-18 21:15:48 +02:00
}
2017-05-20 12:46:21 +02:00
2017-06-06 20:40:27 +02:00
loadPlugin(desc, gallery) {
2017-04-08 23:35:26 +02:00
const plugin = new Plugin({
manifest: desc.manifest,
2017-05-20 11:27:28 +02:00
url: desc.url,
autoUpdate: desc.autoUpdate
2017-04-08 23:35:26 +02:00
});
2017-06-06 20:40:27 +02:00
let enabled = desc.enabled;
if (enabled) {
2019-08-18 08:05:38 +02:00
const galleryPlugin = gallery
2020-06-01 16:53:51 +02:00
? gallery.plugins.find((pl) => pl.manifest.name === desc.manifest.name)
2019-08-18 08:05:38 +02:00
: null;
2019-09-28 14:40:46 +02:00
const expectedPublicKeys = galleryPlugin
? [galleryPlugin.manifest.publicKey]
: SignatureVerifier.getPublicKeys();
enabled = expectedPublicKeys.includes(desc.manifest.publicKey);
2017-06-06 20:40:27 +02:00
}
2019-08-16 23:05:39 +02:00
return plugin
.install(enabled, true)
2017-02-19 18:51:52 +01:00
.then(() => plugin)
2017-04-08 17:34:27 +02:00
.catch(() => plugin);
2019-09-18 21:15:48 +02:00
}
2017-02-19 18:51:52 +01:00
saveState() {
SettingsStore.save('plugins', {
2019-09-18 21:15:48 +02:00
autoUpdateAppVersion: this.autoUpdateAppVersion,
autoUpdateDate: this.autoUpdateDate,
2020-06-01 16:53:51 +02:00
plugins: this.plugins.map((plugin) => ({
2019-09-18 21:15:48 +02:00
manifest: plugin.manifest,
url: plugin.url,
enabled: plugin.status === 'active',
autoUpdate: plugin.autoUpdate
2017-02-19 18:51:52 +01:00
}))
});
2019-09-18 21:15:48 +02:00
}
2017-04-26 22:06:07 +02:00
getStatus(id) {
2019-09-18 21:15:48 +02:00
const plugin = this.plugins.get(id);
return plugin ? plugin.status : '';
}
2017-05-19 22:05:35 +02:00
getPlugin(id) {
2019-09-18 21:15:48 +02:00
return this.plugins.get(id);
2017-02-18 23:46:59 +01:00
}
2019-09-18 21:15:48 +02:00
}
PluginManager.defineModelProperties({
plugins: null,
autoUpdateAppVersion: null,
autoUpdateDate: null
2017-02-19 10:29:18 +01:00
});
2017-02-18 23:46:59 +01:00
2019-09-16 22:01:59 +02:00
const instance = new PluginManager();
2019-09-15 13:29:20 +02:00
2019-09-16 22:01:59 +02:00
export { instance as PluginManager };