const express = require('express')
const AdminBro = require('admin-bro')
const path = require('path')
const formidableMiddleware = require('express-formidable')
const pkg = require('./package.json')
let session
try {
session = require('express-session') // eslint-disable-line global-require
} catch (e) {
console.info('express-session was not required')
}
/**
* Builds the Express Router that handles all the pages and assets
*
* @param {AdminBro} admin instance of AdminBro
* @param {express.Router} [predefinedRouter] Express.js router
* @return {express.Router} Express.js router
* @function
* @static
* @memberof module:admin-bro-expressjs
*/
const buildRouter = (admin, predefinedRouter) => {
if (!admin || admin.constructor.name !== 'AdminBro') {
const e = new Error('you have to pass an instance of AdminBro to the buildRouter() function')
e.name = 'WrongArgumentError'
throw e
}
admin.initialize().then(() => {
console.log('AdminBro: bundle ready')
})
const { routes, assets } = AdminBro.Router
const router = predefinedRouter || express.Router()
router.use(formidableMiddleware())
routes.forEach((route) => {
// we have to change routes defined in AdminBro from {recordId} to :recordId
const expressPath = route.path.replace(/{/g, ':').replace(/}/g, '')
const handler = async (req, res) => {
try {
const controller = new route.Controller({ admin }, req.session && req.session.adminUser)
const { params, query } = req
const method = req.method.toLowerCase()
const payload = {
...(req.fields || {}),
...(req.files || {}),
}
const html = await controller[route.action]({
...req, params, query, payload, method,
}, res)
if (route.contentType) {
res.set({ 'Content-Type': route.contentType })
}
if (html) {
res.send(html)
}
} catch (e) {
// eslint-disable-next-line no-console
console.log(e)
}
}
if (route.method === 'GET') {
router.get(expressPath, handler)
}
if (route.method === 'POST') {
router.post(expressPath, handler)
}
})
assets.forEach((asset) => {
router.get(asset.path, async (req, res) => {
res.sendFile(path.resolve(asset.src))
})
})
return router
}
/**
* @typedef {Function} Authenticate
* @memberof module:admin-bro-expressjs
* @description
* function taking 2 arguments email and password
* @param {string} [email] email given in the form
* @param {string} [password] password given in the form
* @return {CurrentAdmin | null} returns current admin or null
*/
/**
* Builds the Express Router which is protected by a session auth
*
* Using the router requires you to install `express-session` as a
* dependency.
*
* @param {AdminBro} admin instance of AdminBro
* @param {Object} auth authentication options
* @param {module:admin-bro-expressjs.Authenticate} auth.authenticate authenticate function
* @param {String} auth.cookiePassword secret used to encrypt cookies
* @param {String} auth.cookieName=adminbro cookie name
* @param {express.Router} [predefinedRouter] Express.js router
* @param {session.options} [sessionOptions] Options that are passed to [express-session](https://github.com/expressjs/session)
* @return {express.Router} Express.js router
* @static
* @memberof module:admin-bro-expressjs
* @example
* const ADMIN = {
* email: '[email protected]',
* password: 'password',
* }
*
* AdminBroExpress.buildAuthenticatedRouter(adminBro, {
* authenticate: async (email, password) => {
* if (ADMIN.password === password && ADMIN.email === email) {
* return ADMIN
* }
* return null
* },
* cookieName: 'adminbro',
* cookiePassword: 'somepassword',
* }, [router])
*/
const buildAuthenticatedRouter = (admin, auth, predefinedRouter, sessionOptions = {}) => {
if (!session) {
throw new Error(['In order to use authentication, you have to install',
' express-session package'].join(' '))
}
const router = predefinedRouter || express.Router()
router.use(session({
...sessionOptions,
secret: auth.cookiePassword,
name: auth.cookieName || 'adminbro',
}))
router.use(formidableMiddleware())
const { rootPath } = admin.options
let { loginPath, logoutPath } = admin.options
loginPath = loginPath.replace(rootPath, '')
logoutPath = logoutPath.replace(rootPath, '')
router.get(loginPath, async (req, res) => {
const login = await AdminBro.renderLogin({ action: admin.options.loginPath })
res.send(login)
})
router.post(loginPath, async (req, res) => {
const { email, password } = req.fields
const adminUser = await auth.authenticate(email, password)
if (adminUser) {
req.session.adminUser = adminUser
res.redirect(rootPath)
} else {
const login = await AdminBro.renderLogin({
action: admin.options.loginPath,
errorMessage: 'Invalid credentials!',
})
res.send(login)
}
})
router.use((req, res, next) => {
if (AdminBro.Router.assets.find(asset => req.originalUrl.match(asset.path))) {
next()
} else if (req.session.adminUser) {
next()
} else {
res.redirect(admin.options.loginPath)
}
})
router.get(logoutPath, async (req, res) => {
req.session.destroy()
res.redirect(admin.options.loginPath)
})
return buildRouter(admin, router)
}
module.exports = {
buildRouter,
buildAuthenticatedRouter,
/**
* Version of the plugin
* @static
* @memberof module:admin-bro-expressjs
*/
version: pkg.version,
/**
* Plugin name
* @static
* @memberof module:admin-bro-expressjs
*/
name: 'AdminBroExpressjs',
}
Source