UNPKG

node-mongo-admin

Version:

A simple web application to visualize mongo data inspired by PHPMyAdmin

155 lines (139 loc) 3.86 kB
/** * @file Load *.js files in the api folder and setup express * Each file should export: * * @property {Object} fields The request data format. See validate-fields for details * * @callback handler * @param {Object} dbs A map of mongodb connections (indexed by connection name) * @param {Object} body The request body * @param {successCallback} success A callback to answer the request with success * @param {errorCallback} error A callback to answer the request with error */ /** * Success and error callbacks: * * @callback successCallback * @param {Object} [response={}] The response object (default fields like 'status' will be put automatically) * * @callback errorCallback * @param {(number|Error)} error An error instance or code (like 101, 200) * @param {string} [message=''] The error message */ 'use strict' let express = require('express'), fs = require('fs'), validate = require('validate-fields')(), dbs = require('./dbs'), json = require('./json') /** * Setup API routes and call done(err, api) when done * @param {Function} done */ module.exports = function (done) { // Connect to mongo dbs((err, dbs) => { if (err) { return done(err) } let api = new express.Router() api.use((req, res, next) => { if (!req.is('json')) { return next(new APIError(101, 'Invalid Content-Type header, application/json was expected')) } next() }) // JSON parser api.use(require('body-parser').json({ reviver: json.reviver })) // End points fs.readdirSync('./api').forEach(item => { if (item.substr(-3).toLowerCase() === '.js') { processFile('./api/' + item, '/' + item.substr(0, item.length - 3), api, dbs) } }) // Error handler // next isn't used on purpose, because express demands a 4-arity function // eslint-disable-next-line no-unused-vars api.use((err, req, res, _) => { let code, message if (err instanceof APIError) { code = err.code message = err.message } else { console.log(err.stack) code = 100 message = err.message || 'Unknown error' } res.json({ error: { code, message } }) }) done(null, api) }) } /** * @class * @param {number} code * @param {string} message */ function APIError(code, message) { this.code = code this.message = message } /** * Process a file as an api end point * @param {string} fileName * @param {string} url * @param {Express} api * @param {Object.<String, Db>} dbs * @throws */ function processFile(fileName, url, api, dbs) { let file = require(fileName), fields = validate.parse(file.fields) api.post(url, (req, res, next) => { // Fields validation if (fields.validate(req.body)) { return next() } next(new APIError(101, fields.lastError)) }, wrapHandler(file.handler, dbs)) } /** * Return an express middleware for the given API endpoint handler * @param {handler} handler * @param {Object.<String, Db>} dbs * @returns {function} */ function wrapHandler(handler, dbs) { return function (req, res, next) { // Filter allowed dbs let allowedDbs if (!req.user.connections) { allowedDbs = dbs } else { allowedDbs = Object.create(null) req.user.connections.forEach(conn => { if (conn in dbs) { allowedDbs[conn] = dbs[conn] } }) } handler(allowedDbs, req.body, response => { response = response || {} if (typeof response !== 'object') { throw new Error('The response must be an object') } response.error = null response = json.stringify(response) res.set('Content-Type', 'application/json').send(response) }, (error, msg) => { next(typeof error === 'number' ? new APIError(error, msg || '') : error) }) } }