From de449c6d5669dc0dc3856605e75b81d33e9fafe8 Mon Sep 17 00:00:00 2001 From: Brian Seymour Date: Fri, 7 May 2021 14:22:25 -0500 Subject: [PATCH 01/10] validate json instead of a json header, and lots of cleanup --- .github/workflows/package-pr.yaml | 6 +- api/Dockerfile | 3 +- api/src/api/v2.js | 290 ++++++++++++----------- api/src/config.js | 26 +- api/src/globals.js | 12 +- api/src/index.js | 30 ++- api/src/job.js | 57 ++--- api/src/package.js | 16 +- api/src/runtime.js | 12 +- cli/commands/execute.js | 6 +- cli/commands/ppman.js | 8 +- cli/commands/ppman_commands/install.js | 24 +- cli/commands/ppman_commands/list.js | 23 +- cli/commands/ppman_commands/uninstall.js | 24 +- cli/index.js | 4 +- 15 files changed, 272 insertions(+), 269 deletions(-) diff --git a/.github/workflows/package-pr.yaml b/.github/workflows/package-pr.yaml index 5c935c5..4e82395 100644 --- a/.github/workflows/package-pr.yaml +++ b/.github/workflows/package-pr.yaml @@ -71,7 +71,7 @@ jobs: docker run -v $(pwd)'/repo:/piston/repo' -v $(pwd)'/packages:/piston/packages' -d --name repo docker.pkg.github.com/engineer-man/piston/repo-builder --no-build docker run --network container:repo -v $(pwd)'/data:/piston' -e PISTON_LOG_LEVEL=DEBUG -e 'PISTON_REPO_URL=http://localhost:8000/index' -d --name api docker.pkg.github.com/engineer-man/piston/api echo Waiting for API to start.. - docker run --network container:api appropriate/curl -s --retry 10 --retry-connrefused http://localhost:2000/api/v1/runtimes + docker run --network container:api appropriate/curl -s --retry 10 --retry-connrefused http://localhost:2000/api/v2/runtimes echo Waiting for Index to start.. docker run --network container:repo appropriate/curl -s --retry 999 --retry-max-time 0 --retry-connrefused http://localhost:8000/index @@ -80,7 +80,7 @@ jobs: sed -i 's/repo/localhost/g' repo/index echo Listing Packages - PACKAGES_JSON=$(docker run --network container:api appropriate/curl -s http://localhost:2000/api/v1/packages) + PACKAGES_JSON=$(docker run --network container:api appropriate/curl -s http://localhost:2000/api/v2/packages) echo $PACKAGES_JSON echo Getting CLI ready @@ -94,7 +94,7 @@ jobs: PKG_VERSION=$(awk -F- '{ print $2 }' <<< $package) echo "Installing..." - docker run --network container:api appropriate/curl -sXPOST http://localhost:2000/api/v1/packages/$PKG_PATH + docker run --network container:api appropriate/curl -sXPOST http://localhost:2000/api/v2/packages/$PKG_PATH TEST_SCRIPTS=packages/$PKG_PATH/test.* echo "Tests: $TEST_SCRIPTS" diff --git a/api/Dockerfile b/api/Dockerfile index cc2edf8..668c54a 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -20,11 +20,10 @@ RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen WORKDIR /piston_api COPY ["package.json", "package-lock.json", "./"] -RUN npm install +RUN npm install COPY ./src ./src RUN make -C ./src/nosocket/ all && make -C ./src/nosocket/ install CMD [ "node", "src"] EXPOSE 2000/tcp - diff --git a/api/src/api/v2.js b/api/src/api/v2.js index ae6e54a..bbd5e68 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -3,166 +3,182 @@ const router = express.Router(); const config = require('../config'); const runtime = require('../runtime'); -const {Job} = require("../job"); +const { Job } = require('../job'); const package = require('../package') const logger = require('logplease').create('api/v1'); -router.use(function(req, res, next){ - if(req.method == "POST" && !req.headers['content-type'].startsWith("application/json")) +router.use((req, res, next) => { + if (!req.body) { + return next(); + } + + try { + JSON.parse(req.body); + } catch (e) { return res .status(415) .send({ - message: "requests must be of type application/json" - }) + message: 'requests must be of type application/json' + }); + } + next(); -}) - -router.post('/execute', async function(req, res){ - const {language, version, files, stdin, args, run_timeout, compile_timeout, compile_memory_limit, run_memory_limit} = req.body; - - if(!language || typeof language !== "string") - { - return res - .status(400) - .send({ - message: "language is required as a string" - }); - } - - if(!version || typeof version !== "string") - { - return res - .status(400) - .send({ - message: "version is required as a string" - }); - } - - if(!files || !Array.isArray(files)) - { - return res - .status(400) - .send({ - message: "files is required as an array" - }); - } - - for (const [i,file] of files.entries()) { - if(typeof file.content !== "string"){ - return res - .status(400) - .send({ - message: `files[${i}].content is required as a string` - }); - } - } - - if (compile_memory_limit) { - if (typeof compile_memory_limit !== "number") { - return res - .status(400) - .send({ - message: "if specified, compile_memory_limit must be a number" - }) - } else if (config.compile_memory_limit >= 0 && (compile_memory_limit > config.compile_memory_limit || compile_memory_limit < 0)) { - return res - .status(400) - .send({ - message: "compile_memory_limit cannot exceed the configured limit of " + config.compile_memory_limit - }) - } - } - - if (run_memory_limit) { - if (typeof run_memory_limit !== "number") { - return res - .status(400) - .send({ - message: "if specified, run_memory_limit must be a number" - }) - } else if (config.run_memory_limit >= 0 && (run_memory_limit > config.run_memory_limit || run_memory_limit < 0)) { - return res - .status(400) - .send({ - message: "run_memory_limit cannot exceed the configured limit of " + config.run_memory_limit - }) - } - } - - - - const rt = runtime.get_latest_runtime_matching_language_version(language, version); - - if (rt === undefined) { - return res - .status(400) - .send({ - message: `${language}-${version} runtime is unknown` - }); - } - - const job = new Job({ - runtime: rt, - alias: language, - files: files, - args: args || [], - stdin: stdin || "", - timeouts: { - run: run_timeout || 3000, - compile: compile_timeout || 10000 - }, - memory_limits: { - run: run_memory_limit || config.run_memory_limit, - compile: compile_memory_limit || config.compile_memory_limit - } - }); - - await job.prime(); - - const result = await job.execute(); - - await job.cleanup(); - - return res - .status(200) - .send(result); }); -router.get('/runtimes', function(req, res){ - const runtimes = runtime.map(rt => ({ - language: rt.language, - version: rt.version.raw, - aliases: rt.aliases, - runtime: rt.runtime - })); +router.post('/execute', async (req, res) => { + const { + language, version, + files, + stdin, args, + run_timeout, compile_timeout, + compile_memory_limit, run_memory_limit + } = req.body; + + if (!language || typeof language !== 'string') { + return res + .status(400) + .send({ + message: 'language is required as a string' + }); + } + + if (!version || typeof version !== 'string') { + return res + .status(400) + .send({ + message: 'version is required as a string' + }); + } + + if (!files || !Array.isArray(files)) { + return res + .status(400) + .send({ + message: 'files is required as an array' + }); + } + + for (const [i, file] of files.entries()) { + if (typeof file.content !== 'string') { + return res + .status(400) + .send({ + message: `files[${i}].content is required as a string` + }); + } + } + + if (compile_memory_limit) { + if (typeof compile_memory_limit !== 'number') { + return res + .status(400) + .send({ + message: 'if specified, compile_memory_limit must be a number' + }) + } + + if (config.compile_memory_limit >= 0 && (compile_memory_limit > config.compile_memory_limit || compile_memory_limit < 0)) { + return res + .status(400) + .send({ + message: 'compile_memory_limit cannot exceed the configured limit of ' + config.compile_memory_limit + }) + } + } + + if (run_memory_limit) { + if (typeof run_memory_limit !== 'number') { + return res + .status(400) + .send({ + message: 'if specified, run_memory_limit must be a number' + }) + } + + if (config.run_memory_limit >= 0 && (run_memory_limit > config.run_memory_limit || run_memory_limit < 0)) { + return res + .status(400) + .send({ + message: 'run_memory_limit cannot exceed the configured limit of ' + config.run_memory_limit + }) + } + } + + const rt = runtime.get_latest_runtime_matching_language_version(language, version); + + if (rt === undefined) { + return res + .status(400) + .send({ + message: `${language}-${version} runtime is unknown` + }); + } + + const job = new Job({ + runtime: rt, + alias: language, + files: files, + args: args || [], + stdin: stdin || '', + timeouts: { + run: run_timeout || 3000, + compile: compile_timeout || 10000 + }, + memory_limits: { + run: run_memory_limit || config.run_memory_limit, + compile: compile_memory_limit || config.compile_memory_limit + } + }); + + await job.prime(); + + const result = await job.execute(); + + await job.cleanup(); + + return res + .status(200) + .send(result); +}); + +router.get('/runtimes', (req, res) => { + const runtimes = runtime + .map(rt => { + return { + language: rt.language, + version: rt.version.raw, + aliases: rt.aliases, + runtime: rt.runtime + }; + }); return res .status(200) .send(runtimes); }); -router.get('/packages', async function(req, res){ +router.get('/packages', async (req, res) => { logger.debug('Request to list packages'); let packages = await package.get_package_list(); packages = packages .map(pkg => { - return { - language: pkg.language, - language_version: pkg.version.raw, - installed: pkg.installed - }; - }); + return { + language: pkg.language, + language_version: pkg.version.raw, + installed: pkg.installed + }; + }); return res .status(200) .send(packages); }); -router.post('/packages/:language/:version', async function(req, res){ +router.post('/packages/:language/:version', async (req, res) => { logger.debug('Request to install package'); - const {language, version} = req.params; + const { language, version } = req.params; const pkg = await package.get_package(language, version); @@ -180,7 +196,7 @@ router.post('/packages/:language/:version', async function(req, res){ return res .status(200) .send(response); - } catch(e) { + } catch (e) { logger.error(`Error while installing package ${pkg.language}-${pkg.version}:`, e.message); return res @@ -191,7 +207,7 @@ router.post('/packages/:language/:version', async function(req, res){ } }); -router.delete('/packages/:language/:version', async function(req, res){ +router.delete('/packages/:language/:version', async (req, res) => { logger.debug('Request to uninstall package'); const {language, version} = req.params; @@ -212,7 +228,7 @@ router.delete('/packages/:language/:version', async function(req, res){ return res .status(200) .send(response); - } catch(e) { + } catch (e) { logger.error(`Error while uninstalling package ${pkg.language}-${pkg.version}:`, e.message); return res @@ -223,8 +239,4 @@ router.delete('/packages/:language/:version', async function(req, res){ } }); - - - - module.exports = router; diff --git a/api/src/config.js b/api/src/config.js index 8cc1122..1be8453 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -2,7 +2,6 @@ const fss = require('fs'); const Logger = require('logplease'); const logger = Logger.create('config'); - const options = [ { key: 'log_level', @@ -31,7 +30,7 @@ const options = [ default: 1001, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -40,7 +39,7 @@ const options = [ default: 1500, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -49,7 +48,7 @@ const options = [ default: 1001, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -58,7 +57,7 @@ const options = [ default: 1500, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -76,7 +75,7 @@ const options = [ default: 1024, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -85,7 +84,7 @@ const options = [ default: 64, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -94,7 +93,7 @@ const options = [ default: 2048, parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -103,7 +102,7 @@ const options = [ default: 1000000, //1MB parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -112,7 +111,7 @@ const options = [ default: -1, // no limit parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -121,7 +120,7 @@ const options = [ default: -1, // no limit parser: parse_int, validators: [ - (x,raw) => !isNaN(x) || `${raw} is not a number`, + (x, raw) => !is_nan(x) || `${raw} is not a number`, ] }, { @@ -141,16 +140,14 @@ let config = {}; options.forEach(option => { const env_key = "PISTON_" + option.key.to_upper_case(); - const parser = option.parser || (x=>x); + const parser = option.parser || (x => x); const env_val = process.env[env_key]; const parsed_val = parser(env_val); - const value = env_val || option.default; - option.validators.for_each(validator => { let response = null; if(env_val) @@ -174,5 +171,4 @@ if (errored) { logger.info('Configuration successfully loaded'); - module.exports = config; diff --git a/api/src/globals.js b/api/src/globals.js index e632a88..596ae9b 100644 --- a/api/src/globals.js +++ b/api/src/globals.js @@ -1,8 +1,8 @@ // Globals are things the user shouldn't change in config, but is good to not use inline constants for const is_docker = require('is-docker'); -const fss = require('fs'); +const fs = require('fs'); const platform = `${is_docker() ? 'docker' : 'baremetal'}-${ - fss.read_file_sync('/etc/os-release') + fs.read_file_sync('/etc/os-release') .toString() .split('\n') .find(x => x.startsWith('ID')) @@ -18,9 +18,9 @@ module.exports = { platform, pkg_installed_file: '.ppman-installed', //Used as indication for if a package was installed clean_directories: [ - "/dev/shm", - "/run/lock", - "/tmp", - "/var/tmp" + '/dev/shm', + '/run/lock', + '/tmp', + '/var/tmp' ] }; diff --git a/api/src/index.js b/api/src/index.js index ac4d6f7..4e68458 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -40,16 +40,20 @@ const app = express(); const pkglist = await fs.readdir(pkgdir); const languages = await Promise.all( - pkglist.map(lang=> - fs.readdir(path.join(pkgdir,lang)) - .then(x=>x.map(y=>path.join(pkgdir, lang, y))) - )); + pkglist.map(lang => { + return fs + .readdir(path.join(pkgdir,lang)) + .then(x => { + return x.map(y => path.join(pkgdir, lang, y)) + }); + }) + ); const installed_languages = languages .flat() .filter(pkg => fss.exists_sync(path.join(pkg, globals.pkg_installed_file))); - installed_languages.forEach(pkg => runtime.load_package(pkg)); + installed_languages.for_each(pkg => runtime.load_package(pkg)); logger.info('Starting API Server'); logger.debug('Constructing Express App'); @@ -58,22 +62,24 @@ const app = express(); app.use(body_parser.urlencoded({ extended: true })); app.use(body_parser.json()); - app.use(function (err, req, res, next) { + app.use((err, req, res, next) => { return res .status(400) .send({ stack: err.stack - }) - }) + }); + }); logger.debug('Registering Routes'); const api_v2 = require('./api/v2') - app.use('/api/v1', api_v2); - app.use('/api/v2', api_v2); + app.use('/api/v2', api_v2); + app.use('/api/v2', api_v2); - app.use(function (req,res,next){ - return res.status(404).send({message: 'Not Found'}); + app.use((req, res, next) => { + return res + .status(404) + .send({message: 'Not Found'}); }); logger.debug('Calling app.listen'); diff --git a/api/src/job.js b/api/src/job.js index 9484974..2f8a2c1 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -1,5 +1,5 @@ const logger = require('logplease').create('job'); -const {v4: uuidv4} = require('uuid'); +const { v4: uuidv4 } = require('uuid'); const cp = require('child_process'); const path = require('path'); const config = require('./config'); @@ -25,7 +25,7 @@ class Job { name: file.name || `file${i}.code`, content: file.content })); - + this.args = args; this.stdin = stdin; this.timeouts = timeouts; @@ -40,7 +40,6 @@ class Job { uid %= (config.runner_uid_max - config.runner_uid_min) + 1; gid %= (config.runner_gid_max - config.runner_gid_min) + 1; - this.state = job_states.READY; this.dir = path.join(config.data_directory, globals.data_directories.jobs, this.uuid); } @@ -94,7 +93,7 @@ class Job { var output = ''; const proc = cp.spawn(proc_call[0], proc_call.splice(1) ,{ - env: { + env: { ...this.runtime.env_vars, PISTON_LANGUAGE: this.runtime.language }, @@ -189,12 +188,12 @@ class Job { }; } - - async cleanup_processes(){ + async cleanup_processes() { let processes = [1]; - while(processes.length > 0){ - processes = await new Promise((resolve, reject) => cp.execFile('ps', ['awwxo', 'pid,ruid'], function(err, stdout) { - if(err === null){ + + while (processes.length > 0) { + processes = await new Promise((resolve, reject) => cp.execFile('ps', ['awwxo', 'pid,ruid'], (err, stdout) => { + if (err === null) { const lines = stdout.split('\n').slice(1); //Remove header with slice const procs = lines.map(line => { const [pid, ruid] = line @@ -203,55 +202,57 @@ class Job { .map(n => parseInt(n)); return { pid, ruid } - }) - resolve(procs) - } - else{ - reject(error) + }); + + resolve(procs); + } else { + reject(error); } })); - processes = processes.filter(proc => proc.ruid == this.uid); + processes = processes.filter(proc => proc.ruid === this.uid); - for(const proc of processes){ + for (const proc of processes) { // First stop the processes, but keep their resources allocated so they cant re-fork - try{ + try { process.kill(proc.pid, 'SIGSTOP'); - }catch{ + } catch { // Could already be dead } } - for(const proc of processes){ + for (const proc of processes) { // Then clear them out of the process tree - try{ + try { process.kill(proc.pid, 'SIGKILL'); - }catch{ + } catch { // Could already be dead and just needs to be waited on } + wait_pid(proc.pid); } } } - async cleanup_filesystem(){ - + async cleanup_filesystem() { for (const clean_path of globals.clean_directories) { const contents = await fs.readdir(clean_path); for (const file of contents) { const file_path = path.join(clean_path, file); - try{ + + try { const stat = await fs.stat(file_path); - if(stat.uid == this.uid) + + if (stat.uid === this.uid) { await fs.rm(file_path, { recursive: true, force: true }); - }catch(e){ + } + } catch (e) { // File was somehow deleted in the time that we read the dir to when we checked the file logger.warn(`Error removing file ${file_path}: ${e}`) } } - } await fs.rm(this.dir, { recursive: true, force: true }); @@ -259,7 +260,7 @@ class Job { async cleanup() { logger.info(`Cleaning up job uuid=${this.uuid}`); - + await Promise.all([ this.cleanup_processes(), this.cleanup_filesystem() diff --git a/api/src/package.js b/api/src/package.js index 991c72c..5b98634 100644 --- a/api/src/package.js +++ b/api/src/package.js @@ -165,14 +165,14 @@ class Package { static async get_package_list() { const repo_content = await fetch(config.repo_url).then(x => x.text()); - + const entries = repo_content .split('\n') .filter(x => x.length > 0); - + return entries.map(line => { const [ language, version, checksum, download ] = line.split(',', 4); - + return new Package({ language, version, @@ -181,17 +181,17 @@ class Package { }); }); } - - static async get_package (lang, version) { + + static async get_package(lang, version) { const packages = await Package.get_package_list(); - + const candidates = packages .filter(pkg => { return pkg.language == lang && semver.satisfies(pkg.version, version) }); - + candidates.sort((a, b) => semver.rcompare(a.version, b.version)); - + return candidates[0] || null; } diff --git a/api/src/runtime.js b/api/src/runtime.js index 43713ee..6ae824e 100644 --- a/api/src/runtime.js +++ b/api/src/runtime.js @@ -9,7 +9,7 @@ const runtimes = []; class Runtime { - constructor({language, version, aliases, pkgdir, runtime}){ + constructor({language, version, aliases, pkgdir, runtime}) { this.language = language; this.version = version; this.aliases = aliases || []; @@ -17,7 +17,7 @@ class Runtime { this.runtime = runtime; } - static load_package(package_dir){ + static load_package(package_dir) { let info = JSON.parse( fss.read_file_sync(path.join(package_dir, 'pkg-info.json')) ); @@ -32,7 +32,7 @@ class Runtime { ); } - if(provides){ + if (provides) { // Multiple languages in 1 package provides.forEach(lang => { runtimes.push(new Runtime({ @@ -43,7 +43,7 @@ class Runtime { runtime: language })); }); - }else{ + } else { runtimes.push(new Runtime({ language, version, @@ -53,8 +53,6 @@ class Runtime { } logger.debug(`Package ${language}-${version} was loaded`); - - } get compiled() { @@ -108,4 +106,4 @@ module.exports.get_runtime_by_name_and_version = function(runtime, ver){ return runtimes.find(rt => (rt.runtime == runtime || (rt.runtime === undefined && rt.language == runtime)) && semver.satisfies(rt.version, ver)); } -module.exports.load_package = Runtime.load_package; \ No newline at end of file +module.exports.load_package = Runtime.load_package; diff --git a/cli/commands/execute.js b/cli/commands/execute.js index 4ddb221..f511350 100644 --- a/cli/commands/execute.js +++ b/cli/commands/execute.js @@ -8,7 +8,7 @@ exports.aliases = ['run']; exports.describe = 'Executes file with the specified runner'; exports.builder = { - languageVersion: { + version: { string: true, desc: 'Set the version of the language to use', alias: ['l'], @@ -38,7 +38,7 @@ exports.builder = { } }; -exports.handler = async function(argv) { +exports.handler = async (argv) => { const files = [...(argv.files || []),argv.file] .map(file_path => { return { @@ -63,7 +63,7 @@ exports.handler = async function(argv) { run_timeout: argv.rt }; - let { data: response } = await argv.axios.post('/api/v1/execute', request); + let { data: response } = await argv.axios.post('/api/v2/execute', request); const step = (name, ctx) => { console.log(chalk.bold(`== ${name} ==`)); diff --git a/cli/commands/ppman.js b/cli/commands/ppman.js index f41c1dd..8d1cb34 100644 --- a/cli/commands/ppman.js +++ b/cli/commands/ppman.js @@ -1,7 +1,7 @@ -exports.command = 'ppman' -exports.aliases = ['pkg'] -exports.describe = 'Package Manager' +exports.command = 'ppman'; +exports.aliases = ['pkg']; +exports.describe = 'Package Manager'; exports.builder = yargs => yargs .commandDir('ppman_commands') - .demandCommand() \ No newline at end of file + .demandCommand(); diff --git a/cli/commands/ppman_commands/install.js b/cli/commands/ppman_commands/install.js index fedf5b8..3a71410 100644 --- a/cli/commands/ppman_commands/install.js +++ b/cli/commands/ppman_commands/install.js @@ -1,23 +1,21 @@ const chalk = require('chalk'); -exports.command = ['install [language-version]'] -exports.aliases = ['i'] -exports.describe = 'Installs the named package' - +exports.command = ['install [language-version]']; +exports.aliases = ['i']; +exports.describe = 'Installs the named package'; const msg_format = { - 'color': p => `${p.language ? chalk.green.bold('✓') : chalk.red.bold('❌')} Installation ${p.language ? "succeeded" : "failed: " + p.message}`, - 'monochrome': p => `Installation ${p.language ? "succeeded" : "failed: " + p.message}`, - 'json': JSON.stringify + color: p => `${p.language ? chalk.green.bold('✓') : chalk.red.bold('❌')} Installation ${p.language ? 'succeeded' : 'failed: ' + p.message}`, + monochrome: p => `Installation ${p.language ? 'succeeded' : 'failed: ' + p.message}`, + json: JSON.stringify +}; -} - -exports.handler = async function({axios, language, languageVersion}){ - try{ - const install = await axios.post(`/api/v1/packages/${language}/${languageVersion || '*'}`) +exports.handler = async ({ axios, language, version }) => { + try { + const install = await axios.post(`/api/v2/packages/${language}/${version || '*'}`); console.log(msg_format.color(install.data)); - }catch({response}){ + } catch ({ response }) { console.error(response.data.message) } } diff --git a/cli/commands/ppman_commands/list.js b/cli/commands/ppman_commands/list.js index fa3cb27..a45030c 100644 --- a/cli/commands/ppman_commands/list.js +++ b/cli/commands/ppman_commands/list.js @@ -1,22 +1,17 @@ -//const fetch = require('node-fetch'); const chalk = require('chalk'); -exports.command = ['list'] -exports.aliases = ['l'] -exports.describe = 'Lists all available packages' - +exports.command = ['list']; +exports.aliases = ['l']; +exports.describe = 'Lists all available packages'; const msg_format = { - 'color': p => `${chalk[p.installed ? "green":"red"]("•")} ${p.language} ${p.language_version}`, - 'monochrome': p => `${p.language} ${p.language_version} ${p.installed ? "(INSTALLED)": ""}`, - 'json': JSON.stringify - -} - -exports.handler = async function({axios}){ - - const packages = await axios.get('/api/v1/packages'); + color: p => `${chalk[p.installed ? 'green':'red']('•')} ${p.language} ${p.language_version}`, + monochrome: p => `${p.language} ${p.language_version} ${p.installed ? '(INSTALLED)': ''}`, + json: JSON.stringify +}; +exports.handler = async ({ axios }) => { + const packages = await axios.get('/api/v2/packages'); const pkg_msg = packages.data .map(msg_format.color) diff --git a/cli/commands/ppman_commands/uninstall.js b/cli/commands/ppman_commands/uninstall.js index 0e4ebf2..54a8165 100644 --- a/cli/commands/ppman_commands/uninstall.js +++ b/cli/commands/ppman_commands/uninstall.js @@ -1,23 +1,21 @@ const chalk = require('chalk'); -exports.command = ['uninstall [language-version]'] -exports.aliases = ['u'] -exports.describe = 'Uninstalls the named package' - +exports.command = ['uninstall [language-version]']; +exports.aliases = ['u']; +exports.describe = 'Uninstalls the named package'; const msg_format = { - 'color': p => `${p.language ? chalk.green.bold('✓') : chalk.red.bold('❌')} Uninstallation ${p.language ? "succeeded" : "failed: " + p.message}`, - 'monochrome': p => `Uninstallation ${p.language ? "succeeded" : "failed: " + p.message}`, - 'json': JSON.stringify + color: p => `${p.language ? chalk.green.bold('✓') : chalk.red.bold('❌')} Uninstallation ${p.language ? 'succeeded' : 'failed: ' + p.message}`, + monochrome: p => `Uninstallation ${p.language ? 'succeeded' : 'failed: ' + p.message}`, + json: JSON.stringify +}; -} - -exports.handler = async function({axios, language, languageVersion}){ - try{ - const uninstall = await axios.delete(`/api/v1/packages/${language}/${languageVersion || '*'}`) +exports.handler = async ({ axios, language, version }) => { + try { + const uninstall = await axios.delete(`/api/v2/packages/${language}/${version || '*'}`) console.log(msg_format.color(uninstall.data)); - }catch({response}){ + } catch ({ response }) { console.error(response.data.message) } } diff --git a/cli/index.js b/cli/index.js index 415dbf3..cee3d0d 100755 --- a/cli/index.js +++ b/cli/index.js @@ -2,7 +2,7 @@ const axios = require('axios').default; -const axios_instance = function(argv){ +const axios_instance = argv => { argv.axios = axios.create({ baseURL: argv['piston-url'] }); @@ -18,7 +18,7 @@ require('yargs')(process.argv.slice(2)) string: true }) .middleware(axios_instance) - .scriptName("piston") + .scriptName('piston') .commandDir('commands') .demandCommand() .help() From 5830d7fb43c4bb82b7340018293cb5289a574cad Mon Sep 17 00:00:00 2001 From: Brian Seymour Date: Fri, 7 May 2021 14:35:27 -0500 Subject: [PATCH 02/10] fix json validation --- api/src/api/v2.js | 4 ++-- cli/index.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/api/src/api/v2.js b/api/src/api/v2.js index bbd5e68..c7e1697 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -5,10 +5,10 @@ const config = require('../config'); const runtime = require('../runtime'); const { Job } = require('../job'); const package = require('../package') -const logger = require('logplease').create('api/v1'); +const logger = require('logplease').create('api/v2'); router.use((req, res, next) => { - if (!req.body) { + if (!req.body || req.body === '') { return next(); } diff --git a/cli/index.js b/cli/index.js index cee3d0d..d25ec7d 100755 --- a/cli/index.js +++ b/cli/index.js @@ -4,7 +4,10 @@ const axios = require('axios').default; const axios_instance = argv => { argv.axios = axios.create({ - baseURL: argv['piston-url'] + baseURL: argv['piston-url'], + headers: { + 'Content-Type': 'application/json' + } }); return argv; From 3ea6ca1180a99ad008706c8382da82472ba7d9bf Mon Sep 17 00:00:00 2001 From: Brian Seymour Date: Fri, 7 May 2021 14:42:55 -0500 Subject: [PATCH 03/10] fix json validation --- api/src/api/v2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/api/v2.js b/api/src/api/v2.js index c7e1697..478b107 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -8,7 +8,7 @@ const package = require('../package') const logger = require('logplease').create('api/v2'); router.use((req, res, next) => { - if (!req.body || req.body === '') { + if (['GET', 'HEAD', 'OPTIONS'].includes(req.method) || !req.body) { return next(); } From 106d5c72c96ea12347a32bd7a0fa772e383a8d49 Mon Sep 17 00:00:00 2001 From: Brian Seymour Date: Fri, 7 May 2021 14:50:50 -0500 Subject: [PATCH 04/10] fix json validation --- api/src/api/v2.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/api/src/api/v2.js b/api/src/api/v2.js index 478b107..639ce99 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -8,13 +8,11 @@ const package = require('../package') const logger = require('logplease').create('api/v2'); router.use((req, res, next) => { - if (['GET', 'HEAD', 'OPTIONS'].includes(req.method) || !req.body) { + if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } - try { - JSON.parse(req.body); - } catch (e) { + if (req.headers['content-type'] !== 'application/json') { return res .status(415) .send({ From b51350489f6a23716cca0f286d8c1405d208ed03 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 8 May 2021 12:08:13 +1200 Subject: [PATCH 05/10] Up the default max file size limit --- api/src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/config.js b/api/src/config.js index 8cc1122..ea31300 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -100,7 +100,7 @@ const options = [ { key: 'max_file_size', desc: 'Max file size in bytes for a file', - default: 1000000, //1MB + default: 10000000, //10MB parser: parse_int, validators: [ (x,raw) => !isNaN(x) || `${raw} is not a number`, From 4259e89bb29db6823dab3fe09ca97d2093027a99 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 8 May 2021 12:20:21 +1200 Subject: [PATCH 06/10] lint api --- api/.prettierignore | 1 + api/.prettierrc.yaml | 1 + api/package-lock.json | 2009 +++++++++++++++++++++-------------------- api/package.json | 40 +- api/src/api/v2.js | 366 ++++---- api/src/config.js | 272 +++--- api/src/globals.js | 34 +- api/src/index.js | 111 ++- api/src/job.js | 433 ++++----- api/src/package.js | 293 +++--- api/src/runtime.js | 166 ++-- 11 files changed, 1869 insertions(+), 1857 deletions(-) create mode 100644 api/.prettierignore create mode 100644 api/.prettierrc.yaml diff --git a/api/.prettierignore b/api/.prettierignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/api/.prettierignore @@ -0,0 +1 @@ +node_modules diff --git a/api/.prettierrc.yaml b/api/.prettierrc.yaml new file mode 100644 index 0000000..0176969 --- /dev/null +++ b/api/.prettierrc.yaml @@ -0,0 +1 @@ +singleQuote: true diff --git a/api/package-lock.json b/api/package-lock.json index c066efc..bdcbb29 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,998 +1,1019 @@ { - "name": "piston-api", - "version": "3.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "piston-api", - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "body-parser": "^1.19.0", - "chownr": "^2.0.0", - "express": "^4.17.1", - "is-docker": "^2.1.1", - "logplease": "^1.2.15", - "nocamel": "HexF/nocamel#patch-1", - "node-fetch": "^2.6.1", - "semver": "^7.3.4", - "uuid": "^8.3.2", - "waitpid": "git+https://github.com/HexF/node-waitpid.git" - } - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logplease": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", - "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "dependencies": { - "mime-db": "1.46.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nocamel": { - "resolved": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544" - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/waitpid": { - "resolved": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } + "name": "piston-api", + "version": "3.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "piston-api", + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "body-parser": "^1.19.0", + "chownr": "^2.0.0", + "express": "^4.17.1", + "is-docker": "^2.1.1", + "logplease": "^1.2.15", + "nocamel": "HexF/nocamel#patch-1", + "node-fetch": "^2.6.1", + "semver": "^7.3.4", + "uuid": "^8.3.2", + "waitpid": "git+https://github.com/HexF/node-waitpid.git" + }, + "devDependencies": { + "prettier": "2.2.1" + } }, - "dependencies": { - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" - }, - "logplease": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", - "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" - }, - "mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "requires": { - "mime-db": "1.46.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "nocamel": { - "version": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544", - "from": "nocamel@HexF/nocamel#patch-1" - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "waitpid": { - "version": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa", - "from": "waitpid@git+https://github.com/HexF/node-waitpid.git" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logplease": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", + "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "dependencies": { + "mime-db": "1.46.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nocamel": { + "resolved": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544" + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/waitpid": { + "resolved": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } + }, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" + }, + "logplease": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", + "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "requires": { + "mime-db": "1.46.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "nocamel": { + "version": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544", + "from": "nocamel@HexF/nocamel#patch-1" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "waitpid": { + "version": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa", + "from": "waitpid@git+https://github.com/HexF/node-waitpid.git" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } } diff --git a/api/package.json b/api/package.json index cefd900..58ff057 100644 --- a/api/package.json +++ b/api/package.json @@ -1,19 +1,25 @@ { - "name": "piston-api", - "version": "3.0.0", - "description": "API for piston - a high performance code execution engine", - "main": "src/index.js", - "dependencies": { - "body-parser": "^1.19.0", - "chownr": "^2.0.0", - "express": "^4.17.1", - "is-docker": "^2.1.1", - "logplease": "^1.2.15", - "nocamel": "HexF/nocamel#patch-1", - "node-fetch": "^2.6.1", - "semver": "^7.3.4", - "uuid": "^8.3.2", - "waitpid": "git+https://github.com/HexF/node-waitpid.git" - }, - "license": "MIT" + "name": "piston-api", + "version": "3.0.0", + "description": "API for piston - a high performance code execution engine", + "main": "src/index.js", + "dependencies": { + "body-parser": "^1.19.0", + "chownr": "^2.0.0", + "express": "^4.17.1", + "is-docker": "^2.1.1", + "logplease": "^1.2.15", + "nocamel": "HexF/nocamel#patch-1", + "node-fetch": "^2.6.1", + "semver": "^7.3.4", + "uuid": "^8.3.2", + "waitpid": "git+https://github.com/HexF/node-waitpid.git" + }, + "license": "MIT", + "scripts": { + "lint": "prettier . --write" + }, + "devDependencies": { + "prettier": "2.2.1" + } } diff --git a/api/src/api/v2.js b/api/src/api/v2.js index 639ce99..ceb06fd 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -1,240 +1,224 @@ const express = require('express'); -const router = express.Router(); +const router = express.Router(); const config = require('../config'); const runtime = require('../runtime'); const { Job } = require('../job'); -const package = require('../package') +const package = require('../package'); const logger = require('logplease').create('api/v2'); router.use((req, res, next) => { - if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { - return next(); - } + if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { + return next(); + } - if (req.headers['content-type'] !== 'application/json') { - return res - .status(415) - .send({ - message: 'requests must be of type application/json' - }); - } + if (req.headers['content-type'] !== 'application/json') { + return res.status(415).send({ + message: 'requests must be of type application/json', + }); + } - next(); + next(); }); router.post('/execute', async (req, res) => { - const { - language, version, - files, - stdin, args, - run_timeout, compile_timeout, - compile_memory_limit, run_memory_limit - } = req.body; + const { + language, + version, + files, + stdin, + args, + run_timeout, + compile_timeout, + compile_memory_limit, + run_memory_limit, + } = req.body; - if (!language || typeof language !== 'string') { - return res - .status(400) - .send({ - message: 'language is required as a string' - }); - } - - if (!version || typeof version !== 'string') { - return res - .status(400) - .send({ - message: 'version is required as a string' - }); - } - - if (!files || !Array.isArray(files)) { - return res - .status(400) - .send({ - message: 'files is required as an array' - }); - } - - for (const [i, file] of files.entries()) { - if (typeof file.content !== 'string') { - return res - .status(400) - .send({ - message: `files[${i}].content is required as a string` - }); - } - } - - if (compile_memory_limit) { - if (typeof compile_memory_limit !== 'number') { - return res - .status(400) - .send({ - message: 'if specified, compile_memory_limit must be a number' - }) - } - - if (config.compile_memory_limit >= 0 && (compile_memory_limit > config.compile_memory_limit || compile_memory_limit < 0)) { - return res - .status(400) - .send({ - message: 'compile_memory_limit cannot exceed the configured limit of ' + config.compile_memory_limit - }) - } - } - - if (run_memory_limit) { - if (typeof run_memory_limit !== 'number') { - return res - .status(400) - .send({ - message: 'if specified, run_memory_limit must be a number' - }) - } - - if (config.run_memory_limit >= 0 && (run_memory_limit > config.run_memory_limit || run_memory_limit < 0)) { - return res - .status(400) - .send({ - message: 'run_memory_limit cannot exceed the configured limit of ' + config.run_memory_limit - }) - } - } - - const rt = runtime.get_latest_runtime_matching_language_version(language, version); - - if (rt === undefined) { - return res - .status(400) - .send({ - message: `${language}-${version} runtime is unknown` - }); - } - - const job = new Job({ - runtime: rt, - alias: language, - files: files, - args: args || [], - stdin: stdin || '', - timeouts: { - run: run_timeout || 3000, - compile: compile_timeout || 10000 - }, - memory_limits: { - run: run_memory_limit || config.run_memory_limit, - compile: compile_memory_limit || config.compile_memory_limit - } + if (!language || typeof language !== 'string') { + return res.status(400).send({ + message: 'language is required as a string', }); + } - await job.prime(); + if (!version || typeof version !== 'string') { + return res.status(400).send({ + message: 'version is required as a string', + }); + } - const result = await job.execute(); + if (!files || !Array.isArray(files)) { + return res.status(400).send({ + message: 'files is required as an array', + }); + } - await job.cleanup(); + for (const [i, file] of files.entries()) { + if (typeof file.content !== 'string') { + return res.status(400).send({ + message: `files[${i}].content is required as a string`, + }); + } + } - return res - .status(200) - .send(result); + if (compile_memory_limit) { + if (typeof compile_memory_limit !== 'number') { + return res.status(400).send({ + message: 'if specified, compile_memory_limit must be a number', + }); + } + + if ( + config.compile_memory_limit >= 0 && + (compile_memory_limit > config.compile_memory_limit || + compile_memory_limit < 0) + ) { + return res.status(400).send({ + message: + 'compile_memory_limit cannot exceed the configured limit of ' + + config.compile_memory_limit, + }); + } + } + + if (run_memory_limit) { + if (typeof run_memory_limit !== 'number') { + return res.status(400).send({ + message: 'if specified, run_memory_limit must be a number', + }); + } + + if ( + config.run_memory_limit >= 0 && + (run_memory_limit > config.run_memory_limit || run_memory_limit < 0) + ) { + return res.status(400).send({ + message: + 'run_memory_limit cannot exceed the configured limit of ' + + config.run_memory_limit, + }); + } + } + + const rt = runtime.get_latest_runtime_matching_language_version( + language, + version + ); + + if (rt === undefined) { + return res.status(400).send({ + message: `${language}-${version} runtime is unknown`, + }); + } + + const job = new Job({ + runtime: rt, + alias: language, + files: files, + args: args || [], + stdin: stdin || '', + timeouts: { + run: run_timeout || 3000, + compile: compile_timeout || 10000, + }, + memory_limits: { + run: run_memory_limit || config.run_memory_limit, + compile: compile_memory_limit || config.compile_memory_limit, + }, + }); + + await job.prime(); + + const result = await job.execute(); + + await job.cleanup(); + + return res.status(200).send(result); }); router.get('/runtimes', (req, res) => { - const runtimes = runtime - .map(rt => { - return { - language: rt.language, - version: rt.version.raw, - aliases: rt.aliases, - runtime: rt.runtime - }; - }); + const runtimes = runtime.map((rt) => { + return { + language: rt.language, + version: rt.version.raw, + aliases: rt.aliases, + runtime: rt.runtime, + }; + }); - return res - .status(200) - .send(runtimes); + return res.status(200).send(runtimes); }); router.get('/packages', async (req, res) => { - logger.debug('Request to list packages'); - let packages = await package.get_package_list(); + logger.debug('Request to list packages'); + let packages = await package.get_package_list(); - packages = packages - .map(pkg => { - return { - language: pkg.language, - language_version: pkg.version.raw, - installed: pkg.installed - }; - }); + packages = packages.map((pkg) => { + return { + language: pkg.language, + language_version: pkg.version.raw, + installed: pkg.installed, + }; + }); - return res - .status(200) - .send(packages); + return res.status(200).send(packages); }); router.post('/packages/:language/:version', async (req, res) => { - logger.debug('Request to install package'); + logger.debug('Request to install package'); - const { language, version } = req.params; + const { language, version } = req.params; - const pkg = await package.get_package(language, version); + const pkg = await package.get_package(language, version); - if (pkg == null) { - return res - .status(404) - .send({ - message: `Requested package ${language}-${version} does not exist` - }); - } + if (pkg == null) { + return res.status(404).send({ + message: `Requested package ${language}-${version} does not exist`, + }); + } - try { - const response = await pkg.install(); + try { + const response = await pkg.install(); - return res - .status(200) - .send(response); - } catch (e) { - logger.error(`Error while installing package ${pkg.language}-${pkg.version}:`, e.message); + return res.status(200).send(response); + } catch (e) { + logger.error( + `Error while installing package ${pkg.language}-${pkg.version}:`, + e.message + ); - return res - .status(500) - .send({ - message: e.message - }); - } + return res.status(500).send({ + message: e.message, + }); + } }); router.delete('/packages/:language/:version', async (req, res) => { - logger.debug('Request to uninstall package'); + logger.debug('Request to uninstall package'); - const {language, version} = req.params; + const { language, version } = req.params; - const pkg = await package.get_package(language, version); + const pkg = await package.get_package(language, version); - if (pkg == null) { - return res - .status(404) - .send({ - message: `Requested package ${language}-${version} does not exist` - }); - } + if (pkg == null) { + return res.status(404).send({ + message: `Requested package ${language}-${version} does not exist`, + }); + } - try { - const response = await pkg.uninstall(); + try { + const response = await pkg.uninstall(); - return res - .status(200) - .send(response); - } catch (e) { - logger.error(`Error while uninstalling package ${pkg.language}-${pkg.version}:`, e.message); + return res.status(200).send(response); + } catch (e) { + logger.error( + `Error while uninstalling package ${pkg.language}-${pkg.version}:`, + e.message + ); - return res - .status(500) - .send({ - message: e.message - }); - } + return res.status(500).send({ + message: e.message, + }); + } }); module.exports = router; diff --git a/api/src/config.js b/api/src/config.js index 89d06db..a17617f 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -3,132 +3,114 @@ const Logger = require('logplease'); const logger = Logger.create('config'); const options = [ - { - key: 'log_level', - desc: 'Level of data to log', - default: 'INFO', - options: Object.values(Logger.LogLevels), - validators: [ - x => Object.values(Logger.LogLevels).includes(x) || `Log level ${x} does not exist` - ] - }, - { - key: 'bind_address', - desc: 'Address to bind REST API on\nThank @Bones for the number', - default: '0.0.0.0:2000', - validators: [] - }, - { - key: 'data_directory', - desc: 'Absolute path to store all piston related data at', - default: '/piston', - validators: [x=> fss.exists_sync(x) || `Directory ${x} does not exist`] - }, - { - key: 'runner_uid_min', - desc: 'Minimum uid to use for runner', - default: 1001, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'runner_uid_max', - desc: 'Maximum uid to use for runner', - default: 1500, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'runner_gid_min', - desc: 'Minimum gid to use for runner', - default: 1001, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'runner_gid_max', - desc: 'Maximum gid to use for runner', - default: 1500, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'disable_networking', - desc: 'Set to true to disable networking', - default: true, - parser: x => x === "true", - validators: [ - x => typeof x === "boolean" || `${x} is not a boolean` - ] - }, - { - key: 'output_max_size', - desc: 'Max size of each stdio buffer', - default: 1024, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'max_process_count', - desc: 'Max number of processes per job', - default: 64, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'max_open_files', - desc: 'Max number of open files per job', - default: 2048, - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'max_file_size', - desc: 'Max file size in bytes for a file', - default: 10000000, //10MB - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'compile_memory_limit', - desc: 'Max memory usage for compile stage in bytes (set to -1 for no limit)', - default: -1, // no limit - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'run_memory_limit', - desc: 'Max memory usage for run stage in bytes (set to -1 for no limit)', - default: -1, // no limit - parser: parse_int, - validators: [ - (x, raw) => !is_nan(x) || `${raw} is not a number`, - ] - }, - { - key: 'repo_url', - desc: 'URL of repo index', - default: 'https://github.com/engineer-man/piston/releases/download/pkgs/index', - validators: [] - } + { + key: 'log_level', + desc: 'Level of data to log', + default: 'INFO', + options: Object.values(Logger.LogLevels), + validators: [ + (x) => + Object.values(Logger.LogLevels).includes(x) || + `Log level ${x} does not exist`, + ], + }, + { + key: 'bind_address', + desc: 'Address to bind REST API on\nThank @Bones for the number', + default: '0.0.0.0:2000', + validators: [], + }, + { + key: 'data_directory', + desc: 'Absolute path to store all piston related data at', + default: '/piston', + validators: [(x) => fss.exists_sync(x) || `Directory ${x} does not exist`], + }, + { + key: 'runner_uid_min', + desc: 'Minimum uid to use for runner', + default: 1001, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'runner_uid_max', + desc: 'Maximum uid to use for runner', + default: 1500, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'runner_gid_min', + desc: 'Minimum gid to use for runner', + default: 1001, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'runner_gid_max', + desc: 'Maximum gid to use for runner', + default: 1500, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'disable_networking', + desc: 'Set to true to disable networking', + default: true, + parser: (x) => x === 'true', + validators: [(x) => typeof x === 'boolean' || `${x} is not a boolean`], + }, + { + key: 'output_max_size', + desc: 'Max size of each stdio buffer', + default: 1024, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'max_process_count', + desc: 'Max number of processes per job', + default: 64, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'max_open_files', + desc: 'Max number of open files per job', + default: 2048, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'max_file_size', + desc: 'Max file size in bytes for a file', + default: 10000000, //10MB + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'compile_memory_limit', + desc: + 'Max memory usage for compile stage in bytes (set to -1 for no limit)', + default: -1, // no limit + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'run_memory_limit', + desc: 'Max memory usage for run stage in bytes (set to -1 for no limit)', + default: -1, // no limit + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'repo_url', + desc: 'URL of repo index', + default: + 'https://github.com/engineer-man/piston/releases/download/pkgs/index', + validators: [], + }, ]; logger.info(`Loading Configuration from environment`); @@ -137,36 +119,34 @@ let errored = false; let config = {}; -options.forEach(option => { - const env_key = "PISTON_" + option.key.to_upper_case(); +options.forEach((option) => { + const env_key = 'PISTON_' + option.key.to_upper_case(); - const parser = option.parser || (x => x); + const parser = option.parser || ((x) => x); - const env_val = process.env[env_key]; + const env_val = process.env[env_key]; - const parsed_val = parser(env_val); + const parsed_val = parser(env_val); - const value = env_val || option.default; + const value = env_val || option.default; - option.validators.for_each(validator => { - let response = null; - if(env_val) - response = validator(parsed_val, env_val); - else - response = validator(value, value); + option.validators.for_each((validator) => { + let response = null; + if (env_val) response = validator(parsed_val, env_val); + else response = validator(value, value); - if (response !== true) { - errored = true; - logger.error(`Config option ${option.key} failed validation:`, response); - return; - } - }); + if (response !== true) { + errored = true; + logger.error(`Config option ${option.key} failed validation:`, response); + return; + } + }); - config[option.key] = value; + config[option.key] = value; }); if (errored) { - process.exit(1); + process.exit(1); } logger.info('Configuration successfully loaded'); diff --git a/api/src/globals.js b/api/src/globals.js index 596ae9b..4546f74 100644 --- a/api/src/globals.js +++ b/api/src/globals.js @@ -1,26 +1,20 @@ // Globals are things the user shouldn't change in config, but is good to not use inline constants for const is_docker = require('is-docker'); const fs = require('fs'); -const platform = `${is_docker() ? 'docker' : 'baremetal'}-${ - fs.read_file_sync('/etc/os-release') - .toString() - .split('\n') - .find(x => x.startsWith('ID')) - .replace('ID=','') -}`; +const platform = `${is_docker() ? 'docker' : 'baremetal'}-${fs + .read_file_sync('/etc/os-release') + .toString() + .split('\n') + .find((x) => x.startsWith('ID')) + .replace('ID=', '')}`; module.exports = { - data_directories: { - packages: 'packages', - jobs: 'jobs' - }, - version: require('../package.json').version, - platform, - pkg_installed_file: '.ppman-installed', //Used as indication for if a package was installed - clean_directories: [ - '/dev/shm', - '/run/lock', - '/tmp', - '/var/tmp' - ] + data_directories: { + packages: 'packages', + jobs: 'jobs', + }, + version: require('../package.json').version, + platform, + pkg_installed_file: '.ppman-installed', //Used as indication for if a package was installed + clean_directories: ['/dev/shm', '/run/lock', '/tmp', '/var/tmp'], }; diff --git a/api/src/index.js b/api/src/index.js index 4e68458..75fda40 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -14,78 +14,77 @@ const logger = Logger.create('index'); const app = express(); (async () => { - logger.info('Setting loglevel to',config.log_level); - Logger.setLogLevel(config.log_level); - logger.debug('Ensuring data directories exist'); + logger.info('Setting loglevel to', config.log_level); + Logger.setLogLevel(config.log_level); + logger.debug('Ensuring data directories exist'); - Object.values(globals.data_directories).for_each(dir => { - let data_path = path.join(config.data_directory, dir); + Object.values(globals.data_directories).for_each((dir) => { + let data_path = path.join(config.data_directory, dir); - logger.debug(`Ensuring ${data_path} exists`); + logger.debug(`Ensuring ${data_path} exists`); - if (!fss.exists_sync(data_path)) { - logger.info(`${data_path} does not exist.. Creating..`); + if (!fss.exists_sync(data_path)) { + logger.info(`${data_path} does not exist.. Creating..`); - try { - fss.mkdir_sync(data_path); - } catch(e) { - logger.error(`Failed to create ${data_path}: `, e.message); - } - } - }); + try { + fss.mkdir_sync(data_path); + } catch (e) { + logger.error(`Failed to create ${data_path}: `, e.message); + } + } + }); - logger.info('Loading packages'); - const pkgdir = path.join(config.data_directory,globals.data_directories.packages); + logger.info('Loading packages'); + const pkgdir = path.join( + config.data_directory, + globals.data_directories.packages + ); - const pkglist = await fs.readdir(pkgdir); + const pkglist = await fs.readdir(pkgdir); - const languages = await Promise.all( - pkglist.map(lang => { - return fs - .readdir(path.join(pkgdir,lang)) - .then(x => { - return x.map(y => path.join(pkgdir, lang, y)) - }); - }) + const languages = await Promise.all( + pkglist.map((lang) => { + return fs.readdir(path.join(pkgdir, lang)).then((x) => { + return x.map((y) => path.join(pkgdir, lang, y)); + }); + }) + ); + + const installed_languages = languages + .flat() + .filter((pkg) => + fss.exists_sync(path.join(pkg, globals.pkg_installed_file)) ); - const installed_languages = languages - .flat() - .filter(pkg => fss.exists_sync(path.join(pkg, globals.pkg_installed_file))); + installed_languages.for_each((pkg) => runtime.load_package(pkg)); - installed_languages.for_each(pkg => runtime.load_package(pkg)); + logger.info('Starting API Server'); + logger.debug('Constructing Express App'); + logger.debug('Registering middleware'); - logger.info('Starting API Server'); - logger.debug('Constructing Express App'); - logger.debug('Registering middleware'); + app.use(body_parser.urlencoded({ extended: true })); + app.use(body_parser.json()); - app.use(body_parser.urlencoded({ extended: true })); - app.use(body_parser.json()); - - app.use((err, req, res, next) => { - return res - .status(400) - .send({ - stack: err.stack - }); + app.use((err, req, res, next) => { + return res.status(400).send({ + stack: err.stack, }); + }); - logger.debug('Registering Routes'); + logger.debug('Registering Routes'); - const api_v2 = require('./api/v2') - app.use('/api/v2', api_v2); - app.use('/api/v2', api_v2); + const api_v2 = require('./api/v2'); + app.use('/api/v2', api_v2); + app.use('/api/v2', api_v2); - app.use((req, res, next) => { - return res - .status(404) - .send({message: 'Not Found'}); - }); + app.use((req, res, next) => { + return res.status(404).send({ message: 'Not Found' }); + }); - logger.debug('Calling app.listen'); - const [ address, port ] = config.bind_address.split(':'); + logger.debug('Calling app.listen'); + const [address, port] = config.bind_address.split(':'); - app.listen(port, address, () => { - logger.info('API server started on', config.bind_address); - }); + app.listen(port, address, () => { + logger.info('API server started on', config.bind_address); + }); })(); diff --git a/api/src/job.js b/api/src/job.js index 2f8a2c1..f301a5e 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -8,267 +8,268 @@ const fs = require('fs/promises'); const wait_pid = require('waitpid'); const job_states = { - READY: Symbol('Ready to be primed'), - PRIMED: Symbol('Primed and ready for execution'), - EXECUTED: Symbol('Executed and ready for cleanup') + READY: Symbol('Ready to be primed'), + PRIMED: Symbol('Primed and ready for execution'), + EXECUTED: Symbol('Executed and ready for cleanup'), }; let uid = 0; let gid = 0; class Job { + constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { + this.uuid = uuidv4(); + this.runtime = runtime; + this.files = files.map((file, i) => ({ + name: file.name || `file${i}.code`, + content: file.content, + })); - constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { - this.uuid = uuidv4(); - this.runtime = runtime; - this.files = files.map((file,i) => ({ - name: file.name || `file${i}.code`, - content: file.content - })); + this.args = args; + this.stdin = stdin; + this.timeouts = timeouts; + this.memory_limits = memory_limits; - this.args = args; - this.stdin = stdin; - this.timeouts = timeouts; - this.memory_limits = memory_limits; + this.uid = config.runner_uid_min + uid; + this.gid = config.runner_gid_min + gid; - this.uid = config.runner_uid_min + uid; - this.gid = config.runner_gid_min + gid; + uid++; + gid++; - uid++; - gid++; + uid %= config.runner_uid_max - config.runner_uid_min + 1; + gid %= config.runner_gid_max - config.runner_gid_min + 1; - uid %= (config.runner_uid_max - config.runner_uid_min) + 1; - gid %= (config.runner_gid_max - config.runner_gid_min) + 1; + this.state = job_states.READY; + this.dir = path.join( + config.data_directory, + globals.data_directories.jobs, + this.uuid + ); + } - this.state = job_states.READY; - this.dir = path.join(config.data_directory, globals.data_directories.jobs, this.uuid); + async prime() { + logger.info(`Priming job uuid=${this.uuid}`); + + logger.debug('Writing files to job cache'); + + logger.debug(`Transfering ownership uid=${this.uid} gid=${this.gid}`); + + await fs.mkdir(this.dir, { mode: 0o700 }); + await fs.chown(this.dir, this.uid, this.gid); + + for (const file of this.files) { + let file_path = path.join(this.dir, file.name); + + await fs.write_file(file_path, file.content); + await fs.chown(file_path, this.uid, this.gid); } - async prime() { - logger.info(`Priming job uuid=${this.uuid}`); + this.state = job_states.PRIMED; - logger.debug('Writing files to job cache'); + logger.debug('Primed job'); + } - logger.debug(`Transfering ownership uid=${this.uid} gid=${this.gid}`); + async safe_call(file, args, timeout, memory_limit) { + return new Promise((resolve, reject) => { + const nonetwork = config.disable_networking ? ['nosocket'] : []; - await fs.mkdir(this.dir, { mode:0o700 }); - await fs.chown(this.dir, this.uid, this.gid); + const prlimit = [ + 'prlimit', + '--nproc=' + config.max_process_count, + '--nofile=' + config.max_open_files, + '--fsize=' + config.max_file_size, + ]; - for (const file of this.files) { - let file_path = path.join(this.dir, file.name); + if (memory_limit >= 0) { + prlimit.push('--as=' + memory_limit); + } - await fs.write_file(file_path, file.content); - await fs.chown(file_path, this.uid, this.gid); + const proc_call = [...prlimit, ...nonetwork, 'bash', file, ...args]; + + var stdout = ''; + var stderr = ''; + var output = ''; + + const proc = cp.spawn(proc_call[0], proc_call.splice(1), { + env: { + ...this.runtime.env_vars, + PISTON_LANGUAGE: this.runtime.language, + }, + stdio: 'pipe', + cwd: this.dir, + uid: this.uid, + gid: this.gid, + detached: true, //give this process its own process group + }); + + proc.stdin.write(this.stdin); + proc.stdin.end(); + proc.stdin.destroy(); + + const kill_timeout = set_timeout((_) => proc.kill('SIGKILL'), timeout); + + proc.stderr.on('data', (data) => { + if (stderr.length > config.output_max_size) { + proc.kill('SIGKILL'); + } else { + stderr += data; + output += data; } + }); - this.state = job_states.PRIMED; + proc.stdout.on('data', (data) => { + if (stdout.length > config.output_max_size) { + proc.kill('SIGKILL'); + } else { + stdout += data; + output += data; + } + }); - logger.debug('Primed job'); + const exit_cleanup = () => { + clear_timeout(kill_timeout); + + proc.stderr.destroy(); + proc.stdout.destroy(); + }; + + proc.on('exit', (code, signal) => { + exit_cleanup(); + + resolve({ stdout, stderr, code, signal, output }); + }); + + proc.on('error', (err) => { + exit_cleanup(); + + reject({ error: err, stdout, stderr, output }); + }); + }); + } + + async execute() { + if (this.state !== job_states.PRIMED) { + throw new Error( + 'Job must be in primed state, current state: ' + this.state.toString() + ); } - async safe_call(file, args, timeout, memory_limit) { - return new Promise((resolve, reject) => { - const nonetwork = config.disable_networking ? ['nosocket'] : []; + logger.info( + `Executing job uuid=${this.uuid} uid=${this.uid} gid=${ + this.gid + } runtime=${this.runtime.toString()}` + ); - const prlimit = [ - 'prlimit', - '--nproc=' + config.max_process_count, - '--nofile=' + config.max_open_files, - '--fsize=' + config.max_file_size - ]; + logger.debug('Compiling'); - if (memory_limit >= 0) { - prlimit.push('--as=' + memory_limit); - } + let compile; - const proc_call = [ - ...prlimit, - ...nonetwork, - 'bash',file, - ...args - ]; + if (this.runtime.compiled) { + compile = await this.safe_call( + path.join(this.runtime.pkgdir, 'compile'), + this.files.map((x) => x.name), + this.timeouts.compile, + this.memory_limits.compile + ); + } - var stdout = ''; - var stderr = ''; - var output = ''; + logger.debug('Running'); - const proc = cp.spawn(proc_call[0], proc_call.splice(1) ,{ - env: { - ...this.runtime.env_vars, - PISTON_LANGUAGE: this.runtime.language - }, - stdio: 'pipe', - cwd: this.dir, - uid: this.uid, - gid: this.gid, - detached: true //give this process its own process group + const run = await this.safe_call( + path.join(this.runtime.pkgdir, 'run'), + [this.files[0].name, ...this.args], + this.timeouts.run, + this.memory_limits.run + ); + + this.state = job_states.EXECUTED; + + return { + compile, + run, + language: this.runtime.language, + version: this.runtime.version.raw, + }; + } + + async cleanup_processes() { + let processes = [1]; + + while (processes.length > 0) { + processes = await new Promise((resolve, reject) => + cp.execFile('ps', ['awwxo', 'pid,ruid'], (err, stdout) => { + if (err === null) { + const lines = stdout.split('\n').slice(1); //Remove header with slice + const procs = lines.map((line) => { + const [pid, ruid] = line + .trim() + .split(/\s+/) + .map((n) => parseInt(n)); + + return { pid, ruid }; }); - proc.stdin.write(this.stdin); - proc.stdin.end(); - proc.stdin.destroy(); + resolve(procs); + } else { + reject(error); + } + }) + ); - const kill_timeout = set_timeout(_ => proc.kill('SIGKILL'), timeout); + processes = processes.filter((proc) => proc.ruid === this.uid); - proc.stderr.on('data', data => { - if (stderr.length > config.output_max_size) { - proc.kill('SIGKILL'); - } else { - stderr += data; - output += data; - } - }); + for (const proc of processes) { + // First stop the processes, but keep their resources allocated so they cant re-fork + try { + process.kill(proc.pid, 'SIGSTOP'); + } catch { + // Could already be dead + } + } - proc.stdout.on('data', data => { - if (stdout.length > config.output_max_size) { - proc.kill('SIGKILL'); - } else { - stdout += data; - output += data; - } - }); - - const exit_cleanup = () => { - clear_timeout(kill_timeout); - - proc.stderr.destroy(); - proc.stdout.destroy(); - }; - - proc.on('exit', (code, signal)=>{ - exit_cleanup(); - - resolve({ stdout, stderr, code, signal, output }); - }); - - proc.on('error', (err) => { - exit_cleanup(); - - reject({ error: err, stdout, stderr, output }); - }); - }); - } - - async execute() { - if (this.state !== job_states.PRIMED) { - throw new Error('Job must be in primed state, current state: ' + this.state.toString()); + for (const proc of processes) { + // Then clear them out of the process tree + try { + process.kill(proc.pid, 'SIGKILL'); + } catch { + // Could already be dead and just needs to be waited on } - logger.info(`Executing job uuid=${this.uuid} uid=${this.uid} gid=${this.gid} runtime=${this.runtime.toString()}`); + wait_pid(proc.pid); + } + } + } - logger.debug('Compiling'); + async cleanup_filesystem() { + for (const clean_path of globals.clean_directories) { + const contents = await fs.readdir(clean_path); - let compile; + for (const file of contents) { + const file_path = path.join(clean_path, file); - if (this.runtime.compiled) { - compile = await this.safe_call( - path.join(this.runtime.pkgdir, 'compile'), - this.files.map(x => x.name), - this.timeouts.compile, - this.memory_limits.compile - ); + try { + const stat = await fs.stat(file_path); + + if (stat.uid === this.uid) { + await fs.rm(file_path, { recursive: true, force: true }); + } + } catch (e) { + // File was somehow deleted in the time that we read the dir to when we checked the file + logger.warn(`Error removing file ${file_path}: ${e}`); } - - logger.debug('Running'); - - const run = await this.safe_call( - path.join(this.runtime.pkgdir, 'run'), - [this.files[0].name, ...this.args], - this.timeouts.run, - this.memory_limits.run - ); - - this.state = job_states.EXECUTED; - - return { - compile, - run, - language: this.runtime.language, - version: this.runtime.version.raw - }; + } } - async cleanup_processes() { - let processes = [1]; + await fs.rm(this.dir, { recursive: true, force: true }); + } - while (processes.length > 0) { - processes = await new Promise((resolve, reject) => cp.execFile('ps', ['awwxo', 'pid,ruid'], (err, stdout) => { - if (err === null) { - const lines = stdout.split('\n').slice(1); //Remove header with slice - const procs = lines.map(line => { - const [pid, ruid] = line - .trim() - .split(/\s+/) - .map(n => parseInt(n)); - - return { pid, ruid } - }); - - resolve(procs); - } else { - reject(error); - } - })); - - processes = processes.filter(proc => proc.ruid === this.uid); - - for (const proc of processes) { - // First stop the processes, but keep their resources allocated so they cant re-fork - try { - process.kill(proc.pid, 'SIGSTOP'); - } catch { - // Could already be dead - } - } - - - for (const proc of processes) { - // Then clear them out of the process tree - try { - process.kill(proc.pid, 'SIGKILL'); - } catch { - // Could already be dead and just needs to be waited on - } - - wait_pid(proc.pid); - } - } - } - - async cleanup_filesystem() { - for (const clean_path of globals.clean_directories) { - const contents = await fs.readdir(clean_path); - - for (const file of contents) { - const file_path = path.join(clean_path, file); - - try { - const stat = await fs.stat(file_path); - - if (stat.uid === this.uid) { - await fs.rm(file_path, { recursive: true, force: true }); - } - } catch (e) { - // File was somehow deleted in the time that we read the dir to when we checked the file - logger.warn(`Error removing file ${file_path}: ${e}`) - } - } - } - - await fs.rm(this.dir, { recursive: true, force: true }); - } - - async cleanup() { - logger.info(`Cleaning up job uuid=${this.uuid}`); - - await Promise.all([ - this.cleanup_processes(), - this.cleanup_filesystem() - ]); - } + async cleanup() { + logger.info(`Cleaning up job uuid=${this.uuid}`); + await Promise.all([this.cleanup_processes(), this.cleanup_filesystem()]); + } } module.exports = { - Job + Job, }; diff --git a/api/src/package.js b/api/src/package.js index 5b98634..ae6f944 100644 --- a/api/src/package.js +++ b/api/src/package.js @@ -13,188 +13,197 @@ const chownr = require('chownr'); const util = require('util'); class Package { + constructor({ language, version, download, checksum }) { + this.language = language; + this.version = semver.parse(version); + this.checksum = checksum; + this.download = download; + } - constructor({ language, version, download, checksum }){ - this.language = language; - this.version = semver.parse(version); - this.checksum = checksum; - this.download = download; + get installed() { + return fss.exists_sync( + path.join(this.install_path, globals.pkg_installed_file) + ); + } + + get install_path() { + return path.join( + config.data_directory, + globals.data_directories.packages, + this.language, + this.version.raw + ); + } + + async install() { + if (this.installed) { + throw new Error('Already installed'); } - get installed() { - return fss.exists_sync(path.join(this.install_path, globals.pkg_installed_file)); + logger.info(`Installing ${this.language}-${this.version.raw}`); + + if (fss.exists_sync(this.install_path)) { + logger.warn( + `${this.language}-${this.version.raw} has residual files. Removing them.` + ); + await fs.rm(this.install_path, { recursive: true, force: true }); } - get install_path() { - return path.join( - config.data_directory, - globals.data_directories.packages, - this.language, - this.version.raw - ); + logger.debug(`Making directory ${this.install_path}`); + await fs.mkdir(this.install_path, { recursive: true }); + + logger.debug( + `Downloading package from ${this.download} in to ${this.install_path}` + ); + const pkgpath = path.join(this.install_path, 'pkg.tar.gz'); + const download = await fetch(this.download); + + const file_stream = fss.create_write_stream(pkgpath); + await new Promise((resolve, reject) => { + download.body.pipe(file_stream); + download.body.on('error', reject); + + file_stream.on('finish', resolve); + }); + + logger.debug('Validating checksums'); + logger.debug(`Assert sha256(pkg.tar.gz) == ${this.checksum}`); + const cs = crypto + .create_hash('sha256') + .update(fss.readFileSync(pkgpath)) + .digest('hex'); + + if (cs !== this.checksum) { + throw new Error(`Checksum miss-match want: ${val} got: ${cs}`); } - async install() { - if (this.installed) { - throw new Error('Already installed'); - } + logger.debug( + `Extracting package files from archive ${pkgpath} in to ${this.install_path}` + ); - logger.info(`Installing ${this.language}-${this.version.raw}`); + await new Promise((resolve, reject) => { + const proc = cp.exec( + `bash -c 'cd "${this.install_path}" && tar xzf ${pkgpath}'` + ); - if (fss.exists_sync(this.install_path)) { - logger.warn(`${this.language}-${this.version.raw} has residual files. Removing them.`); - await fs.rm(this.install_path, { recursive: true, force: true }); - } + proc.once('exit', (code, _) => { + code === 0 ? resolve() : reject(); + }); - logger.debug(`Making directory ${this.install_path}`); - await fs.mkdir(this.install_path, {recursive: true}); + proc.stdout.pipe(process.stdout); + proc.stderr.pipe(process.stderr); - logger.debug(`Downloading package from ${this.download} in to ${this.install_path}`); - const pkgpath = path.join(this.install_path, 'pkg.tar.gz'); - const download = await fetch(this.download); + proc.once('error', reject); + }); - const file_stream = fss.create_write_stream(pkgpath); - await new Promise((resolve, reject) => { - download.body.pipe(file_stream); - download.body.on('error', reject); + logger.debug('Registering runtime'); + runtime.load_package(this.install_path); - file_stream.on('finish', resolve); - }); + logger.debug('Caching environment'); + const get_env_command = `cd ${this.install_path}; source environment; env`; - logger.debug('Validating checksums'); - logger.debug(`Assert sha256(pkg.tar.gz) == ${this.checksum}`); - const cs = crypto.create_hash("sha256") - .update(fss.readFileSync(pkgpath)) - .digest('hex'); + const envout = await new Promise((resolve, reject) => { + let stdout = ''; - if (cs !== this.checksum) { - throw new Error(`Checksum miss-match want: ${val} got: ${cs}`); - } + const proc = cp.spawn('env', ['-i', 'bash', '-c', `${get_env_command}`], { + stdio: ['ignore', 'pipe', 'pipe'], + }); - logger.debug(`Extracting package files from archive ${pkgpath} in to ${this.install_path}`); + proc.once('exit', (code, _) => { + code === 0 ? resolve(stdout) : reject(); + }); - await new Promise((resolve, reject) => { - const proc = cp.exec(`bash -c 'cd "${this.install_path}" && tar xzf ${pkgpath}'`); + proc.stdout.on('data', (data) => { + stdout += data; + }); - proc.once('exit', (code, _) => { - code === 0 ? resolve() : reject(); - }); + proc.once('error', reject); + }); - proc.stdout.pipe(process.stdout); - proc.stderr.pipe(process.stderr); + const filtered_env = envout + .split('\n') + .filter( + (l) => !['PWD', 'OLDPWD', '_', 'SHLVL'].includes(l.split('=', 2)[0]) + ) + .join('\n'); - proc.once('error', reject); - }); + await fs.write_file(path.join(this.install_path, '.env'), filtered_env); - logger.debug('Registering runtime'); - runtime.load_package(this.install_path); + logger.debug('Changing Ownership of package directory'); + await util.promisify(chownr)(this.install_path, 0, 0); - logger.debug('Caching environment'); - const get_env_command = `cd ${this.install_path}; source environment; env`; + logger.debug('Writing installed state to disk'); + await fs.write_file( + path.join(this.install_path, globals.pkg_installed_file), + Date.now().toString() + ); - const envout = await new Promise((resolve, reject) => { - let stdout = ''; + logger.info(`Installed ${this.language}-${this.version.raw}`); - const proc = cp - .spawn( - 'env', - ['-i','bash','-c',`${get_env_command}`], - { - stdio: ['ignore', 'pipe', 'pipe'] - } - ); + return { + language: this.language, + version: this.version.raw, + }; + } - proc.once('exit', (code, _) => { - code === 0 ? resolve(stdout) : reject(); - }); + async uninstall() { + logger.info(`Uninstalling ${this.language}-${this.version.raw}`); - proc.stdout.on('data', data => { - stdout += data; - }); + logger.debug('Finding runtime'); + const found_runtime = runtime.get_runtime_by_name_and_version( + this.language, + this.version.raw + ); - proc.once('error', reject); - }); - - const filtered_env = envout - .split('\n') - .filter(l => !['PWD','OLDPWD','_', 'SHLVL'].includes(l.split('=',2)[0])) - .join('\n'); - - await fs.write_file(path.join(this.install_path, '.env'), filtered_env); - - logger.debug('Changing Ownership of package directory'); - await util.promisify(chownr)(this.install_path,0,0); - - logger.debug('Writing installed state to disk'); - await fs.write_file(path.join(this.install_path, globals.pkg_installed_file), Date.now().toString()); - - logger.info(`Installed ${this.language}-${this.version.raw}`); - - return { - language: this.language, - version: this.version.raw - }; + if (!found_runtime) { + logger.error( + `Uninstalling ${this.language}-${this.version.raw} failed: Not installed` + ); + throw new Error(`${this.language}-${this.version.raw} is not installed`); } + logger.debug('Unregistering runtime'); + found_runtime.unregister(); - async uninstall(){ - logger.info(`Uninstalling ${this.language}-${this.version.raw}`); + logger.debug('Cleaning files from disk'); + await fs.rmdir(this.install_path, { recursive: true }); - logger.debug("Finding runtime") - const found_runtime = runtime.get_runtime_by_name_and_version(this.language, this.version.raw); + logger.info(`Uninstalled ${this.language}-${this.version.raw}`); - if(!found_runtime){ - logger.error(`Uninstalling ${this.language}-${this.version.raw} failed: Not installed`) - throw new Error(`${this.language}-${this.version.raw} is not installed`) - } + return { + language: this.language, + version: this.version.raw, + }; + } - logger.debug("Unregistering runtime") - found_runtime.unregister(); + static async get_package_list() { + const repo_content = await fetch(config.repo_url).then((x) => x.text()); - logger.debug("Cleaning files from disk") - await fs.rmdir(this.install_path, {recursive: true}) + const entries = repo_content.split('\n').filter((x) => x.length > 0); - logger.info(`Uninstalled ${this.language}-${this.version.raw}`) + return entries.map((line) => { + const [language, version, checksum, download] = line.split(',', 4); - return { - language: this.language, - version: this.version.raw - }; + return new Package({ + language, + version, + checksum, + download, + }); + }); + } - } + static async get_package(lang, version) { + const packages = await Package.get_package_list(); - static async get_package_list() { - const repo_content = await fetch(config.repo_url).then(x => x.text()); + const candidates = packages.filter((pkg) => { + return pkg.language == lang && semver.satisfies(pkg.version, version); + }); - const entries = repo_content - .split('\n') - .filter(x => x.length > 0); - - return entries.map(line => { - const [ language, version, checksum, download ] = line.split(',', 4); - - return new Package({ - language, - version, - checksum, - download - }); - }); - } - - static async get_package(lang, version) { - const packages = await Package.get_package_list(); - - const candidates = packages - .filter(pkg => { - return pkg.language == lang && semver.satisfies(pkg.version, version) - }); - - candidates.sort((a, b) => semver.rcompare(a.version, b.version)); - - return candidates[0] || null; - } + candidates.sort((a, b) => semver.rcompare(a.version, b.version)); + return candidates[0] || null; + } } module.exports = Package; diff --git a/api/src/runtime.js b/api/src/runtime.js index 6ae824e..418dc42 100644 --- a/api/src/runtime.js +++ b/api/src/runtime.js @@ -8,102 +8,118 @@ const path = require('path'); const runtimes = []; class Runtime { + constructor({ language, version, aliases, pkgdir, runtime }) { + this.language = language; + this.version = version; + this.aliases = aliases || []; + this.pkgdir = pkgdir; + this.runtime = runtime; + } - constructor({language, version, aliases, pkgdir, runtime}) { - this.language = language; - this.version = version; - this.aliases = aliases || []; - this.pkgdir = pkgdir; - this.runtime = runtime; + static load_package(package_dir) { + let info = JSON.parse( + fss.read_file_sync(path.join(package_dir, 'pkg-info.json')) + ); + + let { language, version, build_platform, aliases, provides } = info; + version = semver.parse(version); + + if (build_platform !== globals.platform) { + logger.warn( + `Package ${language}-${version} was built for platform ${build_platform}, ` + + `but our platform is ${globals.platform}` + ); } - static load_package(package_dir) { - let info = JSON.parse( - fss.read_file_sync(path.join(package_dir, 'pkg-info.json')) + if (provides) { + // Multiple languages in 1 package + provides.forEach((lang) => { + runtimes.push( + new Runtime({ + language: lang.language, + aliases: lang.aliases, + version, + pkgdir: package_dir, + runtime: language, + }) ); - - let { language, version, build_platform, aliases, provides } = info; - version = semver.parse(version); - - if (build_platform !== globals.platform) { - logger.warn( - `Package ${language}-${version} was built for platform ${build_platform}, ` + - `but our platform is ${globals.platform}` - ); - } - - if (provides) { - // Multiple languages in 1 package - provides.forEach(lang => { - runtimes.push(new Runtime({ - language: lang.language, - aliases: lang.aliases, - version, - pkgdir: package_dir, - runtime: language - })); - }); - } else { - runtimes.push(new Runtime({ - language, - version, - aliases, - pkgdir: package_dir - })) - } - - logger.debug(`Package ${language}-${version} was loaded`); + }); + } else { + runtimes.push( + new Runtime({ + language, + version, + aliases, + pkgdir: package_dir, + }) + ); } - get compiled() { - if (this._compiled === undefined) { - this._compiled = fss.exists_sync(path.join(this.pkgdir, 'compile')); - } + logger.debug(`Package ${language}-${version} was loaded`); + } - return this._compiled; + get compiled() { + if (this._compiled === undefined) { + this._compiled = fss.exists_sync(path.join(this.pkgdir, 'compile')); } - get env_vars() { - if (!this._env_vars) { - const env_file = path.join(this.pkgdir, '.env'); - const env_content = fss.read_file_sync(env_file).toString(); + return this._compiled; + } - this._env_vars = {}; + get env_vars() { + if (!this._env_vars) { + const env_file = path.join(this.pkgdir, '.env'); + const env_content = fss.read_file_sync(env_file).toString(); - env_content - .trim() - .split('\n') - .map(line => line.split('=',2)) - .forEach(([key,val]) => { - this._env_vars[key.trim()] = val.trim(); - }); - } + this._env_vars = {}; - return this._env_vars; + env_content + .trim() + .split('\n') + .map((line) => line.split('=', 2)) + .forEach(([key, val]) => { + this._env_vars[key.trim()] = val.trim(); + }); } - toString() { - return `${this.language}-${this.version.raw}`; - } + return this._env_vars; + } - unregister() { - const index = runtimes.indexOf(this); - runtimes.splice(index, 1); //Remove from runtimes list - } + toString() { + return `${this.language}-${this.version.raw}`; + } + + unregister() { + const index = runtimes.indexOf(this); + runtimes.splice(index, 1); //Remove from runtimes list + } } module.exports = runtimes; module.exports.Runtime = Runtime; -module.exports.get_runtimes_matching_language_version = function(lang, ver){ - return runtimes.filter(rt => (rt.language == lang || rt.aliases.includes(lang)) && semver.satisfies(rt.version, ver)); +module.exports.get_runtimes_matching_language_version = function (lang, ver) { + return runtimes.filter( + (rt) => + (rt.language == lang || rt.aliases.includes(lang)) && + semver.satisfies(rt.version, ver) + ); }; -module.exports.get_latest_runtime_matching_language_version = function(lang, ver){ - return module.exports.get_runtimes_matching_language_version(lang, ver) - .sort((a,b) => semver.rcompare(a.version, b.version))[0]; +module.exports.get_latest_runtime_matching_language_version = function ( + lang, + ver +) { + return module.exports + .get_runtimes_matching_language_version(lang, ver) + .sort((a, b) => semver.rcompare(a.version, b.version))[0]; }; -module.exports.get_runtime_by_name_and_version = function(runtime, ver){ - return runtimes.find(rt => (rt.runtime == runtime || (rt.runtime === undefined && rt.language == runtime)) && semver.satisfies(rt.version, ver)); -} +module.exports.get_runtime_by_name_and_version = function (runtime, ver) { + return runtimes.find( + (rt) => + (rt.runtime == runtime || + (rt.runtime === undefined && rt.language == runtime)) && + semver.satisfies(rt.version, ver) + ); +}; module.exports.load_package = Runtime.load_package; From 1b7504a191c8b67c83e7ef559e05c3e010ba35ad Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 8 May 2021 12:25:04 +1200 Subject: [PATCH 07/10] switch back to startsWith This is because application/json;charset=utf8 is perfectly valid! --- api/package.json | 2 +- api/src/api/v2.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/package.json b/api/package.json index 58ff057..a227cc2 100644 --- a/api/package.json +++ b/api/package.json @@ -17,7 +17,7 @@ }, "license": "MIT", "scripts": { - "lint": "prettier . --write" + "lint": "prettier . --write" }, "devDependencies": { "prettier": "2.2.1" diff --git a/api/src/api/v2.js b/api/src/api/v2.js index ceb06fd..d6b59a0 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -12,7 +12,7 @@ router.use((req, res, next) => { return next(); } - if (req.headers['content-type'] !== 'application/json') { + if (!req.headers['content-type'].startsWith('application/json')) { return res.status(415).send({ message: 'requests must be of type application/json', }); From b3be57e0b4c365e8c408b2ce6d23941ed525a86a Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 8 May 2021 12:30:40 +1200 Subject: [PATCH 08/10] lint like rest of codebase --- api/.prettierrc.yaml | 2 + api/package-lock.json | 2028 ++++++++++++++++++++--------------------- api/package.json | 46 +- api/src/api/v2.js | 346 +++---- api/src/config.js | 258 +++--- api/src/globals.js | 26 +- api/src/index.js | 114 +-- api/src/job.js | 448 ++++----- api/src/package.js | 315 ++++--- api/src/runtime.js | 174 ++-- 10 files changed, 1893 insertions(+), 1864 deletions(-) diff --git a/api/.prettierrc.yaml b/api/.prettierrc.yaml index 0176969..59b6ad3 100644 --- a/api/.prettierrc.yaml +++ b/api/.prettierrc.yaml @@ -1 +1,3 @@ singleQuote: true +tabWidth: 4 +arrowParens: avoid diff --git a/api/package-lock.json b/api/package-lock.json index bdcbb29..c46ae87 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,1019 +1,1019 @@ { - "name": "piston-api", - "version": "3.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "piston-api", - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "body-parser": "^1.19.0", - "chownr": "^2.0.0", - "express": "^4.17.1", - "is-docker": "^2.1.1", - "logplease": "^1.2.15", - "nocamel": "HexF/nocamel#patch-1", - "node-fetch": "^2.6.1", - "semver": "^7.3.4", - "uuid": "^8.3.2", - "waitpid": "git+https://github.com/HexF/node-waitpid.git" - }, - "devDependencies": { - "prettier": "2.2.1" - } - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logplease": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", - "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "dependencies": { - "mime-db": "1.46.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nocamel": { - "resolved": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544" - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/waitpid": { - "resolved": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - }, - "dependencies": { - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" - }, - "logplease": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", - "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" - }, - "mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "requires": { - "mime-db": "1.46.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "nocamel": { - "version": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544", - "from": "nocamel@HexF/nocamel#patch-1" - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", - "dev": true - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "name": "piston-api", + "version": "3.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "piston-api", + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "body-parser": "^1.19.0", + "chownr": "^2.0.0", + "express": "^4.17.1", + "is-docker": "^2.1.1", + "logplease": "^1.2.15", + "nocamel": "HexF/nocamel#patch-1", + "node-fetch": "^2.6.1", + "semver": "^7.3.4", + "uuid": "^8.3.2", + "waitpid": "git+https://github.com/HexF/node-waitpid.git" + }, + "devDependencies": { + "prettier": "2.2.1" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logplease": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", + "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "dependencies": { + "mime-db": "1.46.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nocamel": { + "resolved": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544" + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/waitpid": { + "resolved": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } - } }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "waitpid": { - "version": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa", - "from": "waitpid@git+https://github.com/HexF/node-waitpid.git" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" + }, + "logplease": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", + "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "requires": { + "mime-db": "1.46.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "nocamel": { + "version": "git+ssh://git@github.com/HexF/nocamel.git#89a5bfbbd07c72c302d968b967d0f4fe54846544", + "from": "nocamel@HexF/nocamel#patch-1" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "waitpid": { + "version": "git+ssh://git@github.com/HexF/node-waitpid.git#a08d116a5d993a747624fe72ff890167be8c34aa", + "from": "waitpid@git+https://github.com/HexF/node-waitpid.git" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } } - } } diff --git a/api/package.json b/api/package.json index a227cc2..ab34063 100644 --- a/api/package.json +++ b/api/package.json @@ -1,25 +1,25 @@ { - "name": "piston-api", - "version": "3.0.0", - "description": "API for piston - a high performance code execution engine", - "main": "src/index.js", - "dependencies": { - "body-parser": "^1.19.0", - "chownr": "^2.0.0", - "express": "^4.17.1", - "is-docker": "^2.1.1", - "logplease": "^1.2.15", - "nocamel": "HexF/nocamel#patch-1", - "node-fetch": "^2.6.1", - "semver": "^7.3.4", - "uuid": "^8.3.2", - "waitpid": "git+https://github.com/HexF/node-waitpid.git" - }, - "license": "MIT", - "scripts": { - "lint": "prettier . --write" - }, - "devDependencies": { - "prettier": "2.2.1" - } + "name": "piston-api", + "version": "3.0.0", + "description": "API for piston - a high performance code execution engine", + "main": "src/index.js", + "dependencies": { + "body-parser": "^1.19.0", + "chownr": "^2.0.0", + "express": "^4.17.1", + "is-docker": "^2.1.1", + "logplease": "^1.2.15", + "nocamel": "HexF/nocamel#patch-1", + "node-fetch": "^2.6.1", + "semver": "^7.3.4", + "uuid": "^8.3.2", + "waitpid": "git+https://github.com/HexF/node-waitpid.git" + }, + "license": "MIT", + "scripts": { + "lint": "prettier . --write" + }, + "devDependencies": { + "prettier": "2.2.1" + } } diff --git a/api/src/api/v2.js b/api/src/api/v2.js index d6b59a0..e3e8c68 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -8,217 +8,217 @@ const package = require('../package'); const logger = require('logplease').create('api/v2'); router.use((req, res, next) => { - if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { - return next(); - } + if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { + return next(); + } - if (!req.headers['content-type'].startsWith('application/json')) { - return res.status(415).send({ - message: 'requests must be of type application/json', - }); - } + if (!req.headers['content-type'].startsWith('application/json')) { + return res.status(415).send({ + message: 'requests must be of type application/json', + }); + } - next(); + next(); }); router.post('/execute', async (req, res) => { - const { - language, - version, - files, - stdin, - args, - run_timeout, - compile_timeout, - compile_memory_limit, - run_memory_limit, - } = req.body; + const { + language, + version, + files, + stdin, + args, + run_timeout, + compile_timeout, + compile_memory_limit, + run_memory_limit, + } = req.body; - if (!language || typeof language !== 'string') { - return res.status(400).send({ - message: 'language is required as a string', + if (!language || typeof language !== 'string') { + return res.status(400).send({ + message: 'language is required as a string', + }); + } + + if (!version || typeof version !== 'string') { + return res.status(400).send({ + message: 'version is required as a string', + }); + } + + if (!files || !Array.isArray(files)) { + return res.status(400).send({ + message: 'files is required as an array', + }); + } + + for (const [i, file] of files.entries()) { + if (typeof file.content !== 'string') { + return res.status(400).send({ + message: `files[${i}].content is required as a string`, + }); + } + } + + if (compile_memory_limit) { + if (typeof compile_memory_limit !== 'number') { + return res.status(400).send({ + message: 'if specified, compile_memory_limit must be a number', + }); + } + + if ( + config.compile_memory_limit >= 0 && + (compile_memory_limit > config.compile_memory_limit || + compile_memory_limit < 0) + ) { + return res.status(400).send({ + message: + 'compile_memory_limit cannot exceed the configured limit of ' + + config.compile_memory_limit, + }); + } + } + + if (run_memory_limit) { + if (typeof run_memory_limit !== 'number') { + return res.status(400).send({ + message: 'if specified, run_memory_limit must be a number', + }); + } + + if ( + config.run_memory_limit >= 0 && + (run_memory_limit > config.run_memory_limit || run_memory_limit < 0) + ) { + return res.status(400).send({ + message: + 'run_memory_limit cannot exceed the configured limit of ' + + config.run_memory_limit, + }); + } + } + + const rt = runtime.get_latest_runtime_matching_language_version( + language, + version + ); + + if (rt === undefined) { + return res.status(400).send({ + message: `${language}-${version} runtime is unknown`, + }); + } + + const job = new Job({ + runtime: rt, + alias: language, + files: files, + args: args || [], + stdin: stdin || '', + timeouts: { + run: run_timeout || 3000, + compile: compile_timeout || 10000, + }, + memory_limits: { + run: run_memory_limit || config.run_memory_limit, + compile: compile_memory_limit || config.compile_memory_limit, + }, }); - } - if (!version || typeof version !== 'string') { - return res.status(400).send({ - message: 'version is required as a string', - }); - } + await job.prime(); - if (!files || !Array.isArray(files)) { - return res.status(400).send({ - message: 'files is required as an array', - }); - } + const result = await job.execute(); - for (const [i, file] of files.entries()) { - if (typeof file.content !== 'string') { - return res.status(400).send({ - message: `files[${i}].content is required as a string`, - }); - } - } + await job.cleanup(); - if (compile_memory_limit) { - if (typeof compile_memory_limit !== 'number') { - return res.status(400).send({ - message: 'if specified, compile_memory_limit must be a number', - }); - } - - if ( - config.compile_memory_limit >= 0 && - (compile_memory_limit > config.compile_memory_limit || - compile_memory_limit < 0) - ) { - return res.status(400).send({ - message: - 'compile_memory_limit cannot exceed the configured limit of ' + - config.compile_memory_limit, - }); - } - } - - if (run_memory_limit) { - if (typeof run_memory_limit !== 'number') { - return res.status(400).send({ - message: 'if specified, run_memory_limit must be a number', - }); - } - - if ( - config.run_memory_limit >= 0 && - (run_memory_limit > config.run_memory_limit || run_memory_limit < 0) - ) { - return res.status(400).send({ - message: - 'run_memory_limit cannot exceed the configured limit of ' + - config.run_memory_limit, - }); - } - } - - const rt = runtime.get_latest_runtime_matching_language_version( - language, - version - ); - - if (rt === undefined) { - return res.status(400).send({ - message: `${language}-${version} runtime is unknown`, - }); - } - - const job = new Job({ - runtime: rt, - alias: language, - files: files, - args: args || [], - stdin: stdin || '', - timeouts: { - run: run_timeout || 3000, - compile: compile_timeout || 10000, - }, - memory_limits: { - run: run_memory_limit || config.run_memory_limit, - compile: compile_memory_limit || config.compile_memory_limit, - }, - }); - - await job.prime(); - - const result = await job.execute(); - - await job.cleanup(); - - return res.status(200).send(result); + return res.status(200).send(result); }); router.get('/runtimes', (req, res) => { - const runtimes = runtime.map((rt) => { - return { - language: rt.language, - version: rt.version.raw, - aliases: rt.aliases, - runtime: rt.runtime, - }; - }); + const runtimes = runtime.map(rt => { + return { + language: rt.language, + version: rt.version.raw, + aliases: rt.aliases, + runtime: rt.runtime, + }; + }); - return res.status(200).send(runtimes); + return res.status(200).send(runtimes); }); router.get('/packages', async (req, res) => { - logger.debug('Request to list packages'); - let packages = await package.get_package_list(); + logger.debug('Request to list packages'); + let packages = await package.get_package_list(); - packages = packages.map((pkg) => { - return { - language: pkg.language, - language_version: pkg.version.raw, - installed: pkg.installed, - }; - }); + packages = packages.map(pkg => { + return { + language: pkg.language, + language_version: pkg.version.raw, + installed: pkg.installed, + }; + }); - return res.status(200).send(packages); + return res.status(200).send(packages); }); router.post('/packages/:language/:version', async (req, res) => { - logger.debug('Request to install package'); + logger.debug('Request to install package'); - const { language, version } = req.params; + const { language, version } = req.params; - const pkg = await package.get_package(language, version); + const pkg = await package.get_package(language, version); - if (pkg == null) { - return res.status(404).send({ - message: `Requested package ${language}-${version} does not exist`, - }); - } + if (pkg == null) { + return res.status(404).send({ + message: `Requested package ${language}-${version} does not exist`, + }); + } - try { - const response = await pkg.install(); + try { + const response = await pkg.install(); - return res.status(200).send(response); - } catch (e) { - logger.error( - `Error while installing package ${pkg.language}-${pkg.version}:`, - e.message - ); + return res.status(200).send(response); + } catch (e) { + logger.error( + `Error while installing package ${pkg.language}-${pkg.version}:`, + e.message + ); - return res.status(500).send({ - message: e.message, - }); - } + return res.status(500).send({ + message: e.message, + }); + } }); router.delete('/packages/:language/:version', async (req, res) => { - logger.debug('Request to uninstall package'); + logger.debug('Request to uninstall package'); - const { language, version } = req.params; + const { language, version } = req.params; - const pkg = await package.get_package(language, version); + const pkg = await package.get_package(language, version); - if (pkg == null) { - return res.status(404).send({ - message: `Requested package ${language}-${version} does not exist`, - }); - } + if (pkg == null) { + return res.status(404).send({ + message: `Requested package ${language}-${version} does not exist`, + }); + } - try { - const response = await pkg.uninstall(); + try { + const response = await pkg.uninstall(); - return res.status(200).send(response); - } catch (e) { - logger.error( - `Error while uninstalling package ${pkg.language}-${pkg.version}:`, - e.message - ); + return res.status(200).send(response); + } catch (e) { + logger.error( + `Error while uninstalling package ${pkg.language}-${pkg.version}:`, + e.message + ); - return res.status(500).send({ - message: e.message, - }); - } + return res.status(500).send({ + message: e.message, + }); + } }); module.exports = router; diff --git a/api/src/config.js b/api/src/config.js index a17617f..acf45ce 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -3,114 +3,117 @@ const Logger = require('logplease'); const logger = Logger.create('config'); const options = [ - { - key: 'log_level', - desc: 'Level of data to log', - default: 'INFO', - options: Object.values(Logger.LogLevels), - validators: [ - (x) => - Object.values(Logger.LogLevels).includes(x) || - `Log level ${x} does not exist`, - ], - }, - { - key: 'bind_address', - desc: 'Address to bind REST API on\nThank @Bones for the number', - default: '0.0.0.0:2000', - validators: [], - }, - { - key: 'data_directory', - desc: 'Absolute path to store all piston related data at', - default: '/piston', - validators: [(x) => fss.exists_sync(x) || `Directory ${x} does not exist`], - }, - { - key: 'runner_uid_min', - desc: 'Minimum uid to use for runner', - default: 1001, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'runner_uid_max', - desc: 'Maximum uid to use for runner', - default: 1500, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'runner_gid_min', - desc: 'Minimum gid to use for runner', - default: 1001, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'runner_gid_max', - desc: 'Maximum gid to use for runner', - default: 1500, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'disable_networking', - desc: 'Set to true to disable networking', - default: true, - parser: (x) => x === 'true', - validators: [(x) => typeof x === 'boolean' || `${x} is not a boolean`], - }, - { - key: 'output_max_size', - desc: 'Max size of each stdio buffer', - default: 1024, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'max_process_count', - desc: 'Max number of processes per job', - default: 64, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'max_open_files', - desc: 'Max number of open files per job', - default: 2048, - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'max_file_size', - desc: 'Max file size in bytes for a file', - default: 10000000, //10MB - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'compile_memory_limit', - desc: - 'Max memory usage for compile stage in bytes (set to -1 for no limit)', - default: -1, // no limit - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'run_memory_limit', - desc: 'Max memory usage for run stage in bytes (set to -1 for no limit)', - default: -1, // no limit - parser: parse_int, - validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], - }, - { - key: 'repo_url', - desc: 'URL of repo index', - default: - 'https://github.com/engineer-man/piston/releases/download/pkgs/index', - validators: [], - }, + { + key: 'log_level', + desc: 'Level of data to log', + default: 'INFO', + options: Object.values(Logger.LogLevels), + validators: [ + x => + Object.values(Logger.LogLevels).includes(x) || + `Log level ${x} does not exist`, + ], + }, + { + key: 'bind_address', + desc: 'Address to bind REST API on\nThank @Bones for the number', + default: '0.0.0.0:2000', + validators: [], + }, + { + key: 'data_directory', + desc: 'Absolute path to store all piston related data at', + default: '/piston', + validators: [ + x => fss.exists_sync(x) || `Directory ${x} does not exist`, + ], + }, + { + key: 'runner_uid_min', + desc: 'Minimum uid to use for runner', + default: 1001, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'runner_uid_max', + desc: 'Maximum uid to use for runner', + default: 1500, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'runner_gid_min', + desc: 'Minimum gid to use for runner', + default: 1001, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'runner_gid_max', + desc: 'Maximum gid to use for runner', + default: 1500, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'disable_networking', + desc: 'Set to true to disable networking', + default: true, + parser: x => x === 'true', + validators: [x => typeof x === 'boolean' || `${x} is not a boolean`], + }, + { + key: 'output_max_size', + desc: 'Max size of each stdio buffer', + default: 1024, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'max_process_count', + desc: 'Max number of processes per job', + default: 64, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'max_open_files', + desc: 'Max number of open files per job', + default: 2048, + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'max_file_size', + desc: 'Max file size in bytes for a file', + default: 10000000, //10MB + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'compile_memory_limit', + desc: + 'Max memory usage for compile stage in bytes (set to -1 for no limit)', + default: -1, // no limit + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'run_memory_limit', + desc: + 'Max memory usage for run stage in bytes (set to -1 for no limit)', + default: -1, // no limit + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'repo_url', + desc: 'URL of repo index', + default: + 'https://github.com/engineer-man/piston/releases/download/pkgs/index', + validators: [], + }, ]; logger.info(`Loading Configuration from environment`); @@ -119,34 +122,37 @@ let errored = false; let config = {}; -options.forEach((option) => { - const env_key = 'PISTON_' + option.key.to_upper_case(); +options.forEach(option => { + const env_key = 'PISTON_' + option.key.to_upper_case(); - const parser = option.parser || ((x) => x); + const parser = option.parser || (x => x); - const env_val = process.env[env_key]; + const env_val = process.env[env_key]; - const parsed_val = parser(env_val); + const parsed_val = parser(env_val); - const value = env_val || option.default; + const value = env_val || option.default; - option.validators.for_each((validator) => { - let response = null; - if (env_val) response = validator(parsed_val, env_val); - else response = validator(value, value); + option.validators.for_each(validator => { + let response = null; + if (env_val) response = validator(parsed_val, env_val); + else response = validator(value, value); - if (response !== true) { - errored = true; - logger.error(`Config option ${option.key} failed validation:`, response); - return; - } - }); + if (response !== true) { + errored = true; + logger.error( + `Config option ${option.key} failed validation:`, + response + ); + return; + } + }); - config[option.key] = value; + config[option.key] = value; }); if (errored) { - process.exit(1); + process.exit(1); } logger.info('Configuration successfully loaded'); diff --git a/api/src/globals.js b/api/src/globals.js index 4546f74..933d2ca 100644 --- a/api/src/globals.js +++ b/api/src/globals.js @@ -2,19 +2,19 @@ const is_docker = require('is-docker'); const fs = require('fs'); const platform = `${is_docker() ? 'docker' : 'baremetal'}-${fs - .read_file_sync('/etc/os-release') - .toString() - .split('\n') - .find((x) => x.startsWith('ID')) - .replace('ID=', '')}`; + .read_file_sync('/etc/os-release') + .toString() + .split('\n') + .find(x => x.startsWith('ID')) + .replace('ID=', '')}`; module.exports = { - data_directories: { - packages: 'packages', - jobs: 'jobs', - }, - version: require('../package.json').version, - platform, - pkg_installed_file: '.ppman-installed', //Used as indication for if a package was installed - clean_directories: ['/dev/shm', '/run/lock', '/tmp', '/var/tmp'], + data_directories: { + packages: 'packages', + jobs: 'jobs', + }, + version: require('../package.json').version, + platform, + pkg_installed_file: '.ppman-installed', //Used as indication for if a package was installed + clean_directories: ['/dev/shm', '/run/lock', '/tmp', '/var/tmp'], }; diff --git a/api/src/index.js b/api/src/index.js index 75fda40..ef16916 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -14,77 +14,77 @@ const logger = Logger.create('index'); const app = express(); (async () => { - logger.info('Setting loglevel to', config.log_level); - Logger.setLogLevel(config.log_level); - logger.debug('Ensuring data directories exist'); + logger.info('Setting loglevel to', config.log_level); + Logger.setLogLevel(config.log_level); + logger.debug('Ensuring data directories exist'); - Object.values(globals.data_directories).for_each((dir) => { - let data_path = path.join(config.data_directory, dir); + Object.values(globals.data_directories).for_each(dir => { + let data_path = path.join(config.data_directory, dir); - logger.debug(`Ensuring ${data_path} exists`); + logger.debug(`Ensuring ${data_path} exists`); - if (!fss.exists_sync(data_path)) { - logger.info(`${data_path} does not exist.. Creating..`); + if (!fss.exists_sync(data_path)) { + logger.info(`${data_path} does not exist.. Creating..`); - try { - fss.mkdir_sync(data_path); - } catch (e) { - logger.error(`Failed to create ${data_path}: `, e.message); - } - } - }); + try { + fss.mkdir_sync(data_path); + } catch (e) { + logger.error(`Failed to create ${data_path}: `, e.message); + } + } + }); - logger.info('Loading packages'); - const pkgdir = path.join( - config.data_directory, - globals.data_directories.packages - ); - - const pkglist = await fs.readdir(pkgdir); - - const languages = await Promise.all( - pkglist.map((lang) => { - return fs.readdir(path.join(pkgdir, lang)).then((x) => { - return x.map((y) => path.join(pkgdir, lang, y)); - }); - }) - ); - - const installed_languages = languages - .flat() - .filter((pkg) => - fss.exists_sync(path.join(pkg, globals.pkg_installed_file)) + logger.info('Loading packages'); + const pkgdir = path.join( + config.data_directory, + globals.data_directories.packages ); - installed_languages.for_each((pkg) => runtime.load_package(pkg)); + const pkglist = await fs.readdir(pkgdir); - logger.info('Starting API Server'); - logger.debug('Constructing Express App'); - logger.debug('Registering middleware'); + const languages = await Promise.all( + pkglist.map(lang => { + return fs.readdir(path.join(pkgdir, lang)).then(x => { + return x.map(y => path.join(pkgdir, lang, y)); + }); + }) + ); - app.use(body_parser.urlencoded({ extended: true })); - app.use(body_parser.json()); + const installed_languages = languages + .flat() + .filter(pkg => + fss.exists_sync(path.join(pkg, globals.pkg_installed_file)) + ); - app.use((err, req, res, next) => { - return res.status(400).send({ - stack: err.stack, + installed_languages.for_each(pkg => runtime.load_package(pkg)); + + logger.info('Starting API Server'); + logger.debug('Constructing Express App'); + logger.debug('Registering middleware'); + + app.use(body_parser.urlencoded({ extended: true })); + app.use(body_parser.json()); + + app.use((err, req, res, next) => { + return res.status(400).send({ + stack: err.stack, + }); }); - }); - logger.debug('Registering Routes'); + logger.debug('Registering Routes'); - const api_v2 = require('./api/v2'); - app.use('/api/v2', api_v2); - app.use('/api/v2', api_v2); + const api_v2 = require('./api/v2'); + app.use('/api/v2', api_v2); + app.use('/api/v2', api_v2); - app.use((req, res, next) => { - return res.status(404).send({ message: 'Not Found' }); - }); + app.use((req, res, next) => { + return res.status(404).send({ message: 'Not Found' }); + }); - logger.debug('Calling app.listen'); - const [address, port] = config.bind_address.split(':'); + logger.debug('Calling app.listen'); + const [address, port] = config.bind_address.split(':'); - app.listen(port, address, () => { - logger.info('API server started on', config.bind_address); - }); + app.listen(port, address, () => { + logger.info('API server started on', config.bind_address); + }); })(); diff --git a/api/src/job.js b/api/src/job.js index f301a5e..d4b90ea 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -8,268 +8,278 @@ const fs = require('fs/promises'); const wait_pid = require('waitpid'); const job_states = { - READY: Symbol('Ready to be primed'), - PRIMED: Symbol('Primed and ready for execution'), - EXECUTED: Symbol('Executed and ready for cleanup'), + READY: Symbol('Ready to be primed'), + PRIMED: Symbol('Primed and ready for execution'), + EXECUTED: Symbol('Executed and ready for cleanup'), }; let uid = 0; let gid = 0; class Job { - constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { - this.uuid = uuidv4(); - this.runtime = runtime; - this.files = files.map((file, i) => ({ - name: file.name || `file${i}.code`, - content: file.content, - })); + constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { + this.uuid = uuidv4(); + this.runtime = runtime; + this.files = files.map((file, i) => ({ + name: file.name || `file${i}.code`, + content: file.content, + })); - this.args = args; - this.stdin = stdin; - this.timeouts = timeouts; - this.memory_limits = memory_limits; + this.args = args; + this.stdin = stdin; + this.timeouts = timeouts; + this.memory_limits = memory_limits; - this.uid = config.runner_uid_min + uid; - this.gid = config.runner_gid_min + gid; + this.uid = config.runner_uid_min + uid; + this.gid = config.runner_gid_min + gid; - uid++; - gid++; + uid++; + gid++; - uid %= config.runner_uid_max - config.runner_uid_min + 1; - gid %= config.runner_gid_max - config.runner_gid_min + 1; + uid %= config.runner_uid_max - config.runner_uid_min + 1; + gid %= config.runner_gid_max - config.runner_gid_min + 1; - this.state = job_states.READY; - this.dir = path.join( - config.data_directory, - globals.data_directories.jobs, - this.uuid - ); - } - - async prime() { - logger.info(`Priming job uuid=${this.uuid}`); - - logger.debug('Writing files to job cache'); - - logger.debug(`Transfering ownership uid=${this.uid} gid=${this.gid}`); - - await fs.mkdir(this.dir, { mode: 0o700 }); - await fs.chown(this.dir, this.uid, this.gid); - - for (const file of this.files) { - let file_path = path.join(this.dir, file.name); - - await fs.write_file(file_path, file.content); - await fs.chown(file_path, this.uid, this.gid); + this.state = job_states.READY; + this.dir = path.join( + config.data_directory, + globals.data_directories.jobs, + this.uuid + ); } - this.state = job_states.PRIMED; + async prime() { + logger.info(`Priming job uuid=${this.uuid}`); - logger.debug('Primed job'); - } + logger.debug('Writing files to job cache'); - async safe_call(file, args, timeout, memory_limit) { - return new Promise((resolve, reject) => { - const nonetwork = config.disable_networking ? ['nosocket'] : []; + logger.debug(`Transfering ownership uid=${this.uid} gid=${this.gid}`); - const prlimit = [ - 'prlimit', - '--nproc=' + config.max_process_count, - '--nofile=' + config.max_open_files, - '--fsize=' + config.max_file_size, - ]; + await fs.mkdir(this.dir, { mode: 0o700 }); + await fs.chown(this.dir, this.uid, this.gid); - if (memory_limit >= 0) { - prlimit.push('--as=' + memory_limit); - } + for (const file of this.files) { + let file_path = path.join(this.dir, file.name); - const proc_call = [...prlimit, ...nonetwork, 'bash', file, ...args]; - - var stdout = ''; - var stderr = ''; - var output = ''; - - const proc = cp.spawn(proc_call[0], proc_call.splice(1), { - env: { - ...this.runtime.env_vars, - PISTON_LANGUAGE: this.runtime.language, - }, - stdio: 'pipe', - cwd: this.dir, - uid: this.uid, - gid: this.gid, - detached: true, //give this process its own process group - }); - - proc.stdin.write(this.stdin); - proc.stdin.end(); - proc.stdin.destroy(); - - const kill_timeout = set_timeout((_) => proc.kill('SIGKILL'), timeout); - - proc.stderr.on('data', (data) => { - if (stderr.length > config.output_max_size) { - proc.kill('SIGKILL'); - } else { - stderr += data; - output += data; + await fs.write_file(file_path, file.content); + await fs.chown(file_path, this.uid, this.gid); } - }); - proc.stdout.on('data', (data) => { - if (stdout.length > config.output_max_size) { - proc.kill('SIGKILL'); - } else { - stdout += data; - output += data; - } - }); + this.state = job_states.PRIMED; - const exit_cleanup = () => { - clear_timeout(kill_timeout); - - proc.stderr.destroy(); - proc.stdout.destroy(); - }; - - proc.on('exit', (code, signal) => { - exit_cleanup(); - - resolve({ stdout, stderr, code, signal, output }); - }); - - proc.on('error', (err) => { - exit_cleanup(); - - reject({ error: err, stdout, stderr, output }); - }); - }); - } - - async execute() { - if (this.state !== job_states.PRIMED) { - throw new Error( - 'Job must be in primed state, current state: ' + this.state.toString() - ); + logger.debug('Primed job'); } - logger.info( - `Executing job uuid=${this.uuid} uid=${this.uid} gid=${ - this.gid - } runtime=${this.runtime.toString()}` - ); + async safe_call(file, args, timeout, memory_limit) { + return new Promise((resolve, reject) => { + const nonetwork = config.disable_networking ? ['nosocket'] : []; - logger.debug('Compiling'); + const prlimit = [ + 'prlimit', + '--nproc=' + config.max_process_count, + '--nofile=' + config.max_open_files, + '--fsize=' + config.max_file_size, + ]; - let compile; + if (memory_limit >= 0) { + prlimit.push('--as=' + memory_limit); + } - if (this.runtime.compiled) { - compile = await this.safe_call( - path.join(this.runtime.pkgdir, 'compile'), - this.files.map((x) => x.name), - this.timeouts.compile, - this.memory_limits.compile - ); - } + const proc_call = [...prlimit, ...nonetwork, 'bash', file, ...args]; - logger.debug('Running'); + var stdout = ''; + var stderr = ''; + var output = ''; - const run = await this.safe_call( - path.join(this.runtime.pkgdir, 'run'), - [this.files[0].name, ...this.args], - this.timeouts.run, - this.memory_limits.run - ); - - this.state = job_states.EXECUTED; - - return { - compile, - run, - language: this.runtime.language, - version: this.runtime.version.raw, - }; - } - - async cleanup_processes() { - let processes = [1]; - - while (processes.length > 0) { - processes = await new Promise((resolve, reject) => - cp.execFile('ps', ['awwxo', 'pid,ruid'], (err, stdout) => { - if (err === null) { - const lines = stdout.split('\n').slice(1); //Remove header with slice - const procs = lines.map((line) => { - const [pid, ruid] = line - .trim() - .split(/\s+/) - .map((n) => parseInt(n)); - - return { pid, ruid }; + const proc = cp.spawn(proc_call[0], proc_call.splice(1), { + env: { + ...this.runtime.env_vars, + PISTON_LANGUAGE: this.runtime.language, + }, + stdio: 'pipe', + cwd: this.dir, + uid: this.uid, + gid: this.gid, + detached: true, //give this process its own process group }); - resolve(procs); - } else { - reject(error); - } - }) - ); + proc.stdin.write(this.stdin); + proc.stdin.end(); + proc.stdin.destroy(); - processes = processes.filter((proc) => proc.ruid === this.uid); + const kill_timeout = set_timeout( + _ => proc.kill('SIGKILL'), + timeout + ); - for (const proc of processes) { - // First stop the processes, but keep their resources allocated so they cant re-fork - try { - process.kill(proc.pid, 'SIGSTOP'); - } catch { - // Could already be dead - } - } + proc.stderr.on('data', data => { + if (stderr.length > config.output_max_size) { + proc.kill('SIGKILL'); + } else { + stderr += data; + output += data; + } + }); - for (const proc of processes) { - // Then clear them out of the process tree - try { - process.kill(proc.pid, 'SIGKILL'); - } catch { - // Could already be dead and just needs to be waited on - } + proc.stdout.on('data', data => { + if (stdout.length > config.output_max_size) { + proc.kill('SIGKILL'); + } else { + stdout += data; + output += data; + } + }); - wait_pid(proc.pid); - } - } - } + const exit_cleanup = () => { + clear_timeout(kill_timeout); - async cleanup_filesystem() { - for (const clean_path of globals.clean_directories) { - const contents = await fs.readdir(clean_path); + proc.stderr.destroy(); + proc.stdout.destroy(); + }; - for (const file of contents) { - const file_path = path.join(clean_path, file); + proc.on('exit', (code, signal) => { + exit_cleanup(); - try { - const stat = await fs.stat(file_path); + resolve({ stdout, stderr, code, signal, output }); + }); - if (stat.uid === this.uid) { - await fs.rm(file_path, { recursive: true, force: true }); - } - } catch (e) { - // File was somehow deleted in the time that we read the dir to when we checked the file - logger.warn(`Error removing file ${file_path}: ${e}`); - } - } + proc.on('error', err => { + exit_cleanup(); + + reject({ error: err, stdout, stderr, output }); + }); + }); } - await fs.rm(this.dir, { recursive: true, force: true }); - } + async execute() { + if (this.state !== job_states.PRIMED) { + throw new Error( + 'Job must be in primed state, current state: ' + + this.state.toString() + ); + } - async cleanup() { - logger.info(`Cleaning up job uuid=${this.uuid}`); + logger.info( + `Executing job uuid=${this.uuid} uid=${this.uid} gid=${ + this.gid + } runtime=${this.runtime.toString()}` + ); - await Promise.all([this.cleanup_processes(), this.cleanup_filesystem()]); - } + logger.debug('Compiling'); + + let compile; + + if (this.runtime.compiled) { + compile = await this.safe_call( + path.join(this.runtime.pkgdir, 'compile'), + this.files.map(x => x.name), + this.timeouts.compile, + this.memory_limits.compile + ); + } + + logger.debug('Running'); + + const run = await this.safe_call( + path.join(this.runtime.pkgdir, 'run'), + [this.files[0].name, ...this.args], + this.timeouts.run, + this.memory_limits.run + ); + + this.state = job_states.EXECUTED; + + return { + compile, + run, + language: this.runtime.language, + version: this.runtime.version.raw, + }; + } + + async cleanup_processes() { + let processes = [1]; + + while (processes.length > 0) { + processes = await new Promise((resolve, reject) => + cp.execFile('ps', ['awwxo', 'pid,ruid'], (err, stdout) => { + if (err === null) { + const lines = stdout.split('\n').slice(1); //Remove header with slice + const procs = lines.map(line => { + const [pid, ruid] = line + .trim() + .split(/\s+/) + .map(n => parseInt(n)); + + return { pid, ruid }; + }); + + resolve(procs); + } else { + reject(error); + } + }) + ); + + processes = processes.filter(proc => proc.ruid === this.uid); + + for (const proc of processes) { + // First stop the processes, but keep their resources allocated so they cant re-fork + try { + process.kill(proc.pid, 'SIGSTOP'); + } catch { + // Could already be dead + } + } + + for (const proc of processes) { + // Then clear them out of the process tree + try { + process.kill(proc.pid, 'SIGKILL'); + } catch { + // Could already be dead and just needs to be waited on + } + + wait_pid(proc.pid); + } + } + } + + async cleanup_filesystem() { + for (const clean_path of globals.clean_directories) { + const contents = await fs.readdir(clean_path); + + for (const file of contents) { + const file_path = path.join(clean_path, file); + + try { + const stat = await fs.stat(file_path); + + if (stat.uid === this.uid) { + await fs.rm(file_path, { + recursive: true, + force: true, + }); + } + } catch (e) { + // File was somehow deleted in the time that we read the dir to when we checked the file + logger.warn(`Error removing file ${file_path}: ${e}`); + } + } + } + + await fs.rm(this.dir, { recursive: true, force: true }); + } + + async cleanup() { + logger.info(`Cleaning up job uuid=${this.uuid}`); + + await Promise.all([ + this.cleanup_processes(), + this.cleanup_filesystem(), + ]); + } } module.exports = { - Job, + Job, }; diff --git a/api/src/package.js b/api/src/package.js index ae6f944..1baa8af 100644 --- a/api/src/package.js +++ b/api/src/package.js @@ -13,197 +13,208 @@ const chownr = require('chownr'); const util = require('util'); class Package { - constructor({ language, version, download, checksum }) { - this.language = language; - this.version = semver.parse(version); - this.checksum = checksum; - this.download = download; - } - - get installed() { - return fss.exists_sync( - path.join(this.install_path, globals.pkg_installed_file) - ); - } - - get install_path() { - return path.join( - config.data_directory, - globals.data_directories.packages, - this.language, - this.version.raw - ); - } - - async install() { - if (this.installed) { - throw new Error('Already installed'); + constructor({ language, version, download, checksum }) { + this.language = language; + this.version = semver.parse(version); + this.checksum = checksum; + this.download = download; } - logger.info(`Installing ${this.language}-${this.version.raw}`); - - if (fss.exists_sync(this.install_path)) { - logger.warn( - `${this.language}-${this.version.raw} has residual files. Removing them.` - ); - await fs.rm(this.install_path, { recursive: true, force: true }); + get installed() { + return fss.exists_sync( + path.join(this.install_path, globals.pkg_installed_file) + ); } - logger.debug(`Making directory ${this.install_path}`); - await fs.mkdir(this.install_path, { recursive: true }); - - logger.debug( - `Downloading package from ${this.download} in to ${this.install_path}` - ); - const pkgpath = path.join(this.install_path, 'pkg.tar.gz'); - const download = await fetch(this.download); - - const file_stream = fss.create_write_stream(pkgpath); - await new Promise((resolve, reject) => { - download.body.pipe(file_stream); - download.body.on('error', reject); - - file_stream.on('finish', resolve); - }); - - logger.debug('Validating checksums'); - logger.debug(`Assert sha256(pkg.tar.gz) == ${this.checksum}`); - const cs = crypto - .create_hash('sha256') - .update(fss.readFileSync(pkgpath)) - .digest('hex'); - - if (cs !== this.checksum) { - throw new Error(`Checksum miss-match want: ${val} got: ${cs}`); + get install_path() { + return path.join( + config.data_directory, + globals.data_directories.packages, + this.language, + this.version.raw + ); } - logger.debug( - `Extracting package files from archive ${pkgpath} in to ${this.install_path}` - ); + async install() { + if (this.installed) { + throw new Error('Already installed'); + } - await new Promise((resolve, reject) => { - const proc = cp.exec( - `bash -c 'cd "${this.install_path}" && tar xzf ${pkgpath}'` - ); + logger.info(`Installing ${this.language}-${this.version.raw}`); - proc.once('exit', (code, _) => { - code === 0 ? resolve() : reject(); - }); + if (fss.exists_sync(this.install_path)) { + logger.warn( + `${this.language}-${this.version.raw} has residual files. Removing them.` + ); + await fs.rm(this.install_path, { recursive: true, force: true }); + } - proc.stdout.pipe(process.stdout); - proc.stderr.pipe(process.stderr); + logger.debug(`Making directory ${this.install_path}`); + await fs.mkdir(this.install_path, { recursive: true }); - proc.once('error', reject); - }); + logger.debug( + `Downloading package from ${this.download} in to ${this.install_path}` + ); + const pkgpath = path.join(this.install_path, 'pkg.tar.gz'); + const download = await fetch(this.download); - logger.debug('Registering runtime'); - runtime.load_package(this.install_path); + const file_stream = fss.create_write_stream(pkgpath); + await new Promise((resolve, reject) => { + download.body.pipe(file_stream); + download.body.on('error', reject); - logger.debug('Caching environment'); - const get_env_command = `cd ${this.install_path}; source environment; env`; + file_stream.on('finish', resolve); + }); - const envout = await new Promise((resolve, reject) => { - let stdout = ''; + logger.debug('Validating checksums'); + logger.debug(`Assert sha256(pkg.tar.gz) == ${this.checksum}`); + const cs = crypto + .create_hash('sha256') + .update(fss.readFileSync(pkgpath)) + .digest('hex'); - const proc = cp.spawn('env', ['-i', 'bash', '-c', `${get_env_command}`], { - stdio: ['ignore', 'pipe', 'pipe'], - }); + if (cs !== this.checksum) { + throw new Error(`Checksum miss-match want: ${val} got: ${cs}`); + } - proc.once('exit', (code, _) => { - code === 0 ? resolve(stdout) : reject(); - }); + logger.debug( + `Extracting package files from archive ${pkgpath} in to ${this.install_path}` + ); - proc.stdout.on('data', (data) => { - stdout += data; - }); + await new Promise((resolve, reject) => { + const proc = cp.exec( + `bash -c 'cd "${this.install_path}" && tar xzf ${pkgpath}'` + ); - proc.once('error', reject); - }); + proc.once('exit', (code, _) => { + code === 0 ? resolve() : reject(); + }); - const filtered_env = envout - .split('\n') - .filter( - (l) => !['PWD', 'OLDPWD', '_', 'SHLVL'].includes(l.split('=', 2)[0]) - ) - .join('\n'); + proc.stdout.pipe(process.stdout); + proc.stderr.pipe(process.stderr); - await fs.write_file(path.join(this.install_path, '.env'), filtered_env); + proc.once('error', reject); + }); - logger.debug('Changing Ownership of package directory'); - await util.promisify(chownr)(this.install_path, 0, 0); + logger.debug('Registering runtime'); + runtime.load_package(this.install_path); - logger.debug('Writing installed state to disk'); - await fs.write_file( - path.join(this.install_path, globals.pkg_installed_file), - Date.now().toString() - ); + logger.debug('Caching environment'); + const get_env_command = `cd ${this.install_path}; source environment; env`; - logger.info(`Installed ${this.language}-${this.version.raw}`); + const envout = await new Promise((resolve, reject) => { + let stdout = ''; - return { - language: this.language, - version: this.version.raw, - }; - } + const proc = cp.spawn( + 'env', + ['-i', 'bash', '-c', `${get_env_command}`], + { + stdio: ['ignore', 'pipe', 'pipe'], + } + ); - async uninstall() { - logger.info(`Uninstalling ${this.language}-${this.version.raw}`); + proc.once('exit', (code, _) => { + code === 0 ? resolve(stdout) : reject(); + }); - logger.debug('Finding runtime'); - const found_runtime = runtime.get_runtime_by_name_and_version( - this.language, - this.version.raw - ); + proc.stdout.on('data', data => { + stdout += data; + }); - if (!found_runtime) { - logger.error( - `Uninstalling ${this.language}-${this.version.raw} failed: Not installed` - ); - throw new Error(`${this.language}-${this.version.raw} is not installed`); + proc.once('error', reject); + }); + + const filtered_env = envout + .split('\n') + .filter( + l => + !['PWD', 'OLDPWD', '_', 'SHLVL'].includes( + l.split('=', 2)[0] + ) + ) + .join('\n'); + + await fs.write_file(path.join(this.install_path, '.env'), filtered_env); + + logger.debug('Changing Ownership of package directory'); + await util.promisify(chownr)(this.install_path, 0, 0); + + logger.debug('Writing installed state to disk'); + await fs.write_file( + path.join(this.install_path, globals.pkg_installed_file), + Date.now().toString() + ); + + logger.info(`Installed ${this.language}-${this.version.raw}`); + + return { + language: this.language, + version: this.version.raw, + }; } - logger.debug('Unregistering runtime'); - found_runtime.unregister(); + async uninstall() { + logger.info(`Uninstalling ${this.language}-${this.version.raw}`); - logger.debug('Cleaning files from disk'); - await fs.rmdir(this.install_path, { recursive: true }); + logger.debug('Finding runtime'); + const found_runtime = runtime.get_runtime_by_name_and_version( + this.language, + this.version.raw + ); - logger.info(`Uninstalled ${this.language}-${this.version.raw}`); + if (!found_runtime) { + logger.error( + `Uninstalling ${this.language}-${this.version.raw} failed: Not installed` + ); + throw new Error( + `${this.language}-${this.version.raw} is not installed` + ); + } - return { - language: this.language, - version: this.version.raw, - }; - } + logger.debug('Unregistering runtime'); + found_runtime.unregister(); - static async get_package_list() { - const repo_content = await fetch(config.repo_url).then((x) => x.text()); + logger.debug('Cleaning files from disk'); + await fs.rmdir(this.install_path, { recursive: true }); - const entries = repo_content.split('\n').filter((x) => x.length > 0); + logger.info(`Uninstalled ${this.language}-${this.version.raw}`); - return entries.map((line) => { - const [language, version, checksum, download] = line.split(',', 4); + return { + language: this.language, + version: this.version.raw, + }; + } - return new Package({ - language, - version, - checksum, - download, - }); - }); - } + static async get_package_list() { + const repo_content = await fetch(config.repo_url).then(x => x.text()); - static async get_package(lang, version) { - const packages = await Package.get_package_list(); + const entries = repo_content.split('\n').filter(x => x.length > 0); - const candidates = packages.filter((pkg) => { - return pkg.language == lang && semver.satisfies(pkg.version, version); - }); + return entries.map(line => { + const [language, version, checksum, download] = line.split(',', 4); - candidates.sort((a, b) => semver.rcompare(a.version, b.version)); + return new Package({ + language, + version, + checksum, + download, + }); + }); + } - return candidates[0] || null; - } + static async get_package(lang, version) { + const packages = await Package.get_package_list(); + + const candidates = packages.filter(pkg => { + return ( + pkg.language == lang && semver.satisfies(pkg.version, version) + ); + }); + + candidates.sort((a, b) => semver.rcompare(a.version, b.version)); + + return candidates[0] || null; + } } module.exports = Package; diff --git a/api/src/runtime.js b/api/src/runtime.js index 418dc42..191fc5d 100644 --- a/api/src/runtime.js +++ b/api/src/runtime.js @@ -8,118 +8,118 @@ const path = require('path'); const runtimes = []; class Runtime { - constructor({ language, version, aliases, pkgdir, runtime }) { - this.language = language; - this.version = version; - this.aliases = aliases || []; - this.pkgdir = pkgdir; - this.runtime = runtime; - } - - static load_package(package_dir) { - let info = JSON.parse( - fss.read_file_sync(path.join(package_dir, 'pkg-info.json')) - ); - - let { language, version, build_platform, aliases, provides } = info; - version = semver.parse(version); - - if (build_platform !== globals.platform) { - logger.warn( - `Package ${language}-${version} was built for platform ${build_platform}, ` + - `but our platform is ${globals.platform}` - ); + constructor({ language, version, aliases, pkgdir, runtime }) { + this.language = language; + this.version = version; + this.aliases = aliases || []; + this.pkgdir = pkgdir; + this.runtime = runtime; } - if (provides) { - // Multiple languages in 1 package - provides.forEach((lang) => { - runtimes.push( - new Runtime({ - language: lang.language, - aliases: lang.aliases, - version, - pkgdir: package_dir, - runtime: language, - }) + static load_package(package_dir) { + let info = JSON.parse( + fss.read_file_sync(path.join(package_dir, 'pkg-info.json')) ); - }); - } else { - runtimes.push( - new Runtime({ - language, - version, - aliases, - pkgdir: package_dir, - }) - ); + + let { language, version, build_platform, aliases, provides } = info; + version = semver.parse(version); + + if (build_platform !== globals.platform) { + logger.warn( + `Package ${language}-${version} was built for platform ${build_platform}, ` + + `but our platform is ${globals.platform}` + ); + } + + if (provides) { + // Multiple languages in 1 package + provides.forEach(lang => { + runtimes.push( + new Runtime({ + language: lang.language, + aliases: lang.aliases, + version, + pkgdir: package_dir, + runtime: language, + }) + ); + }); + } else { + runtimes.push( + new Runtime({ + language, + version, + aliases, + pkgdir: package_dir, + }) + ); + } + + logger.debug(`Package ${language}-${version} was loaded`); } - logger.debug(`Package ${language}-${version} was loaded`); - } + get compiled() { + if (this._compiled === undefined) { + this._compiled = fss.exists_sync(path.join(this.pkgdir, 'compile')); + } - get compiled() { - if (this._compiled === undefined) { - this._compiled = fss.exists_sync(path.join(this.pkgdir, 'compile')); + return this._compiled; } - return this._compiled; - } + get env_vars() { + if (!this._env_vars) { + const env_file = path.join(this.pkgdir, '.env'); + const env_content = fss.read_file_sync(env_file).toString(); - get env_vars() { - if (!this._env_vars) { - const env_file = path.join(this.pkgdir, '.env'); - const env_content = fss.read_file_sync(env_file).toString(); + this._env_vars = {}; - this._env_vars = {}; + env_content + .trim() + .split('\n') + .map(line => line.split('=', 2)) + .forEach(([key, val]) => { + this._env_vars[key.trim()] = val.trim(); + }); + } - env_content - .trim() - .split('\n') - .map((line) => line.split('=', 2)) - .forEach(([key, val]) => { - this._env_vars[key.trim()] = val.trim(); - }); + return this._env_vars; } - return this._env_vars; - } + toString() { + return `${this.language}-${this.version.raw}`; + } - toString() { - return `${this.language}-${this.version.raw}`; - } - - unregister() { - const index = runtimes.indexOf(this); - runtimes.splice(index, 1); //Remove from runtimes list - } + unregister() { + const index = runtimes.indexOf(this); + runtimes.splice(index, 1); //Remove from runtimes list + } } module.exports = runtimes; module.exports.Runtime = Runtime; module.exports.get_runtimes_matching_language_version = function (lang, ver) { - return runtimes.filter( - (rt) => - (rt.language == lang || rt.aliases.includes(lang)) && - semver.satisfies(rt.version, ver) - ); + return runtimes.filter( + rt => + (rt.language == lang || rt.aliases.includes(lang)) && + semver.satisfies(rt.version, ver) + ); }; module.exports.get_latest_runtime_matching_language_version = function ( - lang, - ver + lang, + ver ) { - return module.exports - .get_runtimes_matching_language_version(lang, ver) - .sort((a, b) => semver.rcompare(a.version, b.version))[0]; + return module.exports + .get_runtimes_matching_language_version(lang, ver) + .sort((a, b) => semver.rcompare(a.version, b.version))[0]; }; module.exports.get_runtime_by_name_and_version = function (runtime, ver) { - return runtimes.find( - (rt) => - (rt.runtime == runtime || - (rt.runtime === undefined && rt.language == runtime)) && - semver.satisfies(rt.version, ver) - ); + return runtimes.find( + rt => + (rt.runtime == runtime || + (rt.runtime === undefined && rt.language == runtime)) && + semver.satisfies(rt.version, ver) + ); }; module.exports.load_package = Runtime.load_package; From 543cb11e690b563214dcfce674ef7e4955b7c437 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 8 May 2021 12:41:41 +1200 Subject: [PATCH 09/10] Change package manager request signature --- api/src/api/v2.js | 8 ++++---- api/src/config.js | 2 +- cli/commands/ppman_commands/install.js | 5 ++++- cli/commands/ppman_commands/uninstall.js | 5 ++++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/api/src/api/v2.js b/api/src/api/v2.js index e3e8c68..948dccf 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -163,10 +163,10 @@ router.get('/packages', async (req, res) => { return res.status(200).send(packages); }); -router.post('/packages/:language/:version', async (req, res) => { +router.post('/packages', async (req, res) => { logger.debug('Request to install package'); - const { language, version } = req.params; + const { language, version } = req.body; const pkg = await package.get_package(language, version); @@ -192,10 +192,10 @@ router.post('/packages/:language/:version', async (req, res) => { } }); -router.delete('/packages/:language/:version', async (req, res) => { +router.delete('/packages', async (req, res) => { logger.debug('Request to uninstall package'); - const { language, version } = req.params; + const { language, version } = req.body; const pkg = await package.get_package(language, version); diff --git a/api/src/config.js b/api/src/config.js index acf45ce..84270aa 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -16,7 +16,7 @@ const options = [ }, { key: 'bind_address', - desc: 'Address to bind REST API on\nThank @Bones for the number', + desc: 'Address to bind REST API on', default: '0.0.0.0:2000', validators: [], }, diff --git a/cli/commands/ppman_commands/install.js b/cli/commands/ppman_commands/install.js index 3a71410..4e48c20 100644 --- a/cli/commands/ppman_commands/install.js +++ b/cli/commands/ppman_commands/install.js @@ -12,7 +12,10 @@ const msg_format = { exports.handler = async ({ axios, language, version }) => { try { - const install = await axios.post(`/api/v2/packages/${language}/${version || '*'}`); + const install = await axios.post(`/api/v2/packages`, { + language, + version: version || '*' + }); console.log(msg_format.color(install.data)); } catch ({ response }) { diff --git a/cli/commands/ppman_commands/uninstall.js b/cli/commands/ppman_commands/uninstall.js index 54a8165..1ea9161 100644 --- a/cli/commands/ppman_commands/uninstall.js +++ b/cli/commands/ppman_commands/uninstall.js @@ -12,7 +12,10 @@ const msg_format = { exports.handler = async ({ axios, language, version }) => { try { - const uninstall = await axios.delete(`/api/v2/packages/${language}/${version || '*'}`) + const install = await axios.delete(`/api/v2/packages`, { + language, + version: version || '*' + }); console.log(msg_format.color(uninstall.data)); } catch ({ response }) { From 93188099b7a5f34336b8083b5271044c92a36d39 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 8 May 2021 12:57:37 +1200 Subject: [PATCH 10/10] fix cli --- cli/commands/execute.js | 4 ++-- cli/commands/ppman_commands/install.js | 12 +++++++----- cli/commands/ppman_commands/uninstall.js | 11 ++++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cli/commands/execute.js b/cli/commands/execute.js index f511350..e273548 100644 --- a/cli/commands/execute.js +++ b/cli/commands/execute.js @@ -8,7 +8,7 @@ exports.aliases = ['run']; exports.describe = 'Executes file with the specified runner'; exports.builder = { - version: { + language_version: { string: true, desc: 'Set the version of the language to use', alias: ['l'], @@ -55,7 +55,7 @@ exports.handler = async (argv) => { const request = { language: argv.language, - version: argv['language-version'], + version: argv['language_version'], files: files, args: argv.args, stdin, diff --git a/cli/commands/ppman_commands/install.js b/cli/commands/ppman_commands/install.js index 4e48c20..5d9e92a 100644 --- a/cli/commands/ppman_commands/install.js +++ b/cli/commands/ppman_commands/install.js @@ -1,6 +1,6 @@ const chalk = require('chalk'); -exports.command = ['install [language-version]']; +exports.command = ['install [language_version]']; exports.aliases = ['i']; exports.describe = 'Installs the named package'; @@ -10,12 +10,14 @@ const msg_format = { json: JSON.stringify }; -exports.handler = async ({ axios, language, version }) => { +exports.handler = async ({ axios, language, language_version }) => { try { - const install = await axios.post(`/api/v2/packages`, { + const request = { language, - version: version || '*' - }); + version: language_version || '*' + }; + + const install = await axios.post(`/api/v2/packages`, request); console.log(msg_format.color(install.data)); } catch ({ response }) { diff --git a/cli/commands/ppman_commands/uninstall.js b/cli/commands/ppman_commands/uninstall.js index 1ea9161..c7cbc83 100644 --- a/cli/commands/ppman_commands/uninstall.js +++ b/cli/commands/ppman_commands/uninstall.js @@ -1,6 +1,6 @@ const chalk = require('chalk'); -exports.command = ['uninstall [language-version]']; +exports.command = ['uninstall [language_version]']; exports.aliases = ['u']; exports.describe = 'Uninstalls the named package'; @@ -10,12 +10,13 @@ const msg_format = { json: JSON.stringify }; -exports.handler = async ({ axios, language, version }) => { +exports.handler = async ({ axios, language, language_version }) => { try { - const install = await axios.delete(`/api/v2/packages`, { + const request = { language, - version: version || '*' - }); + version: language_version || '*' + }; + const uninstall = await axios.delete(`/api/v2/packages`, {data: request}); console.log(msg_format.color(uninstall.data)); } catch ({ response }) {