From 88775067c5594c43643ea2b0af444500053502db Mon Sep 17 00:00:00 2001 From: Anders Evenrud Date: Sat, 11 Apr 2020 01:12:40 +0200 Subject: [PATCH] Abstracted package manager utility functions (#28) --- src/packages.js | 44 ++++++++++----------------- src/providers/packages.js | 28 ++++++++++------- src/utils/packages.js | 63 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 40 deletions(-) create mode 100644 src/utils/packages.js diff --git a/src/packages.js b/src/packages.js index bc96875..b3fd507 100644 --- a/src/packages.js +++ b/src/packages.js @@ -32,23 +32,17 @@ const fs = require('fs-extra'); const fg = require('fast-glob'); const path = require('path'); const consola = require('consola'); -const bent = require('bent'); -const tar = require('tar'); const Package = require('./package.js'); const {getPrefix} = require('./utils/vfs.js'); -const logger = consola.withTag('Packages'); - -const relative = filename => filename.replace(process.cwd(), ''); +const { + relative, + archiveName, + fetchSteam, + readOrDefault, + extract +} = require('./utils/packages.js'); -const readOrDefault = filename => fs.existsSync(filename) - ? fs.readJsonSync(filename) - : []; - -const extract = (stream, target) => new Promise((resolve, reject) => { - stream.once('end', () => resolve()); - stream.once('error', error => reject(error)); - stream.pipe(tar.extract({C: target})); -}); +const logger = consola.withTag('Packages'); /** * @typedef InstallPackageOptions @@ -156,30 +150,22 @@ class Packages { * @param {object} user */ async installPackage(url, options, user) { + const {realpath} = this.core.make('osjs/vfs'); + if (!options.root) { throw new Error('Missing package installation root path'); } - const {realpath} = this.core.make('osjs/vfs'); - - const name = path.basename(url.split('?')[0]) - .replace(/\.[^/.]+$/, ''); - - const userRoot = options.root; - const target = await realpath(`${userRoot}/${name}`, user); + const name = archiveName(url); + const target = await realpath(`${options.root}/${name}`, user); if (await fs.exists(target)) { throw new Error('Target already exists'); - } - - if (options.system) { + } else if (options.system) { throw new Error('System packages not yet implemented'); } - const stream = await bent()(url, null, { - headers: options.headers || {} - }); - + const stream = await fetchSteam(url, options); await fs.mkdir(target); await extract(stream, target); @@ -190,7 +176,7 @@ class Packages { throw new Error('Invalid package'); } - await this.writeUserManifest(userRoot, user); + await this.writeUserManifest(options.root, user); return { reload: !options.system diff --git a/src/providers/packages.js b/src/providers/packages.js index 42df85d..e49a8e4 100644 --- a/src/providers/packages.js +++ b/src/providers/packages.js @@ -78,17 +78,23 @@ class PackageServiceProvider extends ServiceProvider { res.status(400).json({error: 'Action failed'}); }); - routeAuthenticated('GET', '/api/packages/metadata', usingPackageManager((req, res) => { - return this.packages.readPackageManifests(req.query.root || [], req.session.user); - })); - - routeAuthenticated('POST', '/api/packages/install', usingPackageManager((req, res) => { - return this.packages.installPackage(req.body.url, req.body.options, req.session.user); - })); - - routeAuthenticated('POST', '/api/packages/uninstall', usingPackageManager((req, res) => { - return this.packages.uninstallPackage(req.body.name, req.body.options, req.session.user); - })); + routeAuthenticated( + 'GET', + '/api/packages/metadata', + usingPackageManager(req => this.packages.readPackageManifests(req.query.root || [], req.session.user)) + ); + + routeAuthenticated( + 'POST', + '/api/packages/install', + usingPackageManager(req => this.packages.installPackage(req.body.url, req.body.options, req.session.user)) + ); + + routeAuthenticated( + 'POST', + '/api/packages/uninstall', + usingPackageManager(req => this.packages.uninstallPackage(req.body.name, req.body.options, req.session.user)) + ); return this.packages.init(); } diff --git a/src/utils/packages.js b/src/utils/packages.js new file mode 100644 index 0000000..0a88355 --- /dev/null +++ b/src/utils/packages.js @@ -0,0 +1,63 @@ +/* + * OS.js - JavaScript Cloud/Web Desktop Platform + * + * Copyright (c) 2011-2020, Anders Evenrud + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Anders Evenrud + * @licence Simplified BSD License + */ + +const bent = require('bent'); +const tar = require('tar'); +const path = require('path'); +const fs = require('fs-extra'); + +const relative = filename => filename + .replace(process.cwd(), ''); + +const archiveName = url => path + .basename(url.split('?')[0]) + .replace(/\.[^/.]+$/, ''); + +const fetchSteam = (url, options) => bent()(url, null, { + headers: options.headers || {} +}); + +const readOrDefault = filename => fs.existsSync(filename) + ? fs.readJsonSync(filename) + : []; + +const extract = (stream, target) => new Promise((resolve, reject) => { + stream.once('end', () => resolve()); + stream.once('error', error => reject(error)); + stream.pipe(tar.extract({C: target})); +}); + +module.exports = { + relative, + archiveName, + fetchSteam, + readOrDefault, + extract +};