feat: modular logging + setup wizard

This commit is contained in:
NGPixel
2017-12-24 00:34:47 -05:00
parent 18dee58a06
commit 2d5a3203db
43 changed files with 997 additions and 557 deletions

View File

@@ -5,6 +5,7 @@
name: Wiki.js
defaults:
config:
# File defaults
port: 80
paths:
repo: ./repo
@@ -20,15 +21,30 @@ defaults:
port: 6379
db: 0
password: null
configMode: interactive
configMode: auto
workers: 0
ha:
node: primary
readonly: false
# DB defaults
auth:
public: false
strategies:
local:
enabled: true
allowSelfRegister: false
git:
enabled: false
logging:
telemetry: false
loggers:
console:
enabled: true
site:
path: ''
lang: en
title: Wiki.js
path: ''
rtl: false
title: Wiki.js
configNamespaces:
- auth
- features

View File

@@ -1,5 +1,3 @@
'use strict'
/* global wiki */
// ------------------------------------

View File

@@ -0,0 +1,32 @@
const util = require('util')
const winston = require('winston')
const _ = require('lodash')
// ------------------------------------
// Bugsnag
// ------------------------------------
module.exports = {
key: 'bugsnag',
title: 'Bugsnag',
props: ['key'],
init (logger, conf) {
let BugsnagLogger = winston.transports.BugsnagLogger = function (options) {
this.name = 'bugsnagLogger'
this.level = options.level || 'warn'
this.bugsnag = require('bugsnag')
this.bugsnag.register(options.key)
}
util.inherits(BugsnagLogger, winston.Transport)
BugsnagLogger.prototype.log = function (level, msg, meta, callback) {
this.bugsnag.notify(new Error(msg), _.assignIn(meta, { severity: level }))
callback(null, true)
}
logger.add(BugsnagLogger, {
level: 'warn',
key: conf.key
})
}
}

View File

@@ -0,0 +1,22 @@
const winston = require('winston')
/* global wiki */
// ------------------------------------
// Console
// ------------------------------------
module.exports = {
key: 'console',
title: 'Console',
props: [],
init (logger, conf) {
logger.add(winston.transports.Console, {
level: wiki.config.logLevel,
prettyPrint: true,
colorize: true,
silent: false,
timestamp: true
})
}
}

View File

@@ -0,0 +1,21 @@
const winston = require('winston')
// ------------------------------------
// Loggly
// ------------------------------------
module.exports = {
key: 'loggly',
title: 'Loggly',
props: ['token', 'subdomain'],
init (logger, conf) {
require('winston-loggly-bulk')
logger.add(winston.transports.Loggly, {
token: conf.token,
subdomain: conf.subdomain,
tags: ['wiki-js'],
level: 'warn',
json: true
})
}
}

View File

@@ -0,0 +1,20 @@
const winston = require('winston')
// ------------------------------------
// Papertrail
// ------------------------------------
module.exports = {
key: 'papertrail',
title: 'Papertrail',
props: ['host', 'port'],
init (logger, conf) {
require('winston-papertrail').Papertrail // eslint-disable-line no-unused-expressions
logger.add(winston.transports.Papertrail, {
host: conf.host,
port: conf.port,
level: 'warn',
program: 'wiki.js'
})
}
}

View File

@@ -0,0 +1,32 @@
const util = require('util')
const winston = require('winston')
const _ = require('lodash')
// ------------------------------------
// Rollbar
// ------------------------------------
module.exports = {
key: 'rollbar',
title: 'Rollbar',
props: ['key'],
init (logger, conf) {
let RollbarLogger = winston.transports.RollbarLogger = function (options) {
this.name = 'rollbarLogger'
this.level = options.level || 'warn'
this.rollbar = require('rollbar')
this.rollbar.init(options.key)
}
util.inherits(RollbarLogger, winston.Transport)
RollbarLogger.prototype.log = function (level, msg, meta, callback) {
this.rollbar.handleErrorWithPayloadData(new Error(msg), _.assignIn(meta, { level }))
callback(null, true)
}
logger.add(RollbarLogger, {
level: 'warn',
key: conf.key
})
}
}

View File

@@ -0,0 +1,32 @@
const util = require('util')
const winston = require('winston')
// ------------------------------------
// Sentry
// ------------------------------------
module.exports = {
key: 'sentry',
title: 'Sentry',
props: ['key'],
init (logger, conf) {
let SentryLogger = winston.transports.RollbarLogger = function (options) {
this.name = 'sentryLogger'
this.level = options.level || 'warn'
this.raven = require('raven')
this.raven.config(options.key).install()
}
util.inherits(SentryLogger, winston.Transport)
SentryLogger.prototype.log = function (level, msg, meta, callback) {
level = (level === 'warn') ? 'warning' : level
this.raven.captureMessage(msg, { level, extra: meta })
callback(null, true)
}
logger.add(SentryLogger, {
level: 'warn',
key: conf.key
})
}
}

View File

@@ -29,7 +29,7 @@ module.exports = async () => {
const path = require('path')
const session = require('express-session')
const SessionRedisStore = require('connect-redis')(session)
const graceful = require('node-graceful')
// const graceful = require('node-graceful')
const graphqlApollo = require('apollo-server-express')
const graphqlSchema = require('./modules/graphql')
@@ -142,7 +142,7 @@ module.exports = async () => {
// Start HTTP server
// ----------------------------------------
wiki.logger.info(`HTTP Server on port: ${wiki.config.port}`)
wiki.logger.info(`HTTP Server on port: [ ${wiki.config.port} ]`)
app.set('port', wiki.config.port)
let server = http.createServer(app)
@@ -167,20 +167,20 @@ module.exports = async () => {
})
server.on('listening', () => {
wiki.logger.info('HTTP Server: RUNNING')
wiki.logger.info('HTTP Server: [ RUNNING ]')
})
// ----------------------------------------
// Graceful shutdown
// ----------------------------------------
graceful.on('exit', () => {
wiki.logger.info('- SHUTTING DOWN - Performing git sync...')
return global.git.resync().then(() => {
wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.')
process.exit()
})
})
// graceful.on('exit', () => {
// wiki.logger.info('- SHUTTING DOWN - Performing git sync...')
// return global.git.resync().then(() => {
// wiki.logger.info('- SHUTTING DOWN - Git sync successful. Now safe to exit.')
// process.exit()
// })
// })
return true
}

View File

@@ -1,23 +1,16 @@
'use strict'
/* global rights */
/* global wiki */
/**
* Authentication middleware
*
* @param {Express Request} req Express Request object
* @param {Express Response} res Express Response object
* @param {Function} next Next callback function
* @return {any} void
*/
module.exports = (req, res, next) => {
// Is user authenticated ?
if (!req.isAuthenticated()) {
if (req.app.locals.appconfig.public !== true) {
if (wiki.config.auth.public !== true) {
return res.redirect('/login')
} else {
req.user = rights.guest
// req.user = rights.guest
res.locals.isGuest = true
}
} else {
@@ -26,11 +19,11 @@ module.exports = (req, res, next) => {
// Check permissions
res.locals.rights = rights.check(req)
// res.locals.rights = rights.check(req)
if (!res.locals.rights.read) {
return res.render('error-forbidden')
}
// if (!res.locals.rights.read) {
// return res.render('error-forbidden')
// }
// Expose user data

View File

@@ -31,11 +31,9 @@ module.exports = {
// Load authentication strategies
wiki.config.auth.strategies.local = {}
_.forOwn(wiki.config.auth.strategies, (strategyConfig, strategyKey) => {
_.forOwn(_.omitBy(wiki.config.auth.strategies, s => s.enabled === false), (strategyConfig, strategyKey) => {
strategyConfig.callbackURL = `${wiki.config.site.host}${wiki.config.site.path}/login/${strategyKey}/callback`
let strategy = require(`../authentication/${strategyKey}`)
let strategy = require(`../extensions/authentication/${strategyKey}`)
strategy.init(passport, strategyConfig)
fs.readFile(path.join(wiki.ROOTPATH, `assets/svg/auth-icon-${strategyKey}.svg`), 'utf8').then(iconData => {
strategy.icon = iconData
@@ -47,7 +45,7 @@ module.exports = {
}
})
this.strategies[strategy.key] = strategy
wiki.logger.info(`Authentication Provider ${strategyKey}: OK`)
wiki.logger.info(`Authentication Provider ${strategyKey}: [ OK ]`)
})
// Create Guest account for first-time

View File

@@ -80,7 +80,7 @@ module.exports = {
// Attempt to connect and authenticate to DB
self.inst.authenticate().then(() => {
wiki.logger.info('Database (PostgreSQL) connection: OK')
wiki.logger.info('Database (PostgreSQL) connection: [ OK ]')
}).catch(err => {
wiki.logger.error('Failed to connect to PostgreSQL instance.')
wiki.logger.error(err)

View File

@@ -115,7 +115,7 @@ module.exports = {
wiki.logger.error(err)
}
wiki.logger.info('Disk Data Paths: OK')
wiki.logger.info('Disk Data Paths: [ OK ]')
},
/**

View File

@@ -88,8 +88,8 @@ module.exports = {
self._repo.exists = false
})
}).then(() => {
if (wiki.config.git === false) {
wiki.logger.warn('Remote Git syncing is disabled. Not recommended!')
if (wiki.config.git.enabled === false) {
wiki.logger.warn('Git Remote Sync: [ DISABLED ]')
return Promise.resolve(true)
}
@@ -129,7 +129,7 @@ module.exports = {
wiki.logger.error('Git remote error!')
throw err
}).then(() => {
wiki.logger.info('Git Repository: OK')
wiki.logger.info('Git Repository: [ OK ]')
return true
})
},

View File

@@ -37,11 +37,11 @@ module.exports = {
*/
bootMaster() {
this.preBootMaster().then(sequenceResults => {
if (_.every(sequenceResults, rs => rs === true)) {
if (_.every(sequenceResults, rs => rs === true) && wiki.config.configMode !== 'setup') {
this.postBootMaster()
} else {
wiki.logger.info('Starting configuration manager...')
require('../configure')()
require('../setup')()
}
return true
}).catch(err => {
@@ -52,13 +52,15 @@ module.exports = {
/**
* Post-Master Boot Sequence
*/
postBootMaster() {
require('../master')().then(() => {
_.times(this.numWorker, this.spawnWorker)
async postBootMaster() {
await require('../master')()
wiki.queue.uplClearTemp.add({}, {
repeat: { cron: '*/15 * * * *' }
})
_.times(this.numWorkers, () => {
this.spawnWorker()
})
wiki.queue.uplClearTemp.add({}, {
repeat: { cron: '*/15 * * * *' }
})
cluster.on('exit', (worker, code, signal) => {

View File

@@ -1,26 +1,18 @@
'use strict'
/* global wiki */
const cluster = require('cluster')
const _ = require('lodash')
const fs = require('fs-extra')
const path = require('path')
module.exports = {
loggers: {},
init() {
let winston = require('winston')
// Console
let logger = new (winston.Logger)({
level: wiki.config.logLevel,
transports: [
new (winston.transports.Console)({
level: wiki.config.logLevel,
prettyPrint: true,
colorize: true,
silent: false,
timestamp: true
})
]
transports: []
})
logger.filters.push((level, msg) => {
@@ -28,52 +20,20 @@ module.exports = {
return '[' + processName + '] ' + msg
})
// External services
// if (wiki.config.externalLogging.bugsnag) {
// const bugsnagTransport = require('./winston-transports/bugsnag')
// logger.add(bugsnagTransport, {
// level: 'warn',
// key: wiki.config.externalLogging.bugsnag
// })
// }
// if (wiki.config.externalLogging.loggly) {
// require('winston-loggly-bulk')
// logger.add(winston.transports.Loggly, {
// token: wiki.config.externalLogging.loggly.token,
// subdomain: wiki.config.externalLogging.loggly.subdomain,
// tags: ['wiki-js'],
// level: 'warn',
// json: true
// })
// }
// if (wiki.config.externalLogging.papertrail) {
// require('winston-papertrail').Papertrail // eslint-disable-line no-unused-expressions
// logger.add(winston.transports.Papertrail, {
// host: wiki.config.externalLogging.papertrail.host,
// port: wiki.config.externalLogging.papertrail.port,
// level: 'warn',
// program: 'wiki.js'
// })
// }
// if (wiki.config.externalLogging.rollbar) {
// const rollbarTransport = require('./winston-transports/rollbar')
// logger.add(rollbarTransport, {
// level: 'warn',
// key: wiki.config.externalLogging.rollbar
// })
// }
// if (wiki.config.externalLogging.sentry) {
// const sentryTransport = require('./winston-transports/sentry')
// logger.add(sentryTransport, {
// level: 'warn',
// key: wiki.config.externalLogging.sentry
// })
// }
_.forOwn(_.omitBy(wiki.config.logging.loggers, s => s.enabled === false), (loggerConfig, loggerKey) => {
let loggerModule = require(`../extensions/logging/${loggerKey}`)
loggerModule.init(logger, loggerConfig)
fs.readFile(path.join(wiki.ROOTPATH, `assets/svg/auth-icon-${loggerKey}.svg`), 'utf8').then(iconData => {
logger.icon = iconData
}).catch(err => {
if (err.code === 'ENOENT') {
logger.icon = '[missing icon]'
} else {
logger.error(err)
}
})
this.loggers[logger.key] = loggerModule
})
return logger
}

View File

@@ -29,7 +29,7 @@ module.exports = {
keyStream.on('end', resolve)
})
}).then(() => {
wiki.logger.info('Purging old queue jobs: OK')
wiki.logger.info('Purging old queue jobs: [ OK ]')
}).return(true).catch(err => {
wiki.logger.error(err)
})

View File

@@ -21,7 +21,7 @@ module.exports = {
if (isPlainObject(wiki.config.redis)) {
let red = new Redis(wiki.config.redis)
red.on('ready', () => {
wiki.logger.info('Redis connection: OK')
wiki.logger.info('Redis connection: [ OK ]')
})
red.on('error', () => {
wiki.logger.error('Failed to connect to Redis instance!')

View File

@@ -1,20 +0,0 @@
'use strict'
const util = require('util')
const winston = require('winston')
const _ = require('lodash')
let BugsnagLogger = winston.transports.BugsnagLogger = function (options) {
this.name = 'bugsnagLogger'
this.level = options.level || 'warn'
this.bugsnag = require('bugsnag')
this.bugsnag.register(options.key)
}
util.inherits(BugsnagLogger, winston.Transport)
BugsnagLogger.prototype.log = function (level, msg, meta, callback) {
this.bugsnag.notify(new Error(msg), _.assignIn(meta, { severity: level }))
callback(null, true)
}
module.exports = BugsnagLogger

View File

@@ -1,20 +0,0 @@
'use strict'
const util = require('util')
const winston = require('winston')
const _ = require('lodash')
let RollbarLogger = winston.transports.RollbarLogger = function (options) {
this.name = 'rollbarLogger'
this.level = options.level || 'warn'
this.rollbar = require('rollbar')
this.rollbar.init(options.key)
}
util.inherits(RollbarLogger, winston.Transport)
RollbarLogger.prototype.log = function (level, msg, meta, callback) {
this.rollbar.handleErrorWithPayloadData(new Error(msg), _.assignIn(meta, { level }))
callback(null, true)
}
module.exports = RollbarLogger

View File

@@ -1,20 +0,0 @@
'use strict'
const util = require('util')
const winston = require('winston')
let SentryLogger = winston.transports.RollbarLogger = function (options) {
this.name = 'sentryLogger'
this.level = options.level || 'warn'
this.raven = require('raven')
this.raven.config(options.key).install()
}
util.inherits(SentryLogger, winston.Transport)
SentryLogger.prototype.log = function (level, msg, meta, callback) {
level = (level === 'warn') ? 'warning' : level
this.raven.captureMessage(msg, { level, extra: meta })
callback(null, true)
}
module.exports = SentryLogger

View File

@@ -63,7 +63,7 @@ module.exports = () => {
app.get('*', async (req, res) => {
let packageObj = await fs.readJson(path.join(wiki.ROOTPATH, 'package.json'))
res.render('configure/index', {
res.render('setup', {
packageObj,
telemetryClientID: wiki.telemetry.cid
})
@@ -265,6 +265,7 @@ module.exports = () => {
// Auth namespace
_.set(wiki.config.auth, 'public', req.body.public === 'true')
_.set(wiki.config.auth, 'strategies.local.enabled', true)
_.set(wiki.config.auth, 'strategies.local.allowSelfRegister', req.body.selfRegister === 'true')
// Git namespace
@@ -311,6 +312,7 @@ module.exports = () => {
tfaIsActive: false
})
wiki.logger.info('Setup is complete!')
res.json({ ok: true })
} catch (err) {
res.json({ ok: false, error: err.message })

View File

@@ -6,24 +6,24 @@ html
meta(name='viewport', content='width=device-width, initial-scale=1')
meta(name='theme-color', content='#009688')
meta(name='msapplication-TileColor', content='#009688')
meta(name='msapplication-TileImage', content=config.site.path + '/favicons/ms-icon-144x144.png')
meta(name='msapplication-TileImage', content=config.site.path + 'favicons/ms-icon-144x144.png')
title= config.site.title
//- Favicon
each favsize in [57, 60, 72, 76, 114, 120, 144, 152, 180]
link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href=config.site.path + '/favicons/apple-icon-' + favsize + 'x' + favsize + '.png')
link(rel='icon', type='image/png', sizes='192x192', href=config.site.path + '/favicons/android-icon-192x192.png')
link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href=config.site.path + 'favicons/apple-icon-' + favsize + 'x' + favsize + '.png')
link(rel='icon', type='image/png', sizes='192x192', href=config.site.path + 'favicons/android-icon-192x192.png')
each favsize in [32, 96, 16]
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href=config.site.path + '/favicons/favicon-' + favsize + 'x' + favsize + '.png')
link(rel='manifest', href=config.site.path + '/manifest.json')
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href=config.site.path + 'favicons/favicon-' + favsize + 'x' + favsize + '.png')
link(rel='manifest', href=config.site.path + 'manifest.json')
//- Site Lang
script.
var siteConfig = !{JSON.stringify(config.site)}
//- JS / CSS
script(type='text/javascript', src=config.site.path + '/js/libs.js')
script(type='text/javascript', src=config.site.path + '/js/app.js')
script(type='text/javascript', src=config.site.path + 'js/libs.js')
script(type='text/javascript', src=config.site.path + 'js/app.js')
block head

View File

@@ -1,9 +1,9 @@
extends ../master.pug
extends master.pug
block body
body
#app.config-manager
config-manager(inline-template)
#app.setup
setup(inline-template)
div
.container
.content(v-cloak)
@@ -342,7 +342,7 @@ block body
h4 All done!
p(v-if='!loading && final.ok'): strong Wiki.js was configured successfully and is now ready for use.
p(v-if='!loading && final.ok')
| Click the <strong>Start</strong> button below to start the Wiki.js server.
| Click the <strong>Start</strong> button below to launch your newly configured wiki.
p(v-if='!loading && !final.ok') #[svg.icons.is-18.is-text: use(xlink:href='#nc-square-remove-12')] Error: {{ final.error }}
.panel-footer
.progress-bar: div(v-bind:style='{width: currentProgress}')