UNPKG

hfs

Version:
197 lines (196 loc) 9.81 kB
"use strict"; // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkDependencies = checkDependencies; const plugins_1 = require("./plugins"); const lodash_1 = __importDefault(require("lodash")); const assert_1 = __importDefault(require("assert")); const misc_1 = require("./misc"); const apiMiddleware_1 = require("./apiMiddleware"); const promises_1 = require("fs/promises"); const github_1 = require("./github"); const const_1 = require("./const"); const SendList_1 = require("./SendList"); const apis = { get_plugins({}, ctx) { const list = new SendList_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize, false), ...(0, plugins_1.getAvailablePlugins)().map(serialize)] }); return list.events(ctx, { pluginInstalled: p => list.add(serialize(p)), 'pluginStarted pluginStopped pluginUpdated': p => { const { id, ...rest } = serialize(p); list.update({ id }, rest); }, pluginUninstalled: id => list.remove({ id }), pluginLog: id => list.update({ id }, { log: true }) // SendList is already capping frequency }); }, async get_plugin_updates({}, ctx) { return new SendList_1.SendListReadable({ async doAtStart(list) { const errs = []; list.events(ctx, { pluginDownload({ repo, status }) { var _a; list.update({ id: (_a = (0, plugins_1.findPluginByRepo)(repo)) === null || _a === void 0 ? void 0 : _a.id }, { downloading: status !== null && status !== void 0 ? status : null }); }, pluginDownloaded({ id }) { list.update({ id }, { updated: true }); } }); await Promise.allSettled(lodash_1.default.map((0, github_1.getFolder2repo)(), async (repo, folder) => { try { if (!repo) return; const online = await (0, github_1.readOnlineCompatiblePlugin)(repo); if (!online) return; const disk = (0, plugins_1.getPluginInfo)(folder); if (!disk) return; // plugin removed in the meantime? if (online.version === disk.version) return; // different, not just newer ones, in case a version was retired list.add(Object.assign(online, { id: disk.id, // id is installation-dependant, and online cannot know installedVersion: disk.version, repo: serialize(disk).repo, // show the user the current repo we are getting this update from, not a possibly-changed future one downgrade: online.version < disk.version, downloading: lodash_1.default.isString(online.repo) && github_1.downloading[online.repo], })); } catch (err) { if (err.message !== '404') // the plugin is declaring a wrong repo errs.push(err.code || err.message); } })); for (const x of lodash_1.default.uniq(errs)) list.error(x); list.ready(); } }); }, async start_plugin({ id }) { if ((0, plugins_1.isPluginRunning)(id)) return { msg: 'already running' }; if (plugins_1.suspendPlugins.get()) return new apiMiddleware_1.ApiError(misc_1.HTTP_PRECONDITION_FAILED, 'all plugins suspended'); await (0, plugins_1.stopPlugin)(id); return (0, plugins_1.startPlugin)(id).then(() => ({}), e => new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e.message)); }, async stop_plugin({ id }) { if (!(0, plugins_1.isPluginRunning)(id)) return { msg: 'already stopped' }; await (0, plugins_1.stopPlugin)(id); return {}; }, async set_plugin({ id, enabled, config }) { (0, assert_1.default)(id, 'id'); if (config) (0, plugins_1.setPluginConfig)(id, config); if (enabled !== undefined) (0, plugins_1.enablePlugin)(id, enabled); return {}; }, async get_plugin({ id }) { return { enabled: plugins_1.enablePlugins.get().includes(id), config: { ...(0, misc_1.newObj)((0, plugins_1.getPluginConfigFields)(id), v => v === null || v === void 0 ? void 0 : v.defaultValue), ...plugins_1.pluginsConfig.get()[id] } }; }, get_online_plugins({ text }, ctx) { return new SendList_1.SendListReadable({ async doAtStart(list) { const repos = []; list.events(ctx, { pluginInstalled: p => { if (repos.includes(p.repo)) list.update({ id: p.repo }, { installed: true }); }, pluginUninstalled: folder => { const repo = (0, github_1.getFolder2repo)()[folder]; if (typeof repo !== 'string') return; // custom repo if (repos.includes(repo)) list.update({ id: repo }, { installed: false }); }, pluginDownload({ repo, status }) { if (repos.includes(repo)) list.update({ id: repo }, { downloading: status !== null && status !== void 0 ? status : null }); } }); try { const already = Object.values((0, github_1.getFolder2repo)()).map(String); for await (const pl of await (0, github_1.searchPlugins)(text, { skipRepos: already })) { const repo = pl.repo || pl.id; // .repo property can be more trustworthy in case github user renamed and left the previous link in 'repo' const missing = await (0, plugins_1.getMissingDependencies)(pl); if (missing.length) pl.missing = missing; list.add(pl); repos.push(repo); } } catch (err) { list.error(err.code || err.message); } list.ready(); } }); }, async download_plugin({ id, branch, stop }) { await checkDependencies(await (0, github_1.readOnlinePlugin)(id, branch)); const folder = await (0, github_1.downloadPlugin)(id, { branch }); if (stop) // be sure this is not automatically started await (0, plugins_1.stopPlugin)(folder); return (await (0, misc_1.waitFor)(() => (0, plugins_1.getPluginInfo)(folder), { timeout: 5000 })) || new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR); }, async update_plugin({ id, branch }) { const found = (0, plugins_1.getPluginInfo)(id); if (!found) return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND); const online = await (0, github_1.readOnlineCompatiblePlugin)(found.repo); // branch returned by readOnlineCompatiblePlugin is possibly fresher, so we use that if (!online) return new apiMiddleware_1.ApiError(misc_1.HTTP_CONFLICT); await checkDependencies(online); await (0, github_1.downloadPlugin)(found.repo, { branch: online.branch, overwrite: true }); return {}; }, async uninstall_plugin({ id, deleteConfig }) { await (0, plugins_1.stopPlugin)(id); await (0, promises_1.rm)(plugins_1.PATH + '/' + id, { recursive: true, force: true }); if (deleteConfig) (0, plugins_1.setPluginConfig)(id, null); return {}; }, get_plugin_log({ id }, ctx) { const p = (0, plugins_1.getPluginInfo)(id); if (!p) return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND); const list = new SendList_1.SendListReadable({ addAtStart: p.log }); return list.events(ctx, { ['pluginLog:' + id]: x => list.add(x) }); }, }; exports.default = apis; function serialize(p) { var _a; let o = 'getData' in p ? Object.assign(lodash_1.default.pick(p, ['id', 'started']), p.getData()) : { ...p }; // _.defaults mutates object, and we don't want that if (typeof o.repo === 'object') // custom repo o.repo = o.repo.web; o.log = 'log' in p && ((_a = p.log) === null || _a === void 0 ? void 0 : _a.length) > 0; o.config && (o.config = lodash_1.default.isFunction(o.config) ? String(o.config) : JSON.stringify(o.config, (_k, v) => lodash_1.default.isFunction(v) ? String(v) : v)); // allow simple functions return lodash_1.default.defaults(o, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values, } async function checkDependencies(plugin) { const miss = await (0, plugins_1.getMissingDependencies)(plugin); if (miss.length) throw new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, miss); }