lib/applications/routes.js
'use strict';
/**
* @fileoverview Applications routing functions
*
*/
const config = require('config');
const _ = require('lodash');
const types = require('punchcard-content-types');
const uuid = require('uuid');
const database = require('../database');
const model = require('./model');
const utils = require('../utils');
/**
* All Applications Landing page
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
*
* @returns {boolean} returns true
*/
const all = (req, res) => {
const apps = req.app.get('applications-apps') || [];
res.render('applications/all', {
applications: apps,
config: config.applications,
structure: model.structure,
});
return true;
};
/**
* Add a new Application
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
* @param {object} next - Express callback
*
* @returns {promise} content-types promise
*
* TODO: Content-Types errors need to be vastly expanded to properly use promise catch for error
* https://github.com/punchcard-cms/content-types/issues/112
*/
const add = (req, res, next) => {
const errors = _.get(req.session, 'form.applications.save.errors', {});
const values = _.get(req.session, 'form.applications.save.content', {});
const merged = req.app.get('applications-merged') || {};
_.unset(req.session, 'form.applications.save');
return types.only('applications', values, [merged], config).then(only => {
return types.form(only, errors, config).then(form => {
res.render('applications/add', {
form,
action: `/${config.applications.base}/${config.applications.actions.save}`,
config: config.applications,
structure: model.structure,
button: config.applications.actions.save,
});
});
}).catch(e => {
const err = {
message: e.message,
safe: `/${config.applications.base}`,
status: 500,
};
return next(err);
});
};
/**
* View/edit an individual Application
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
* @param {object} next - Express callback
*
* @returns {promise} resolves/rejects promise
*/
const one = (req, res, next) => {
const id = _.get(req.session, 'form.applications.edit.id', null);
const errors = _.get(req.session, 'form.applications.save.errors', {});
const values = _.get(req.session, 'form.applications.save.content', {});
const merged = req.app.get('applications-merged') || {};
let app = _.get(req.session, 'form.applications.edit.app', null);
_.unset(req.session, 'form.applications.edit');
_.unset(req.session, 'form.applications.save');
// something went wrong on save:
if (Object.keys(values).length > 0 && id) {
// add the previous session data back in
_.set(req.session, 'form.applications.edit', {
id,
});
// grab the edit form and inject errors and values from req.session
return types.only('applications', values, [merged], config).then(only => {
return types.form(only, errors, config).then(form => {
res.render('applications/one', {
form,
action: `/${config.applications.base}/${config.applications.actions.save}`,
config: config.applications,
structure: model.structure,
button: config.applications.actions.update,
app,
});
return true;
});
});
}
// grab the application from the database
return database('applications').where({
id: req.params.id,
}).then(rows => {
// application not in database; send to 404
if (rows.length < 1) {
const err = {
message: config.applications.messages.missing.id.replace('%id', req.params.id),
safe: `/${config.applications.base}`,
status: 404,
};
return next(err);
}
app = rows[0];
app.client = {
id: app['client-id'],
secret: app['client-secret'],
};
// add session data for this application
_.set(req.session, 'form.applications.edit', {
id: rows[0].id,
app,
});
const data = {
'name': {
'text': {
'value': app.name,
},
},
'live-endpoint': {
'url': {
'value': app['live-endpoint'],
},
},
'updated-endpoint': {
'url': {
'value': app['updated-endpoint'],
},
},
'sunset-endpoint': {
'url': {
'value': app['sunset-endpoint'],
},
},
};
return types.only('applications', data, [merged], config);
}).then(only => {
return types.form(only, null, config);
}).then(form => {
res.render('applications/one', {
form,
action: `/${config.applications.base}/${config.applications.actions.save}`,
config: config.applications,
button: config.applications.actions.update,
app,
});
return true;
}).catch(e => {
const err = {
message: e.message,
safe: `/${config.applications.base}`,
status: 404,
};
return next(err);
});
};
/**
* Create a new client secret
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
* @param {object} next - Express callback
*
* @returns {promise} resolves/rejects promise
*/
const secret = (req, res, next) => {
const id = _.get(req.session, 'form.applications.edit.id', null);
const referrer = req.get('Referrer');
const data = {
'client-secret': uuid.v4(),
};
if (!_.includes(referrer, `/${config.applications.base}/${id}`)) {
const err = {
message: 'Secret can only be changed from the application edit screen',
safe: `/${config.applications.base}`,
status: 500,
};
return next(err);
}
return database('applications').where('id', '=', id).update(data).then(() => {
res.redirect(`/${config.applications.base}/${id}`);
return data['client-secret'];
}).catch(e => {
const err = {
message: e.message,
safe: `/${config.applications.base}`,
status: 500,
};
return next(err);
});
};
/**
* View/edit an individual Application
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
*
* @returns {promise} database promise
*/
const save = (req, res) => {
const id = _.get(req.session, 'form.applications.edit.id', null);
const referrer = (_.get(req.session, 'referrer') || req.get('Referrer')) || `/${config.applications.base}`;
const merged = req.app.get('applications-merged') || {};
let apps = req.app.get('applications-apps') || [];
// user hit delete button
if (req.body.submit === config.applications.actions.delete && id) {
return database('applications').where('id', '=', id).del().then(() => {
_.unset(req.session, 'form.applications.edit');
_.unset(req.session, 'form.applications.save');
// remove this app from apps object
apps = apps.filter(app => {
if (app.id === id) {
return false;
}
return true;
});
// replace apps in the application's settings
req.app.set('applications-apps', apps);
res.redirect(`/${config.applications.base}`);
return true;
});
}
// Validation
const validated = types.form.validate(req.body, merged, 'save');
if (validated === true) {
const data = {
'name': req.body['name--text'],
'live-endpoint': req.body['live-endpoint--url'],
'updated-endpoint': req.body['updated-endpoint--url'],
'sunset-endpoint': req.body['sunset-endpoint--url'],
};
// `update` is for the Edit form
if (req.body.submit === config.applications.actions.update && id) {
return database('applications').where('id', '=', id).update(data).returning('*').then((updated) => {
_.unset(req.session, 'form.applications.edit');
_.unset(req.session, 'form.applications.save');
// find this app in the request apps object
const index = apps.findIndex(app => {
return app.id === id;
});
// update this app in the application's settings
apps[index] = updated[0];
req.app.set('applications-apps', apps);
res.redirect(`/${config.applications.base}`);
return true;
});
}
// on add, create fresh id/secret
data['client-id'] = uuid.v4();
data['client-secret'] = uuid.v4();
return database('applications').insert(data).returning('*').then((added) => {
_.unset(req.session, 'form.applications.save');
// add this new app to the request's apps object
apps.push(added[0]);
req.app.set('applications-apps', apps);
res.redirect(`/${config.applications.base}/${added[0].id}`);
return true;
});
}
_.set(req.session, 'form.applications.save', {
errors: validated,
content: utils.format(req.body),
});
return res.redirect(referrer);
};
module.exports = {
all,
add,
one,
secret,
save,
};