Compare commits
No commits in common. "80499237119bce548b84428a5a9c830f08f6e77f" and "ceddf970448184765603f6bc383bd85e937750fe" have entirely different histories.
8049923711
...
ceddf97044
24
app.js
24
app.js
@ -1,15 +1,22 @@
|
|||||||
global['debug'] = (process.env.NODE_ENV === 'debug') ? true : false;
|
// GDS: Global Data System
|
||||||
|
global['gds'] = {
|
||||||
|
debug: (process.env.NODE_ENV === 'debug') ? true : false,
|
||||||
|
db: null,
|
||||||
|
cache: {},
|
||||||
|
cfg: require(__dirname+'/bin/config')
|
||||||
|
};
|
||||||
|
global['modules'] = {};
|
||||||
global['__dirname'] = __dirname;
|
global['__dirname'] = __dirname;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* load modules
|
* load modules
|
||||||
*/
|
*/
|
||||||
const load = (name) => {
|
let load = (name) => {
|
||||||
return require(__dirname+'/bin/'+name+'/module');
|
return require(__dirname+'/bin/'+name+'/module');
|
||||||
};
|
};
|
||||||
|
|
||||||
// environment variable check
|
// environment variable check
|
||||||
const env_vars = ["APP_ID", "APP_SECRET"];
|
let env_vars = ["APP_ID", "APP_SECRET"];
|
||||||
let env_missing = false;
|
let env_missing = false;
|
||||||
env_vars.forEach((el) => {
|
env_vars.forEach((el) => {
|
||||||
if(typeof process.env[el] == 'undefined') {
|
if(typeof process.env[el] == 'undefined') {
|
||||||
@ -19,9 +26,14 @@ env_vars.forEach((el) => {
|
|||||||
});
|
});
|
||||||
if(env_missing) process.exit();
|
if(env_missing) process.exit();
|
||||||
|
|
||||||
global['logs'] = load('logs'); // log handler
|
global['modules'].logs = load('logs'); // log handler
|
||||||
|
global['logs'] = global['modules'].logs; // alias
|
||||||
|
|
||||||
|
global['modules'].web = load('web'); // web server
|
||||||
|
global['modules'].sso = load('sso'); // sso service
|
||||||
|
|
||||||
|
// custom modules
|
||||||
|
|
||||||
const webServer = load('web'); // web server
|
|
||||||
|
|
||||||
// start web server
|
// start web server
|
||||||
webServer.start();
|
global['modules'].web.start();
|
||||||
|
@ -9,18 +9,17 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
web: {
|
web: {
|
||||||
host: "*",
|
host: "*",
|
||||||
port: 8081,
|
port: 8080,
|
||||||
poweredBy: 'baseApp',
|
poweredBy: 'baseApp',
|
||||||
rootUrl: '/', // '/', '/SSObaseApp/'
|
rootUrl: '/', // '/', '/SSObaseApp'
|
||||||
doubleSlashCheck: true, // replacing // with /
|
doubleSlashCheck: true, // replacing // with /
|
||||||
sessionKey: require('crypto').randomBytes(32).toString('hex'),
|
sessionKey: require('crypto').randomBytes(32).toString('hex'),
|
||||||
cookieKey: require('crypto').randomBytes(32).toString('hex'),
|
cookieKey: require('crypto').randomBytes(32).toString('hex'),
|
||||||
rememberMeMaxAge: 1000*60*60 // two weeks (milliseconds*seconds*minutes)
|
rememberMeMaxAge: 1000*60*60 // two weeks (milliseconds*seconds*minutes)
|
||||||
},
|
},
|
||||||
sso: {
|
sso: {
|
||||||
authenticator: "http://localhost:8081/api/authenticate", // redirect back to this url;
|
authenticator: "http://localhost:8080/api/authenticate", // redirect back to this url;
|
||||||
providerApi: "http://localhost:8080/api/authenticate", // sso service api
|
provider: "https://auth.rxbn.de/authenticate", // sso service
|
||||||
providerUI: "http://localhost:8080/authenticate", // sso service
|
|
||||||
appId: process.env.APP_ID, // app id
|
appId: process.env.APP_ID, // app id
|
||||||
appSecret: process.env.APP_SECRET // random token
|
appSecret: process.env.APP_SECRET // random token
|
||||||
},
|
},
|
||||||
|
@ -2,13 +2,12 @@ var methods = {};
|
|||||||
let fs = require('fs');
|
let fs = require('fs');
|
||||||
let util = require('util');
|
let util = require('util');
|
||||||
|
|
||||||
var cfg = require(global['__dirname']+'/bin/config');
|
|
||||||
|
|
||||||
// save new line to file
|
// save new line to file
|
||||||
newLine = (prefix, obj) => {
|
newLine = (prefix, obj) => {
|
||||||
let date = new Date(); // current date
|
let date = new Date(); // current date
|
||||||
let filename = cfg.log.filename(); // filename
|
let filename = global['gds'].cfg.log.filename(); // filename
|
||||||
let dir = cfg.log.directory(); // directory
|
let dir = global['gds'].cfg.log.directory(); // directory
|
||||||
let path = dir + filename; // filepath
|
let path = dir + filename; // filepath
|
||||||
let fs_options = { // fs options for encoding, file mode and file flag
|
let fs_options = { // fs options for encoding, file mode and file flag
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
@ -47,7 +46,7 @@ newLine = (prefix, obj) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
log = (fn, ...data) => {
|
fallback = (fn, ...data) => {
|
||||||
if(data.length == 1) data = data[0];
|
if(data.length == 1) data = data[0];
|
||||||
|
|
||||||
fn.apply(null, data);
|
fn.apply(null, data);
|
||||||
@ -56,7 +55,8 @@ log = (fn, ...data) => {
|
|||||||
|
|
||||||
// LOG | INFO
|
// LOG | INFO
|
||||||
methods.log = (...data) => {
|
methods.log = (...data) => {
|
||||||
log(console.log, data);
|
if(global['modules'].cli) global['modules'].cli.log.apply(global['modules'].cli, data);
|
||||||
|
else fallback(console.log, data);
|
||||||
|
|
||||||
if(data.length == 1) data = data[0];
|
if(data.length == 1) data = data[0];
|
||||||
newLine(" [LOG]", data);
|
newLine(" [LOG]", data);
|
||||||
@ -65,7 +65,8 @@ methods.info = methods.log;
|
|||||||
|
|
||||||
// WARNING
|
// WARNING
|
||||||
methods.warn = (...data) => {
|
methods.warn = (...data) => {
|
||||||
log(console.warn, data);
|
if(global['modules'].cli) global['modules'].cli.log.apply(global['modules'].cli, data);
|
||||||
|
else fallback(console.warn, data);
|
||||||
|
|
||||||
if(data.length == 1) data = data[0];
|
if(data.length == 1) data = data[0];
|
||||||
newLine(" [WARN]", data);
|
newLine(" [WARN]", data);
|
||||||
@ -73,7 +74,8 @@ methods.warn = (...data) => {
|
|||||||
|
|
||||||
// ERROR
|
// ERROR
|
||||||
methods.error = (...data) => {
|
methods.error = (...data) => {
|
||||||
log(console.error, data);
|
if(global['modules'].cli) global['modules'].cli.log.apply(global['modules'].cli, data);
|
||||||
|
else fallback(console.error, data);
|
||||||
|
|
||||||
if(data.length == 1) data = data[0];
|
if(data.length == 1) data = data[0];
|
||||||
newLine("[ERROR]", data);
|
newLine("[ERROR]", data);
|
||||||
@ -82,8 +84,9 @@ methods.err = methods.error;
|
|||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
methods.debug = (...data) => {
|
methods.debug = (...data) => {
|
||||||
if(global['debug'] === true) {
|
if(global['gds'].debug) {
|
||||||
log(console.log, data);
|
if(global['modules'].cli) global['modules'].cli.log.apply(global['modules'].cli, data);
|
||||||
|
else fallback(console.log, data);
|
||||||
|
|
||||||
if(data.length == 1) data = data[0];
|
if(data.length == 1) data = data[0];
|
||||||
newLine("[DEBUG]", data);
|
newLine("[DEBUG]", data);
|
||||||
|
@ -6,38 +6,42 @@
|
|||||||
* For the full copyright and license information, please view the LICENSE
|
* For the full copyright and license information, please view the LICENSE
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
const methods = {};
|
var functions = {};
|
||||||
const bent = require('bent');
|
var request = require('request');
|
||||||
const querystring = require('querystring');
|
var querystring = require('querystring');
|
||||||
|
|
||||||
const cfg = require(global['__dirname']+'/bin/config');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* authenticate user
|
* authenticate user
|
||||||
* @author Ruben Meyer
|
* @author Ruben Meyer
|
||||||
* @async
|
* @param {Object} obj obj (userId, userToken, appId, appSecret)
|
||||||
* @param {Object} obj obj (userId, token, appId, appSecret)
|
* @param {Function} callback obj (err, access => true, false)
|
||||||
* @return {Object} callback obj (err, access => {true, false})
|
|
||||||
*/
|
*/
|
||||||
methods.authenticateUser = async (obj, callback) => {
|
functions.authenticateUser = (obj, callback) => {
|
||||||
let request = bent(cfg.sso.providerApi, 'POST', 'json', 200);
|
if(typeof callback !== 'function') callback = function() {};
|
||||||
|
|
||||||
try {
|
request({
|
||||||
let post = await request('', {
|
method: 'POST',
|
||||||
|
uri: global['gds'].cfg.sso.provider,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
applicationId: obj.appId,
|
applicationId: obj.appId,
|
||||||
applicationSecret: obj.appSecret,
|
applicationSecret: obj.appSecret,
|
||||||
userId: obj.userId,
|
userId: obj.userId,
|
||||||
token: obj.token
|
token: obj.userToken
|
||||||
});
|
})
|
||||||
|
}, (err, res, body) => {
|
||||||
if(post.status == 200 && post.message == "msg.auth.authentication.successful")
|
console.log('error:', err); // Print the error if one occurred
|
||||||
return true;
|
console.log('statusCode:', res && res.statusCode); // Print the response status code if a response was received
|
||||||
} catch(err) {
|
console.log('body:', body); // Print the HTML
|
||||||
// something went wrong
|
try {
|
||||||
console.log(err);
|
if(typeof body !== "object") body = JSON.parse(body);
|
||||||
return false;
|
return callback(err, body.access);
|
||||||
|
} catch(e) {
|
||||||
|
callback(new Error("Body is misformed"));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,12 +51,12 @@ methods.authenticateUser = async (obj, callback) => {
|
|||||||
* @param {Object} obj obj(url, appId)
|
* @param {Object} obj obj(url, appId)
|
||||||
* @param {Function} callback string(url)
|
* @param {Function} callback string(url)
|
||||||
*/
|
*/
|
||||||
methods.createAuthentication = (obj) => {
|
functions.createAuthentication = (obj) => {
|
||||||
let nUrl = {
|
let nUrl = {
|
||||||
redirectUrl: obj.url,
|
redirectUrl: obj.url,
|
||||||
appId: obj.appId
|
appId: obj.appId
|
||||||
};
|
};
|
||||||
return cfg.sso.providerUI+"?"+querystring.stringify(nUrl);
|
return global['gds'].cfg.sso.provider+"?"+querystring.stringify(nUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1,27 +1,19 @@
|
|||||||
var express = require('express');
|
var express = require('express');
|
||||||
var route = express.Router();
|
var route = express.Router();
|
||||||
|
|
||||||
const cfg = require(global['__dirname']+'/bin/config');
|
|
||||||
|
|
||||||
route.use((req, res, next) => {
|
route.use((req, res, next) => {
|
||||||
if(!req.session || !req.session.user) {
|
if(!req.session || !req.session.user) {
|
||||||
if(!req.path.startsWith('/api')) {
|
if(!req.path.startsWith('/api')) {
|
||||||
let path = (cfg.web.rootUrl+'/auth');
|
let path = (global['gds'].cfg.web.rootUrl+'/auth');
|
||||||
|
|
||||||
if(cfg.web.doubleSlashCheck) path = path.replace(/\/+/g, "/");
|
if(global['gds'].cfg.web.doubleSlashCheck) path = path.replace(/\/+/g, "/");
|
||||||
|
|
||||||
res.redirect(path);
|
res.redirect(path);
|
||||||
} else {
|
} else {
|
||||||
res.redirect('/auth'+req.path);
|
res.redirect('/auth'+req.path);
|
||||||
}
|
}
|
||||||
} else {
|
} else next();
|
||||||
// initialize User
|
|
||||||
if(req.session.user.initializeUser) {
|
|
||||||
// do it
|
|
||||||
req.session.user.initializeUser = false;
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
route.get('/', (req, res) => {
|
route.get('/', (req, res) => {
|
||||||
|
@ -1,35 +1,18 @@
|
|||||||
var express = require('express');
|
var express = require('express');
|
||||||
var route = express.Router();
|
var route = express.Router();
|
||||||
|
|
||||||
const cfg = require(global['__dirname']+'/bin/config');
|
|
||||||
const sso = require(global['__dirname']+'/bin/sso/module');
|
|
||||||
|
|
||||||
route.get('/login', (req, res) => {
|
route.get('/login', (req, res) => {
|
||||||
// TODO: login
|
// TODO: login
|
||||||
let a = sso.createAuthentication({
|
let a = global['modules'].sso.createAuthentication({
|
||||||
url: cfg.sso.authenticator,
|
url: global['gds'].cfg.sso.authenticator,
|
||||||
appId: cfg.sso.appId
|
appId: global['gds'].cfg.sso.appId
|
||||||
});
|
});
|
||||||
res.redirect(a);
|
res.redirect(a);
|
||||||
});
|
});
|
||||||
|
|
||||||
route.get('/authenticate', async (req, res) => {
|
route.get('/authenticate', (req, res) => {
|
||||||
if(req.query && req.query.uid && req.query.token) {
|
// TODO: authenticate
|
||||||
let auth = await sso.authenticateUser({
|
res.end();
|
||||||
userId: req.query.uid,
|
|
||||||
token: req.query.token,
|
|
||||||
appId: cfg.sso.appId,
|
|
||||||
appSecret: cfg.sso.appSecret
|
|
||||||
});
|
|
||||||
if(auth) {
|
|
||||||
req.session.user = {
|
|
||||||
ssoId: req.query.uid,
|
|
||||||
initializeUser: true
|
|
||||||
};
|
|
||||||
return res.redirect(cfg.web.rootUrl);
|
|
||||||
}
|
|
||||||
else return res.redirect(cfg.web.rootUrl + 'auth/login');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
route.get('/logout', (req, res) => {
|
route.get('/logout', (req, res) => {
|
||||||
@ -48,7 +31,7 @@ route.get('/logout', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(global['debug']) {
|
if(global['gds'].debug) {
|
||||||
// DEBUG info
|
// DEBUG info
|
||||||
route.get('/info', (req, res) => {
|
route.get('/info', (req, res) => {
|
||||||
let obj = {};
|
let obj = {};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
var express = require('express');
|
var express = require('express');
|
||||||
var route = express.Router();
|
var route = express.Router();
|
||||||
|
|
||||||
const cfg = require(global['__dirname']+'/bin/config');
|
|
||||||
|
|
||||||
route.all('/', function(req, res, next) {
|
route.all('/', function(req, res, next) {
|
||||||
// TODO: show login page or dashboard
|
// TODO: show login page or dashboard
|
||||||
@ -18,9 +17,7 @@ route.all('/*', (req, res, next) => {
|
|||||||
// TODO: role-based authorization
|
// TODO: role-based authorization
|
||||||
// TODO: show login page or page
|
// TODO: show login page or page
|
||||||
|
|
||||||
res.render('auth/views/index', {
|
res.end('500 - LEL');
|
||||||
appName: cfg.app.name
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = route;
|
module.exports = route;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//- variables
|
//- variables
|
||||||
- var appName = appName || "SSObaseApp";
|
- var appName = global['gds'].cfg.app.name || "SSObaseApp";
|
||||||
- var title = "Dashboard";
|
- var title = "Dashboard";
|
||||||
|
|
||||||
mixin navItem(name, id, symbol, href)
|
mixin navItem(name, id, symbol, href)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
// init
|
// init
|
||||||
const methods = {};
|
var methods = {};
|
||||||
|
|
||||||
const cfg = require(global['__dirname']+'/bin/config');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* start web server
|
* start web server
|
||||||
@ -29,7 +27,7 @@ methods.start = () => {
|
|||||||
// Access Control Headers
|
// Access Control Headers
|
||||||
app.use( (req, res, next) => {
|
app.use( (req, res, next) => {
|
||||||
res.set({
|
res.set({
|
||||||
'X-Powered-By': cfg.web.poweredBy
|
'X-Powered-By': global['gds'].cfg
|
||||||
});
|
});
|
||||||
res.header("Access-Control-Allow-Origin", "*");
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||||
@ -41,14 +39,14 @@ methods.start = () => {
|
|||||||
app.use(bp.urlencoded({
|
app.use(bp.urlencoded({
|
||||||
extended: true
|
extended: true
|
||||||
}));
|
}));
|
||||||
app.use(cp(cfg.web.cookieKey));
|
app.use(cp(global['gds'].cfg.web.cookieKey));
|
||||||
|
|
||||||
// Pretty print
|
// Pretty print
|
||||||
app.locals.pretty = true;
|
app.locals.pretty = true;
|
||||||
|
|
||||||
// Sessions
|
// Sessions
|
||||||
session_options = {
|
session_options = {
|
||||||
secret: cfg.web.sessionKey,
|
secret: global['gds'].cfg.web.sessionKey,
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false, cookie: {}};
|
saveUninitialized: false, cookie: {}};
|
||||||
if(app.get('env') === 'production') {
|
if(app.get('env') === 'production') {
|
||||||
@ -58,6 +56,8 @@ methods.start = () => {
|
|||||||
|
|
||||||
//static files
|
//static files
|
||||||
app.use('/res', (req, res, next) => {
|
app.use('/res', (req, res, next) => {
|
||||||
|
if(typeof global['gds'].cache.web == 'undefined') global['gds'].cache.web = {};
|
||||||
|
|
||||||
let dir = global['__dirname'] + '/res/web';
|
let dir = global['__dirname'] + '/res/web';
|
||||||
|
|
||||||
|
|
||||||
@ -65,7 +65,19 @@ methods.start = () => {
|
|||||||
else dir += '/app';
|
else dir += '/app';
|
||||||
|
|
||||||
let joined_path = path.join(dir, /^[^?]+/.exec(req.url)[0]);
|
let joined_path = path.join(dir, /^[^?]+/.exec(req.url)[0]);
|
||||||
|
|
||||||
|
// path already cached; not exist
|
||||||
|
if(global['gds'].cache.web[joined_path] == false) {
|
||||||
|
res.status(404).end();
|
||||||
|
// path already cached; exist
|
||||||
|
} else if(global['gds'].cache.web[joined_path] == true){
|
||||||
|
let contentType = mime.contentType(path.extname(joined_path));
|
||||||
|
res.setHeader('Content-Type', contentType);
|
||||||
|
fs.createReadStream(joined_path).pipe(res);
|
||||||
|
// check path
|
||||||
|
} else {
|
||||||
fs.exists(joined_path, (exists) => {
|
fs.exists(joined_path, (exists) => {
|
||||||
|
global['gds'].cache.web[joined_path] = exists;
|
||||||
if(exists) {
|
if(exists) {
|
||||||
let contentType = mime.contentType(path.extname(joined_path));
|
let contentType = mime.contentType(path.extname(joined_path));
|
||||||
res.setHeader('Content-Type', contentType);
|
res.setHeader('Content-Type', contentType);
|
||||||
@ -75,6 +87,7 @@ methods.start = () => {
|
|||||||
res.status(404).end();
|
res.status(404).end();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// web routes
|
// web routes
|
||||||
@ -82,9 +95,44 @@ methods.start = () => {
|
|||||||
app.use('/', require(global['__dirname']+'/bin/web/app/routes/main'));
|
app.use('/', require(global['__dirname']+'/bin/web/app/routes/main'));
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
app.listen(cfg.web.port, () => {
|
app.listen(global['gds'].cfg.web.port, () => {
|
||||||
global['logs'].log("Server is listening on port: "+cfg.web.port);
|
global['modules'].logs.log("Server is listening on port: "+global['gds'].cfg.web.port);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// DEBUG OUTPUT: list all routes with HTTP method
|
||||||
|
setTimeout(function () {
|
||||||
|
function print (path, layer) {
|
||||||
|
if (layer.route) {
|
||||||
|
layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path))))
|
||||||
|
} else if (layer.name === 'router' && layer.handle.stack) {
|
||||||
|
layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp))))
|
||||||
|
} else if (layer.method) {
|
||||||
|
console.log('%s /%s',
|
||||||
|
layer.method.toUpperCase(),
|
||||||
|
path.concat(split(layer.regexp)).filter(Boolean).join('/')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function split (thing) {
|
||||||
|
if (typeof thing === 'string') {
|
||||||
|
return thing.split('/')
|
||||||
|
} else if (thing.fast_slash) {
|
||||||
|
return ''
|
||||||
|
} else {
|
||||||
|
var match = thing.toString()
|
||||||
|
.replace('\\/?', '')
|
||||||
|
.replace('(?=\\/|$)', '$')
|
||||||
|
.match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)
|
||||||
|
|
||||||
|
return match
|
||||||
|
? match[1].replace(/\\(.)/g, '$1').split('/')
|
||||||
|
: '<complex:' + thing.toString() + '>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app._router.stack.forEach(print.bind(null, []))
|
||||||
|
}, 1500);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = methods;
|
module.exports = methods;
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
"author": "rxbn_",
|
"author": "rxbn_",
|
||||||
"license": "",
|
"license": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bent": "^7.3.10",
|
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"cookie-parser": "^1.4.5",
|
"cookie-parser": "^1.4.4",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-session": "^1.17.1",
|
"express-session": "^1.16.2",
|
||||||
"pug": "^3.0.0"
|
"pug": "^2.0.4",
|
||||||
|
"request": "^2.88.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user