#!/usr/bin/env node const path = require('path'); const fs = require('fs'); const monk = require('monk'); const express = require('express'); let serverInstance; const requireFromString = require('require-from-string'); const userConfigPath = path.join(process.env.HOME, '.faas.config'); require('dotenv').config({ path: userConfigPath, }); let mongoURL = process.env.MONGO_URL; const chalk = require('chalk'); const vorpal = require('vorpal')(); vorpal.delimiter(chalk.blue('faas $')); const showMongoNotSetError = v => { v.log(chalk.red('MongoDB URL is not set or is invalid. The URL is needed to authorize the CLI.')); v.log(chalk.red('Please use the command "authorize " to set the URL.')); } const checkConnection = async (url, v) => { return new Promise((resolve, reject) => { let db; try { db = monk(url); } catch (err) { v.log(chalk.red('Failed to authorize the CLI! Please review the Mongo URL.')); v.log(chalk.red(String(err))); reject(err); } db.then(async () => { models = require('./models')(db); await models.endpoints.stats(); resolve(); }).catch(err => { v.log(chalk.red('Failed to authorize the CLI! Please review the Mongo URL.')); v.log(chalk.red(String(err))); reject(err); }); }); } let db, models; (async function() { try { await checkConnection(mongoURL, vorpal); db = monk(mongoURL); models = require('./models')(db); } catch (err) { showMongoNotSetError(vorpal); } })(); vorpal .command('authorize ', 'Set the MongoDB URL to authorize the CLI.') .action(async function(args, callback) { if (!args.url) { this.log('Please give the Mongo URL.'); return callback(); } const row = `MONGO_URL=${args.url}`; fs.writeFileSync(userConfigPath, row); try { await checkConnection(args.url, this); this.log(chalk.green('CLI successfully authorized!')); mongoURL = args.url; } catch (err) { mongoURL = null; } }); vorpal .command('create ', 'Create new endpoint called "name".') .action(async function(args, callback) { try { await checkConnection(mongoURL, this); } catch (err) { showMongoNotSetError(vorpal); return callback(); } if (!args.name) { this.log('Please give a name for the endpoint.'); return callback(); } try { await models.endpoints.insert({ name: args.name, code: `module.exports = function (req, res) { res.send('Hello ${args.name}!') }`, }); this.log(`Created endpoint ${args.name}.`); } catch (err) { this.log(chalk.red('Failed to create endpoint.')); if (String(err).includes('E11000')) { this.log('Endpoint names must be unique.'); } else { this.log(String(err)); } } callback(); }); vorpal .command('remove ', 'Remove endpoint called "name".') .action(async function(args, callback) { try { await checkConnection(mongoURL, this); } catch (err) { showMongoNotSetError(vorpal); return callback(); } if (!args.name) { this.log('Please give a name for the endpoint.'); return callback(); } try { await models.endpoints.remove({ name: args.name, }); } catch (err) { this.log(chalk.red(`Failed to remove endpoint ${args.name}!`)); this.log(chalk.red(String(err))); } this.log(`Removed endpoint ${args.name}.`); callback(); }); vorpal .command('list', 'List all endpoints.') .action(async function(args, callback) { try { await checkConnection(mongoURL, this); } catch (err) { showMongoNotSetError(vorpal); return callback(); } const endpoints = await models.endpoints.find({}); endpoints.forEach(endpoint => { this.log(`${chalk(endpoint.name)}`); }) callback(); }); vorpal .command('upload ', 'Upload "file" to endpoint "endpoint".') .action(async function(args, callback) { try { await checkConnection(mongoURL, this); } catch (err) { showMongoNotSetError(vorpal); return callback(); } if (!args.file || !args.endpoint) { this.log('Please provide both "file" and "endpoint" as arguments.'); return callback(); } const existing = await models.endpoints.findOne({ name: args.endpoint }); if (!existing) { this.log(chalk.red(`Endpoint ${args.endpoint} doesn't exist!`)); return callback(); } try { this.log(chalk.yellow(`Uploading function from file "${args.file}"...`)); const data = fs.readFileSync(path.join(process.cwd(), args.file), 'utf-8'); await models.endpoints.update( { name: args.endpoint }, { code: data, name: args.endpoint }, { upsert: true }); this.log(chalk.green(`Uploaded function successfully to endpoint ${args.endpoint}!`)); } catch (err) { this.log(chalk.red(`Failed to upload to endpoint ${args.endpoint}!`)); this.log(chalk.red(String(err))); } callback(); }); vorpal .command('start dev ', 'Run a function locally for development purposes.') .action(async function(args, callback) { if (!args.file ) { this.log('Please provide the "file" to run.'); return callback(); } const code = fs.readFileSync(path.join(process.cwd(), args.file), 'utf-8'); if (serverInstance) { serverInstance.close(); } const app = express(); const func = requireFromString(code); app.all('*', async (req, res) => { func(req, res); }); const port = 1337; serverInstance = app.listen(port, () => console.log(`${chalk.blue('debug server')} listening on port ${port}! URL: ${chalk.yellow(`http://localhost:${port}`)}`) ); callback(); }); vorpal .command('stop dev', 'Stop the development server.') .action(async function(args, callback) { if (serverInstance) { serverInstance.close(); this.log(chalk.green('Server stopped.')); } else { this.log(chalk.red('Server is not running.')); } callback(); }); vorpal .command('info', 'Show information about the session') .action(async function(args, callback) { this.log('Mongo URL: ' + chalk.yellow(mongoURL)); callback(); }); vorpal.show();