1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
require('dotenv').config();
const path = require('path');
const chalk = require('chalk');
const npm = require('npm');
const intercept = require('intercept-stdout');
if (!process.env.MONGO_URL) {
throw new Error('No MONGO_URL set in .env!');
}
const db = require('monk')(process.env.MONGO_URL);
const express = require('express');
const app = express();
const requireFromString = require('require-from-string');
const models = require('./models')(db);
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '/frontpage.html'));
});
app.all('/:path*', async (req, res) => {
const path = req.params.path.split('/')[0];
if (!path || path.length === 0) {
res.status(400);
return res.send('Empty endpoint.');
}
const endpoint = await models.endpoints.findOne({name: path});
if (!endpoint) {
res.status(400);
return res.send('No such endpoint.');
}
const secretsList = await models.secrets.find();
if (!secretsList) {
res.status(500);
return res.send('Failed to fetch secrets from the database.');
}
const secrets = secretsList.reduce((prev, cur) => ({
...prev,
[cur.key]: cur.value
}), {});
console.info(`${chalk.green(req.path)}, running endpoint "${chalk.yellow(endpoint.name)}".`);
const {code} = endpoint;
try {
const func = requireFromString(code);
func(req, res, secrets);
} catch (err) {
res.status(500);
console.error(chalk.red(`Error in endpoint ${endpoint.name}! Details below.`));
console.error(err);
res.send(err);
}
});
const npmLoad = () => new Promise((resolve, reject) => {
npm.load(function(err) {
if (err) {
reject(err);
}
npm.on('log', function(message) {
// log installation progress
//console.log(message);
});
resolve();
});
});
const npmList = () => new Promise((resolve, reject) => {
const unhook_intercept = intercept(txt => "");
npm.config.set('depth', 0);
npm.commands.list([], function(err, data) {
unhook_intercept();
if (err) {
reject(err);
}
resolve(Object.keys(data._dependencies));
});
});
const npmInstall = (packages) => new Promise((resolve, reject) => {
const unhook_intercept = intercept(txt => '');
npm.config.set('progress', 'false');
npm.config.set('silent', 'true');
npm.commands.install(packages, function(err, data) {
unhook_intercept();
if (err) {
reject(err);
}
resolve(data);
});
});
async function installPackagesFromDatabase(showInfo = false) {
try {
await npmLoad();
// handle errors
const packagesToInstall = await models.packages.find({});
const packages = packagesToInstall.map(obj => obj.name.trim());
const installedPackages = await npmList();
packages.forEach(async pkg => {
if (installedPackages.some(installedPkg => installedPkg.includes(pkg))) {
if (showInfo) {
console.info(`"${pkg}" Already installed, skipping...`);
}
return;
}
console.info(`Installing package "${pkg}"...`);
// install module
await npmInstall([pkg]);
});
if (showInfo) {
console.info(`Successfully installed ${packages.length} NPM packages programmatically.`);
}
} catch (err) {
console.error(`Failed to install NPM packages programmatically.`);
console.error(err);
}
}
const packagePollInterval = process.env.PACKAGE_POLL_INTERVAL || 60000; // 1 minute
installPackagesFromDatabase(true);
setInterval(installPackagesFromDatabase, packagePollInterval);
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`${chalk.blue('faas')} listening on port ${port}! URL: ${chalk.yellow(`http://localhost:${port}`)}`));
|