diff --git a/Dockerfile b/Dockerfile index 50fbcdc..dca26ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ ENV REDIS_USE_TLS false ENV REDIS_PASSWORD '' ENV BULL_PREFIX bull ENV BULL_VERSION BULLMQ +ENV USER_LOGIN '' +ENV USER_PASSWORD '' RUN yarn install @@ -21,4 +23,4 @@ ARG PORT=3000 ENV PORT $PORT EXPOSE $PORT -CMD ["node", "index.js"] +CMD ["node", "src/index.js"] diff --git a/README.md b/README.md index 18d1300..e1ca45e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,15 @@ see "Example with docker-compose" section for example with env parameters * `BULL_PREFIX` - prefix to your bull queue name (bull by default) * `BULL_VERSION` - version of bull lib to use 'BULLMQ' or 'BULL' ('BULLMQ' by default) * `BASE_PATH` - basePath for bull board, e.g. '/bull-board' ('/' by default) +* `USER_LOGIN` - login to restrict access to bull-board interface (disabled by default) +* `USER_PASSWORD` - password to restrict access to bull-board interface (disabled by default) + + +### Restrict access with login and password + +To restrict access to bull-board use `USER_LOGIN` and `USER_PASSWORD` env vars. +Only when both `USER_LOGIN` and `USER_PASSWORD` specified, access will be restricted with login/password + ### Example with docker-compose ``` diff --git a/index.js b/index.js deleted file mode 100644 index 861b898..0000000 --- a/index.js +++ /dev/null @@ -1,52 +0,0 @@ -const {router, setQueues, BullMQAdapter, BullAdapter} = require('bull-board') -const Queue = require('bull'); -const bullmq = require('bullmq'); -const express = require('express'); -const redis = require('redis'); - -const { - REDIS_PORT = 6379, - REDIS_HOST = 'localhost', - REDIS_PASSWORD, - REDIS_USE_TLS, - BULL_PREFIX = 'bull', - BULL_VERSION = 'BULLMQ', - PORT = 3000, - BASE_PATH = '/' -} = process.env; - -const redisConfig = { - redis: { - port: REDIS_PORT, - host: REDIS_HOST, - ...(REDIS_PASSWORD && {password: REDIS_PASSWORD}), - tls: REDIS_USE_TLS === 'true', - }, -}; - -const client = redis.createClient(redisConfig.redis); -const prefix = BULL_PREFIX; -const port = PORT; -const basePath = BASE_PATH; - -client.KEYS(`${prefix}:*`, (err, keys) => { - const uniqKeys = new Set(keys.map(key => key.replace(/^.+?:(.+?):.+?$/, '$1'))); - const queueList = Array.from(uniqKeys).sort().map( - (item) => { - if (BULL_VERSION === 'BULLMQ') { - return new BullMQAdapter(new bullmq.Queue(item, {connection: redisConfig.redis})); - } - - return new BullAdapter(new Queue(item, redisConfig)); - } - ); - - setQueues(queueList); -}); - -const app = express(); - -app.use(basePath, router); -app.listen(port, () => { - console.log(`bull-board listening on port ${port} on basePath ${basePath}!`); -}); diff --git a/package.json b/package.json index a61c294..817c32a 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,18 @@ { "name": "bullboard", "version": "1.0.0-alpha.7", - "main": "index.js", + "main": "src/index.js", "license": "MIT", "dependencies": { + "body-parser": "^1.19.0", "bull": "^3.13.0", - "bull-board": "^1.0.0-alpha.8", + "bull-board": "^1.2.0", "bullmq": "^1.8.4", + "connect-ensure-login": "^0.1.1", "express": "^4.17.1", + "express-session": "^1.17.1", + "passport": "^0.4.1", + "passport-local": "^1.0.0", "redis": "^3.0.2" } } diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..e47b915 --- /dev/null +++ b/src/config.js @@ -0,0 +1,24 @@ +let BASE_PATH = process.env.BASE_PATH || '/'; + +if (BASE_PATH.endsWith('/')) { + BASE_PATH = BASE_PATH.substr(0, BASE_PATH.length - 1); +} + +const config = { + REDIS_PORT: process.env.REDIS_PORT || 6379, + REDIS_HOST: process.env.REDIS_HOST || 'localhost', + REDIS_PASSWORD: process.env.REDIS_PASSWORD, + REDIS_USE_TLS: process.env.REDIS_USE_TLS, + BULL_PREFIX: process.env.BULL_PREFIX || 'bull', + BULL_VERSION: process.env.BULL_VERSION || 'BULLMQ', + PORT: process.env.PORT || 3000, + BASE_PATH: BASE_PATH, + USER_LOGIN: process.env.USER_LOGIN, + USER_PASSWORD: process.env.USER_PASSWORD, + + AUTH_ENABLED: Boolean(process.env.USER_LOGIN && process.env.USER_PASSWORD), + HOME_PAGE: BASE_PATH || '/', + LOGIN_PAGE: `${BASE_PATH}/login`, +}; + +module.exports = config; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..99ada25 --- /dev/null +++ b/src/index.js @@ -0,0 +1,61 @@ +const {router, setQueues, BullMQAdapter, BullAdapter} = require('bull-board'); +const Queue = require('bull'); +const bullmq = require('bullmq'); +const express = require('express'); +const redis = require('redis'); +const session = require('express-session'); +const passport = require('passport'); +const {ensureLoggedIn} = require('connect-ensure-login'); +const bodyParser = require('body-parser'); + +const {authRouter} = require('./login'); +const config = require('./config'); + +const redisConfig = { + redis: { + port: config.REDIS_PORT, + host: config.REDIS_HOST, + ...(config.REDIS_PASSWORD && {password: config.REDIS_PASSWORD}), + tls: config.REDIS_USE_TLS === 'true', + }, +}; + +const client = redis.createClient(redisConfig.redis); + +client.KEYS(`${config.BULL_PREFIX}:*`, (err, keys) => { + const uniqKeys = new Set(keys.map(key => key.replace(/^.+?:(.+?):.+?$/, '$1'))); + const queueList = Array.from(uniqKeys).sort().map( + (item) => { + if (config.BULL_VERSION === 'BULLMQ') { + return new BullMQAdapter(new bullmq.Queue(item, {connection: redisConfig.redis})); + } + + return new BullAdapter(new Queue(item, redisConfig)); + } + ); + + setQueues(queueList); +}); + +const app = express(); + +app.set('views', __dirname + '/views'); +app.set('view engine', 'ejs'); + +app.use(session({secret: Math.random().toString(), resave: false, saveUninitialized: false})); +app.use(passport.initialize({})); +app.use(passport.session({})); + +app.use(bodyParser.urlencoded({extended: false})); + +if (config.AUTH_ENABLED) { + app.use(config.LOGIN_PAGE, authRouter); + app.use(config.HOME_PAGE, ensureLoggedIn(config.LOGIN_PAGE), router); +} +else { + app.use(config.HOME_PAGE, router); +} + +app.listen(config.PORT, () => { + console.log(`bull-board is started http://localhost:${config.PORT}${config.HOME_PAGE}`); +}); diff --git a/src/login.js b/src/login.js new file mode 100644 index 0000000..f151a47 --- /dev/null +++ b/src/login.js @@ -0,0 +1,36 @@ +const passport = require('passport'); +const LocalStrategy = require('passport-local').Strategy; +const express = require('express'); + +const config = require('./config'); + +const authRouter = express.Router(); + +passport.use(new LocalStrategy( + function (username, password, cb) { + if (username === config.USER_LOGIN && password === config.USER_PASSWORD) { + return cb(null, {user: 'bull-board'}); + } + + return cb(null, false); + }) +); + +passport.serializeUser((user, cb) => { + cb(null, user); +}); + +passport.deserializeUser((user, cb) => { + cb(null, user); +}); + +authRouter.route('/') + .get((req, res) => { + res.render('login'); + }) + .post(passport.authenticate('local', { + successRedirect: config.HOME_PAGE, + failureRedirect: config.LOGIN_PAGE, + })); + +exports.authRouter = authRouter; diff --git a/src/views/login.ejs b/src/views/login.ejs new file mode 100644 index 0000000..e998f6c --- /dev/null +++ b/src/views/login.ejs @@ -0,0 +1,119 @@ + + +