feat: modular auth + queue tasks
This commit is contained in:
parent
9c112ab535
commit
f32429325c
@ -5,7 +5,7 @@
|
|||||||
# https://docs.requarks.io/wiki/install
|
# https://docs.requarks.io/wiki/install
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Port the main server should listen to (80 by default)
|
# Port the main server should listen to
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
port: 80
|
port: 80
|
||||||
@ -39,3 +39,9 @@ redis:
|
|||||||
db: 0
|
db: 0
|
||||||
password: null
|
password: null
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Background Workers
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Leave 0 for auto based on CPU cores
|
||||||
|
workers: 0
|
||||||
|
@ -52,7 +52,6 @@
|
|||||||
"connect-flash": "~0.1.1",
|
"connect-flash": "~0.1.1",
|
||||||
"connect-redis": "~3.3.0",
|
"connect-redis": "~3.3.0",
|
||||||
"cookie-parser": "~1.4.3",
|
"cookie-parser": "~1.4.3",
|
||||||
"cron": "~1.2.1",
|
|
||||||
"diff2html": "~2.3.0",
|
"diff2html": "~2.3.0",
|
||||||
"execa": "~0.7.0",
|
"execa": "~0.7.0",
|
||||||
"express": "~4.15.3",
|
"express": "~4.15.3",
|
||||||
|
@ -5,65 +5,22 @@
|
|||||||
name: Wiki.js
|
name: Wiki.js
|
||||||
defaults:
|
defaults:
|
||||||
config:
|
config:
|
||||||
title: Wiki
|
|
||||||
host: http://localhost
|
|
||||||
port: 80
|
port: 80
|
||||||
paths:
|
paths:
|
||||||
repo: ./repo
|
repo: ./repo
|
||||||
data: ./data
|
data: ./data
|
||||||
uploads:
|
db:
|
||||||
maxImageFileSize: 3,
|
host: localhost
|
||||||
maxOtherFileSize: 100
|
port: 5432
|
||||||
lang: en
|
user: wikijs
|
||||||
public: false
|
pass: wikijsrocks
|
||||||
auth:
|
db: wiki
|
||||||
defaultReadAccess: false
|
redis:
|
||||||
local:
|
host: localhost
|
||||||
enabled: true
|
port: 6379
|
||||||
microsoft:
|
db: 0
|
||||||
enabled: false
|
password: null
|
||||||
google:
|
workers: 0
|
||||||
enabled: false
|
|
||||||
facebook:
|
|
||||||
enabled: false
|
|
||||||
github:
|
|
||||||
enabled: false
|
|
||||||
slack:
|
|
||||||
enabled: false
|
|
||||||
ldap:
|
|
||||||
enabled: false
|
|
||||||
azure:
|
|
||||||
enabled: false
|
|
||||||
db: mongodb://localhost/wiki
|
|
||||||
sessionSecret: null
|
|
||||||
admin: null
|
|
||||||
git:
|
|
||||||
url: null
|
|
||||||
branch: master
|
|
||||||
auth:
|
|
||||||
type: basic
|
|
||||||
username: null
|
|
||||||
password: null
|
|
||||||
privateKey: null
|
|
||||||
sslVerify: true
|
|
||||||
serverEmail: wiki@example.com
|
|
||||||
showUserEmail: true
|
|
||||||
features:
|
|
||||||
linebreaks: true
|
|
||||||
mathjax: true
|
|
||||||
externalLogging:
|
|
||||||
bugsnap: false
|
|
||||||
loggly: false
|
|
||||||
papertrail: false
|
|
||||||
rollbar: false
|
|
||||||
sentry: false
|
|
||||||
theme:
|
|
||||||
primary: indigo
|
|
||||||
alt: blue-grey
|
|
||||||
footer: blue-grey
|
|
||||||
code:
|
|
||||||
dark: true
|
|
||||||
colorize: true
|
|
||||||
authProviders:
|
authProviders:
|
||||||
- local
|
- local
|
||||||
- microsoft
|
- microsoft
|
||||||
@ -112,6 +69,9 @@ langs:
|
|||||||
-
|
-
|
||||||
id: ko
|
id: ko
|
||||||
name: Korean - 한국어
|
name: Korean - 한국어
|
||||||
|
-
|
||||||
|
id: pt
|
||||||
|
name: Portuguese - Português
|
||||||
-
|
-
|
||||||
id: ru
|
id: ru
|
||||||
name: Russian - Русский
|
name: Russian - Русский
|
||||||
|
33
server/authentication/azure.js
Normal file
33
server/authentication/azure.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Azure AD Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.azure && wiki.config.auth.azure.enabled) {
|
||||||
|
const jwt = require('jsonwebtoken')
|
||||||
|
passport.use('azure_ad_oauth2',
|
||||||
|
new AzureAdOAuth2Strategy({
|
||||||
|
clientID: wiki.config.auth.azure.clientId,
|
||||||
|
clientSecret: wiki.config.auth.azure.clientSecret,
|
||||||
|
callbackURL: wiki.config.host + '/login/azure/callback',
|
||||||
|
resource: wiki.config.auth.azure.resource,
|
||||||
|
tenant: wiki.config.auth.azure.tenant
|
||||||
|
}, (accessToken, refreshToken, params, profile, cb) => {
|
||||||
|
let waadProfile = jwt.decode(params.id_token)
|
||||||
|
waadProfile.id = waadProfile.oid
|
||||||
|
waadProfile.provider = 'azure'
|
||||||
|
wiki.db.User.processProfile(waadProfile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
28
server/authentication/facebook.js
Normal file
28
server/authentication/facebook.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Facebook Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const FacebookStrategy = require('passport-facebook').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.facebook && wiki.config.auth.facebook.enabled) {
|
||||||
|
passport.use('facebook',
|
||||||
|
new FacebookStrategy({
|
||||||
|
clientID: wiki.config.auth.facebook.clientId,
|
||||||
|
clientSecret: wiki.config.auth.facebook.clientSecret,
|
||||||
|
callbackURL: wiki.config.host + '/login/facebook/callback',
|
||||||
|
profileFields: ['id', 'displayName', 'email']
|
||||||
|
}, function (accessToken, refreshToken, profile, cb) {
|
||||||
|
wiki.db.User.processProfile(profile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
28
server/authentication/github.js
Normal file
28
server/authentication/github.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// GitHub Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const GitHubStrategy = require('passport-github2').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.github && wiki.config.auth.github.enabled) {
|
||||||
|
passport.use('github',
|
||||||
|
new GitHubStrategy({
|
||||||
|
clientID: wiki.config.auth.github.clientId,
|
||||||
|
clientSecret: wiki.config.auth.github.clientSecret,
|
||||||
|
callbackURL: wiki.config.host + '/login/github/callback',
|
||||||
|
scope: ['user:email']
|
||||||
|
}, (accessToken, refreshToken, profile, cb) => {
|
||||||
|
wiki.db.User.processProfile(profile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
27
server/authentication/google.js
Normal file
27
server/authentication/google.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Google ID Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const GoogleStrategy = require('passport-google-oauth20').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.google && wiki.config.auth.google.enabled) {
|
||||||
|
passport.use('google',
|
||||||
|
new GoogleStrategy({
|
||||||
|
clientID: wiki.config.auth.google.clientId,
|
||||||
|
clientSecret: wiki.config.auth.google.clientSecret,
|
||||||
|
callbackURL: wiki.config.host + '/login/google/callback'
|
||||||
|
}, (accessToken, refreshToken, profile, cb) => {
|
||||||
|
wiki.db.User.processProfile(profile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
41
server/authentication/ldap.js
Normal file
41
server/authentication/ldap.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// LDAP Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const LdapStrategy = require('passport-ldapauth').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.ldap && wiki.config.auth.ldap.enabled) {
|
||||||
|
passport.use('ldapauth',
|
||||||
|
new LdapStrategy({
|
||||||
|
server: {
|
||||||
|
url: wiki.config.auth.ldap.url,
|
||||||
|
bindDn: wiki.config.auth.ldap.bindDn,
|
||||||
|
bindCredentials: wiki.config.auth.ldap.bindCredentials,
|
||||||
|
searchBase: wiki.config.auth.ldap.searchBase,
|
||||||
|
searchFilter: wiki.config.auth.ldap.searchFilter,
|
||||||
|
searchAttributes: ['displayName', 'name', 'cn', 'mail'],
|
||||||
|
tlsOptions: (wiki.config.auth.ldap.tlsEnabled) ? {
|
||||||
|
ca: [
|
||||||
|
fs.readFileSync(wiki.config.auth.ldap.tlsCertPath)
|
||||||
|
]
|
||||||
|
} : {}
|
||||||
|
},
|
||||||
|
usernameField: 'email',
|
||||||
|
passReqToCallback: false
|
||||||
|
}, (profile, cb) => {
|
||||||
|
profile.provider = 'ldap'
|
||||||
|
profile.id = profile.dn
|
||||||
|
wiki.db.User.processProfile(profile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
34
server/authentication/local.js
Normal file
34
server/authentication/local.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Local Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const LocalStrategy = require('passport-local').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.local && wiki.config.auth.local.enabled) {
|
||||||
|
passport.use('local',
|
||||||
|
new LocalStrategy({
|
||||||
|
usernameField: 'email',
|
||||||
|
passwordField: 'password'
|
||||||
|
}, (uEmail, uPassword, done) => {
|
||||||
|
wiki.db.User.findOne({ email: uEmail, provider: 'local' }).then((user) => {
|
||||||
|
if (user) {
|
||||||
|
return user.validatePassword(uPassword).then(() => {
|
||||||
|
return done(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return done(err, null)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return done(new Error('INVALID_LOGIN'), null)
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
done(err, null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
27
server/authentication/microsoft.js
Normal file
27
server/authentication/microsoft.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Microsoft Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const WindowsLiveStrategy = require('passport-windowslive').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.microsoft && wiki.config.auth.microsoft.enabled) {
|
||||||
|
passport.use('windowslive',
|
||||||
|
new WindowsLiveStrategy({
|
||||||
|
clientID: wiki.config.auth.microsoft.clientId,
|
||||||
|
clientSecret: wiki.config.auth.microsoft.clientSecret,
|
||||||
|
callbackURL: wiki.config.host + '/login/ms/callback'
|
||||||
|
}, function (accessToken, refreshToken, profile, cb) {
|
||||||
|
wiki.db.User.processProfile(profile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
27
server/authentication/slack.js
Normal file
27
server/authentication/slack.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Slack Account
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
const SlackStrategy = require('passport-slack').Strategy
|
||||||
|
|
||||||
|
module.exports = (passport) => {
|
||||||
|
if (wiki.config.auth.slack && wiki.config.auth.slack.enabled) {
|
||||||
|
passport.use('slack',
|
||||||
|
new SlackStrategy({
|
||||||
|
clientID: wiki.config.auth.slack.clientId,
|
||||||
|
clientSecret: wiki.config.auth.slack.clientSecret,
|
||||||
|
callbackURL: wiki.config.host + '/login/slack/callback'
|
||||||
|
}, (accessToken, refreshToken, profile, cb) => {
|
||||||
|
wiki.db.User.processProfile(profile).then((user) => {
|
||||||
|
return cb(null, user) || true
|
||||||
|
}).catch((err) => {
|
||||||
|
return cb(err, null) || true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
@ -6,10 +6,14 @@
|
|||||||
// ===========================================
|
// ===========================================
|
||||||
|
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const cluster = require('cluster')
|
||||||
|
|
||||||
let wiki = {
|
let wiki = {
|
||||||
IS_DEBUG: process.env.NODE_ENV === 'development',
|
IS_DEBUG: process.env.NODE_ENV === 'development',
|
||||||
|
IS_MASTER: cluster.isMaster,
|
||||||
ROOTPATH: process.cwd(),
|
ROOTPATH: process.cwd(),
|
||||||
SERVERPATH: path.join(process.cwd(), 'server')
|
SERVERPATH: path.join(process.cwd(), 'server'),
|
||||||
|
configSvc: require('./modules/config')
|
||||||
}
|
}
|
||||||
global.wiki = wiki
|
global.wiki = wiki
|
||||||
|
|
||||||
@ -19,29 +23,36 @@ process.env.VIPS_WARNING = false
|
|||||||
// require('@glimpse/glimpse').init()
|
// require('@glimpse/glimpse').init()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let appconf = require('./modules/config')()
|
wiki.configSvc.init()
|
||||||
wiki.config = appconf.config
|
|
||||||
wiki.data = appconf.data
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Load Winston
|
// Init Logger
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
wiki.logger = require('./modules/logger')()
|
wiki.logger = require('./modules/logger').init()
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Init DB
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
wiki.db = require('./modules/db').init()
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Start Cluster
|
// Start Cluster
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
const cluster = require('cluster')
|
|
||||||
const numCPUs = require('os').cpus().length
|
const numCPUs = require('os').cpus().length
|
||||||
|
let numWorkers = (wiki.config.workers > 0) ? wiki.config.workers : numCPUs
|
||||||
|
if (numWorkers > numCPUs) {
|
||||||
|
numWorkers = numCPUs
|
||||||
|
}
|
||||||
|
|
||||||
if (cluster.isMaster) {
|
if (cluster.isMaster) {
|
||||||
wiki.logger.info('Wiki.js is initializing...')
|
wiki.logger.info('Wiki.js is initializing...')
|
||||||
|
|
||||||
require('./master')
|
require('./master')
|
||||||
|
|
||||||
for (let i = 0; i < numCPUs; i++) {
|
for (let i = 0; i < numWorkers; i++) {
|
||||||
cluster.fork()
|
cluster.fork()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,5 +61,5 @@ if (cluster.isMaster) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
wiki.logger.info(`Background Worker #${cluster.worker.id} is starting...`)
|
wiki.logger.info(`Background Worker #${cluster.worker.id} is starting...`)
|
||||||
// require('./worker')
|
require('./worker')
|
||||||
}
|
}
|
||||||
|
418
server/master.js
418
server/master.js
@ -2,231 +2,241 @@
|
|||||||
|
|
||||||
/* global wiki */
|
/* global wiki */
|
||||||
|
|
||||||
const path = require('path')
|
const Promise = require('bluebird')
|
||||||
|
|
||||||
// ----------------------------------------
|
module.exports = Promise.join(
|
||||||
// Load global modules
|
wiki.db.onReady,
|
||||||
// ----------------------------------------
|
wiki.configSvc.loadFromDb()
|
||||||
|
).then(() => {
|
||||||
|
// ----------------------------------------
|
||||||
|
// Load global modules
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
wiki.disk = require('./modules/disk').init()
|
wiki.disk = require('./modules/disk').init()
|
||||||
wiki.db = require('./modules/db').init()
|
wiki.entries = require('./modules/entries').init()
|
||||||
wiki.entries = require('./modules/entries').init()
|
wiki.git = require('./modules/git').init(false)
|
||||||
wiki.git = require('./modules/git').init(false)
|
wiki.lang = require('i18next')
|
||||||
wiki.lang = require('i18next')
|
wiki.mark = require('./modules/markdown')
|
||||||
wiki.mark = require('./modules/markdown')
|
wiki.redis = require('./modules/redis').init()
|
||||||
wiki.redis = require('./modules/redis').init()
|
wiki.search = require('./modules/search').init()
|
||||||
wiki.search = require('./modules/search').init()
|
wiki.upl = require('./modules/uploads').init()
|
||||||
wiki.upl = require('./modules/uploads').init()
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Load modules
|
// Load modules
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
const autoload = require('auto-load')
|
const autoload = require('auto-load')
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
const compression = require('compression')
|
const compression = require('compression')
|
||||||
const cookieParser = require('cookie-parser')
|
const cookieParser = require('cookie-parser')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const favicon = require('serve-favicon')
|
const favicon = require('serve-favicon')
|
||||||
const flash = require('connect-flash')
|
const flash = require('connect-flash')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const i18nBackend = require('i18next-node-fs-backend')
|
const i18nBackend = require('i18next-node-fs-backend')
|
||||||
const passport = require('passport')
|
const path = require('path')
|
||||||
const passportSocketIo = require('passport.socketio')
|
const passport = require('passport')
|
||||||
const session = require('express-session')
|
const passportSocketIo = require('passport.socketio')
|
||||||
const SessionRedisStore = require('connect-redis')(session)
|
const session = require('express-session')
|
||||||
const graceful = require('node-graceful')
|
const SessionRedisStore = require('connect-redis')(session)
|
||||||
const socketio = require('socket.io')
|
const graceful = require('node-graceful')
|
||||||
const graphqlApollo = require('apollo-server-express')
|
const socketio = require('socket.io')
|
||||||
const graphqlSchema = require('./modules/graphql')
|
const graphqlApollo = require('apollo-server-express')
|
||||||
|
const graphqlSchema = require('./modules/graphql')
|
||||||
|
|
||||||
var mw = autoload(path.join(wiki.SERVERPATH, '/middlewares'))
|
var mw = autoload(path.join(wiki.SERVERPATH, '/middlewares'))
|
||||||
var ctrl = autoload(path.join(wiki.SERVERPATH, '/controllers'))
|
var ctrl = autoload(path.join(wiki.SERVERPATH, '/controllers'))
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Define Express App
|
// Define Express App
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
wiki.app = app
|
wiki.app = app
|
||||||
app.use(compression())
|
app.use(compression())
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Security
|
// Security
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
app.use(mw.security)
|
app.use(mw.security)
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Public Assets
|
// Public Assets
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
app.use(favicon(path.join(wiki.ROOTPATH, 'assets', 'favicon.ico')))
|
app.use(favicon(path.join(wiki.ROOTPATH, 'assets', 'favicon.ico')))
|
||||||
app.use(express.static(path.join(wiki.ROOTPATH, 'assets'), {
|
app.use(express.static(path.join(wiki.ROOTPATH, 'assets'), {
|
||||||
index: false,
|
index: false,
|
||||||
maxAge: '7d'
|
maxAge: '7d'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Passport Authentication
|
// Passport Authentication
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
require('./modules/auth')(passport)
|
require('./modules/auth')(passport)
|
||||||
wiki.rights = require('./modules/rights')
|
wiki.rights = require('./modules/rights')
|
||||||
wiki.rights.init()
|
// wiki.rights.init()
|
||||||
|
|
||||||
let sessionStore = new SessionRedisStore({
|
let sessionStore = new SessionRedisStore({
|
||||||
client: wiki.redis
|
client: wiki.redis
|
||||||
})
|
|
||||||
|
|
||||||
app.use(cookieParser())
|
|
||||||
app.use(session({
|
|
||||||
name: 'wikijs.sid',
|
|
||||||
store: sessionStore,
|
|
||||||
secret: wiki.config.sessionSecret,
|
|
||||||
resave: false,
|
|
||||||
saveUninitialized: false
|
|
||||||
}))
|
|
||||||
app.use(flash())
|
|
||||||
app.use(passport.initialize())
|
|
||||||
app.use(passport.session())
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// SEO
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
app.use(mw.seo)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Localization Engine
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
wiki.lang.use(i18nBackend).init({
|
|
||||||
load: 'languageOnly',
|
|
||||||
ns: ['common', 'admin', 'auth', 'errors', 'git'],
|
|
||||||
defaultNS: 'common',
|
|
||||||
saveMissing: false,
|
|
||||||
preload: [wiki.config.lang],
|
|
||||||
lng: wiki.config.lang,
|
|
||||||
fallbackLng: 'en',
|
|
||||||
backend: {
|
|
||||||
loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// View Engine Setup
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
app.set('views', path.join(wiki.SERVERPATH, 'views'))
|
|
||||||
app.set('view engine', 'pug')
|
|
||||||
|
|
||||||
app.use(bodyParser.json({ limit: '1mb' }))
|
|
||||||
app.use(bodyParser.urlencoded({ extended: false, limit: '1mb' }))
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// View accessible data
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
app.locals._ = require('lodash')
|
|
||||||
app.locals.t = wiki.lang.t.bind(wiki.lang)
|
|
||||||
app.locals.moment = require('moment')
|
|
||||||
app.locals.moment.locale(wiki.config.lang)
|
|
||||||
app.locals.appconfig = wiki.config
|
|
||||||
app.use(mw.flash)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Controllers
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
app.use('/', ctrl.auth)
|
|
||||||
|
|
||||||
app.use('/graphql', graphqlApollo.graphqlExpress({ schema: graphqlSchema }))
|
|
||||||
app.use('/graphiql', graphqlApollo.graphiqlExpress({ endpointURL: '/graphql' }))
|
|
||||||
app.use('/uploads', mw.auth, ctrl.uploads)
|
|
||||||
app.use('/admin', mw.auth, ctrl.admin)
|
|
||||||
app.use('/', mw.auth, ctrl.pages)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Error handling
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
|
||||||
var err = new Error('Not Found')
|
|
||||||
err.status = 404
|
|
||||||
next(err)
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(function (err, req, res, next) {
|
|
||||||
res.status(err.status || 500)
|
|
||||||
res.render('error', {
|
|
||||||
message: err.message,
|
|
||||||
error: wiki.IS_DEBUG ? err : {}
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// ----------------------------------------
|
app.use(cookieParser())
|
||||||
// Start HTTP server
|
app.use(session({
|
||||||
// ----------------------------------------
|
name: 'wikijs.sid',
|
||||||
|
store: sessionStore,
|
||||||
|
secret: wiki.config.site.sessionSecret,
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: false
|
||||||
|
}))
|
||||||
|
app.use(flash())
|
||||||
|
app.use(passport.initialize())
|
||||||
|
app.use(passport.session())
|
||||||
|
|
||||||
wiki.logger.info('Starting HTTP/WS server on port ' + wiki.config.port + '...')
|
// ----------------------------------------
|
||||||
|
// SEO
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
app.set('port', wiki.config.port)
|
app.use(mw.seo)
|
||||||
var server = http.createServer(app)
|
|
||||||
var io = socketio(server)
|
|
||||||
|
|
||||||
server.listen(wiki.config.port)
|
// ----------------------------------------
|
||||||
server.on('error', (error) => {
|
// Localization Engine
|
||||||
if (error.syscall !== 'listen') {
|
// ----------------------------------------
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle specific listen errors with friendly messages
|
wiki.lang.use(i18nBackend).init({
|
||||||
switch (error.code) {
|
load: 'languageOnly',
|
||||||
case 'EACCES':
|
ns: ['common', 'admin', 'auth', 'errors', 'git'],
|
||||||
wiki.logger.error('Listening on port ' + wiki.config.port + ' requires elevated privileges!')
|
defaultNS: 'common',
|
||||||
return process.exit(1)
|
saveMissing: false,
|
||||||
case 'EADDRINUSE':
|
preload: [wiki.config.site.lang],
|
||||||
wiki.logger.error('Port ' + wiki.config.port + ' is already in use!')
|
lng: wiki.config.site.lang,
|
||||||
return process.exit(1)
|
fallbackLng: 'en',
|
||||||
default:
|
backend: {
|
||||||
|
loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// View Engine Setup
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
app.set('views', path.join(wiki.SERVERPATH, 'views'))
|
||||||
|
app.set('view engine', 'pug')
|
||||||
|
|
||||||
|
app.use(bodyParser.json({ limit: '1mb' }))
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false, limit: '1mb' }))
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// View accessible data
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
app.locals._ = require('lodash')
|
||||||
|
app.locals.t = wiki.lang.t.bind(wiki.config.site.lang)
|
||||||
|
app.locals.moment = require('moment')
|
||||||
|
app.locals.moment.locale(wiki.config.site.lang)
|
||||||
|
app.locals.appconfig = wiki.config
|
||||||
|
app.use(mw.flash)
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Controllers
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
app.use('/', ctrl.auth)
|
||||||
|
|
||||||
|
app.use('/graphql', graphqlApollo.graphqlExpress({ schema: graphqlSchema }))
|
||||||
|
app.use('/graphiql', graphqlApollo.graphiqlExpress({ endpointURL: '/graphql' }))
|
||||||
|
app.use('/uploads', mw.auth, ctrl.uploads)
|
||||||
|
app.use('/admin', mw.auth, ctrl.admin)
|
||||||
|
app.use('/', mw.auth, ctrl.pages)
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Error handling
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
var err = new Error('Not Found')
|
||||||
|
err.status = 404
|
||||||
|
next(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
res.status(err.status || 500)
|
||||||
|
res.render('error', {
|
||||||
|
message: err.message,
|
||||||
|
error: wiki.IS_DEBUG ? err : {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Start HTTP server
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
wiki.logger.info('Starting HTTP/WS server on port ' + wiki.config.port + '...')
|
||||||
|
|
||||||
|
app.set('port', wiki.config.port)
|
||||||
|
var server = http.createServer(app)
|
||||||
|
var io = socketio(server)
|
||||||
|
|
||||||
|
server.listen(wiki.config.port)
|
||||||
|
server.on('error', (error) => {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
server.on('listening', () => {
|
// handle specific listen errors with friendly messages
|
||||||
wiki.logger.info('HTTP/WS server started successfully! [RUNNING]')
|
switch (error.code) {
|
||||||
})
|
case 'EACCES':
|
||||||
|
wiki.logger.error('Listening on port ' + wiki.config.port + ' requires elevated privileges!')
|
||||||
// ----------------------------------------
|
return process.exit(1)
|
||||||
// WebSocket
|
case 'EADDRINUSE':
|
||||||
// ----------------------------------------
|
wiki.logger.error('Port ' + wiki.config.port + ' is already in use!')
|
||||||
|
return process.exit(1)
|
||||||
io.use(passportSocketIo.authorize({
|
default:
|
||||||
key: 'wikijs.sid',
|
throw error
|
||||||
store: sessionStore,
|
}
|
||||||
secret: wiki.config.sessionSecret,
|
|
||||||
cookieParser,
|
|
||||||
success: (data, accept) => {
|
|
||||||
accept()
|
|
||||||
},
|
|
||||||
fail: (data, message, error, accept) => {
|
|
||||||
accept()
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
io.on('connection', ctrl.ws)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Graceful shutdown
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
graceful.on('exit', () => {
|
|
||||||
// wiki.logger.info('- SHUTTING DOWN - Terminating Background Agent...')
|
|
||||||
// bgAgent.kill()
|
|
||||||
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()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
server.on('listening', () => {
|
||||||
|
wiki.logger.info('HTTP/WS server started successfully! [RUNNING]')
|
||||||
|
})
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// WebSocket
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
io.use(passportSocketIo.authorize({
|
||||||
|
key: 'wikijs.sid',
|
||||||
|
store: sessionStore,
|
||||||
|
secret: wiki.config.site.sessionSecret,
|
||||||
|
cookieParser,
|
||||||
|
success: (data, accept) => {
|
||||||
|
accept()
|
||||||
|
},
|
||||||
|
fail: (data, message, error, accept) => {
|
||||||
|
accept()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
io.on('connection', ctrl.ws)
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Graceful shutdown
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
graceful.on('exit', () => {
|
||||||
|
// wiki.logger.info('- SHUTTING DOWN - Terminating Background Agent...')
|
||||||
|
// bgAgent.kill()
|
||||||
|
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
|
||||||
|
}).catch(err => {
|
||||||
|
wiki.logger.error(err)
|
||||||
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
@ -24,232 +24,51 @@ module.exports = function (passport) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Local Account
|
|
||||||
|
|
||||||
if (wiki.config.auth.local && wiki.config.auth.local.enabled) {
|
|
||||||
const LocalStrategy = require('passport-local').Strategy
|
|
||||||
passport.use('local',
|
|
||||||
new LocalStrategy({
|
|
||||||
usernameField: 'email',
|
|
||||||
passwordField: 'password'
|
|
||||||
}, (uEmail, uPassword, done) => {
|
|
||||||
wiki.db.User.findOne({ email: uEmail, provider: 'local' }).then((user) => {
|
|
||||||
if (user) {
|
|
||||||
return user.validatePassword(uPassword).then(() => {
|
|
||||||
return done(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return done(err, null)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return done(new Error('INVALID_LOGIN'), null)
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
done(err, null)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Google ID
|
|
||||||
|
|
||||||
if (wiki.config.auth.google && wiki.config.auth.google.enabled) {
|
|
||||||
const GoogleStrategy = require('passport-google-oauth20').Strategy
|
|
||||||
passport.use('google',
|
|
||||||
new GoogleStrategy({
|
|
||||||
clientID: wiki.config.auth.google.clientId,
|
|
||||||
clientSecret: wiki.config.auth.google.clientSecret,
|
|
||||||
callbackURL: wiki.config.host + '/login/google/callback'
|
|
||||||
}, (accessToken, refreshToken, profile, cb) => {
|
|
||||||
wiki.db.User.processProfile(profile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Microsoft Accounts
|
|
||||||
|
|
||||||
if (wiki.config.auth.microsoft && wiki.config.auth.microsoft.enabled) {
|
|
||||||
const WindowsLiveStrategy = require('passport-windowslive').Strategy
|
|
||||||
passport.use('windowslive',
|
|
||||||
new WindowsLiveStrategy({
|
|
||||||
clientID: wiki.config.auth.microsoft.clientId,
|
|
||||||
clientSecret: wiki.config.auth.microsoft.clientSecret,
|
|
||||||
callbackURL: wiki.config.host + '/login/ms/callback'
|
|
||||||
}, function (accessToken, refreshToken, profile, cb) {
|
|
||||||
wiki.db.User.processProfile(profile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Facebook
|
|
||||||
|
|
||||||
if (wiki.config.auth.facebook && wiki.config.auth.facebook.enabled) {
|
|
||||||
const FacebookStrategy = require('passport-facebook').Strategy
|
|
||||||
passport.use('facebook',
|
|
||||||
new FacebookStrategy({
|
|
||||||
clientID: wiki.config.auth.facebook.clientId,
|
|
||||||
clientSecret: wiki.config.auth.facebook.clientSecret,
|
|
||||||
callbackURL: wiki.config.host + '/login/facebook/callback',
|
|
||||||
profileFields: ['id', 'displayName', 'email']
|
|
||||||
}, function (accessToken, refreshToken, profile, cb) {
|
|
||||||
wiki.db.User.processProfile(profile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitHub
|
|
||||||
|
|
||||||
if (wiki.config.auth.github && wiki.config.auth.github.enabled) {
|
|
||||||
const GitHubStrategy = require('passport-github2').Strategy
|
|
||||||
passport.use('github',
|
|
||||||
new GitHubStrategy({
|
|
||||||
clientID: wiki.config.auth.github.clientId,
|
|
||||||
clientSecret: wiki.config.auth.github.clientSecret,
|
|
||||||
callbackURL: wiki.config.host + '/login/github/callback',
|
|
||||||
scope: ['user:email']
|
|
||||||
}, (accessToken, refreshToken, profile, cb) => {
|
|
||||||
wiki.db.User.processProfile(profile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slack
|
|
||||||
|
|
||||||
if (wiki.config.auth.slack && wiki.config.auth.slack.enabled) {
|
|
||||||
const SlackStrategy = require('passport-slack').Strategy
|
|
||||||
passport.use('slack',
|
|
||||||
new SlackStrategy({
|
|
||||||
clientID: wiki.config.auth.slack.clientId,
|
|
||||||
clientSecret: wiki.config.auth.slack.clientSecret,
|
|
||||||
callbackURL: wiki.config.host + '/login/slack/callback'
|
|
||||||
}, (accessToken, refreshToken, profile, cb) => {
|
|
||||||
wiki.db.User.processProfile(profile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// LDAP
|
|
||||||
|
|
||||||
if (wiki.config.auth.ldap && wiki.config.auth.ldap.enabled) {
|
|
||||||
const LdapStrategy = require('passport-ldapauth').Strategy
|
|
||||||
passport.use('ldapauth',
|
|
||||||
new LdapStrategy({
|
|
||||||
server: {
|
|
||||||
url: wiki.config.auth.ldap.url,
|
|
||||||
bindDn: wiki.config.auth.ldap.bindDn,
|
|
||||||
bindCredentials: wiki.config.auth.ldap.bindCredentials,
|
|
||||||
searchBase: wiki.config.auth.ldap.searchBase,
|
|
||||||
searchFilter: wiki.config.auth.ldap.searchFilter,
|
|
||||||
searchAttributes: ['displayName', 'name', 'cn', 'mail'],
|
|
||||||
tlsOptions: (wiki.config.auth.ldap.tlsEnabled) ? {
|
|
||||||
ca: [
|
|
||||||
fs.readFileSync(wiki.config.auth.ldap.tlsCertPath)
|
|
||||||
]
|
|
||||||
} : {}
|
|
||||||
},
|
|
||||||
usernameField: 'email',
|
|
||||||
passReqToCallback: false
|
|
||||||
}, (profile, cb) => {
|
|
||||||
profile.provider = 'ldap'
|
|
||||||
profile.id = profile.dn
|
|
||||||
wiki.db.User.processProfile(profile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AZURE AD
|
|
||||||
|
|
||||||
if (wiki.config.auth.azure && wiki.config.auth.azure.enabled) {
|
|
||||||
const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy
|
|
||||||
const jwt = require('jsonwebtoken')
|
|
||||||
passport.use('azure_ad_oauth2',
|
|
||||||
new AzureAdOAuth2Strategy({
|
|
||||||
clientID: wiki.config.auth.azure.clientId,
|
|
||||||
clientSecret: wiki.config.auth.azure.clientSecret,
|
|
||||||
callbackURL: wiki.config.host + '/login/azure/callback',
|
|
||||||
resource: wiki.config.auth.azure.resource,
|
|
||||||
tenant: wiki.config.auth.azure.tenant
|
|
||||||
}, (accessToken, refreshToken, params, profile, cb) => {
|
|
||||||
let waadProfile = jwt.decode(params.id_token)
|
|
||||||
waadProfile.id = waadProfile.oid
|
|
||||||
waadProfile.provider = 'azure'
|
|
||||||
wiki.db.User.processProfile(waadProfile).then((user) => {
|
|
||||||
return cb(null, user) || true
|
|
||||||
}).catch((err) => {
|
|
||||||
return cb(err, null) || true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create users for first-time
|
// Create users for first-time
|
||||||
|
|
||||||
wiki.db.onReady.then(() => {
|
return wiki.db.User.findOne({ provider: 'local', email: 'guest@example.com' }).then((c) => {
|
||||||
return wiki.db.User.findOne({ provider: 'local', email: 'guest' }).then((c) => {
|
if (c < 1) {
|
||||||
if (c < 1) {
|
// Create guest account
|
||||||
// Create guest account
|
|
||||||
|
|
||||||
return wiki.db.User.create({
|
return wiki.db.User.create({
|
||||||
provider: 'local',
|
provider: 'local',
|
||||||
email: 'guest@example.com',
|
email: 'guest@example.com',
|
||||||
name: 'Guest',
|
name: 'Guest',
|
||||||
password: '',
|
password: '',
|
||||||
role: 'guest'
|
role: 'guest'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
wiki.logger.info('[AUTH] Guest account created successfully!')
|
wiki.logger.info('[AUTH] Guest account created successfully!')
|
||||||
return true
|
return true
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
wiki.logger.error('[AUTH] An error occured while creating guest account:')
|
wiki.logger.error('[AUTH] An error occured while creating guest account:')
|
||||||
wiki.logger.error(err)
|
wiki.logger.error(err)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).then(() => {
|
|
||||||
if (process.env.WIKI_JS_HEROKU) {
|
|
||||||
return wiki.db.User.findOne({ provider: 'local', email: process.env.WIKI_ADMIN_EMAIL }).then((c) => {
|
|
||||||
if (c < 1) {
|
|
||||||
// Create root admin account (HEROKU ONLY)
|
|
||||||
|
|
||||||
return wiki.db.User.create({
|
|
||||||
provider: 'local',
|
|
||||||
email: process.env.WIKI_ADMIN_EMAIL,
|
|
||||||
name: 'Administrator',
|
|
||||||
password: '$2a$04$MAHRw785Xe/Jd5kcKzr3D.VRZDeomFZu2lius4gGpZZ9cJw7B7Mna', // admin123 (default)
|
|
||||||
role: 'admin'
|
|
||||||
}).then(() => {
|
|
||||||
wiki.logger.info('[AUTH] Root admin account created successfully!')
|
|
||||||
return true
|
|
||||||
}).catch((err) => {
|
|
||||||
wiki.logger.error('[AUTH] An error occured while creating root admin account:')
|
|
||||||
wiki.logger.error(err)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
} else { return true }
|
|
||||||
})
|
|
||||||
} else { return true }
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// .then(() => {
|
||||||
|
// if (process.env.WIKI_JS_HEROKU) {
|
||||||
|
// return wiki.db.User.findOne({ provider: 'local', email: process.env.WIKI_ADMIN_EMAIL }).then((c) => {
|
||||||
|
// if (c < 1) {
|
||||||
|
// // Create root admin account (HEROKU ONLY)
|
||||||
|
|
||||||
|
// return wiki.db.User.create({
|
||||||
|
// provider: 'local',
|
||||||
|
// email: process.env.WIKI_ADMIN_EMAIL,
|
||||||
|
// name: 'Administrator',
|
||||||
|
// password: '$2a$04$MAHRw785Xe/Jd5kcKzr3D.VRZDeomFZu2lius4gGpZZ9cJw7B7Mna', // admin123 (default)
|
||||||
|
// role: 'admin'
|
||||||
|
// }).then(() => {
|
||||||
|
// wiki.logger.info('[AUTH] Root admin account created successfully!')
|
||||||
|
// return true
|
||||||
|
// }).catch((err) => {
|
||||||
|
// wiki.logger.error('[AUTH] An error occured while creating root admin account:')
|
||||||
|
// wiki.logger.error(err)
|
||||||
|
// return err
|
||||||
|
// })
|
||||||
|
// } else { return true }
|
||||||
|
// })
|
||||||
|
// } else { return true }
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
@ -8,62 +8,95 @@ const _ = require('lodash')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const cfgHelper = require('../helpers/config')
|
const cfgHelper = require('../helpers/config')
|
||||||
|
|
||||||
/**
|
module.exports = {
|
||||||
* Load Application Configuration
|
SUBSETS: ['auth', 'features', 'git', 'logging', 'site', 'theme', 'uploads'],
|
||||||
*
|
|
||||||
* @param {Object} confPaths Path to the configuration files
|
|
||||||
* @return {Object} Application Configuration
|
|
||||||
*/
|
|
||||||
module.exports = (confPaths) => {
|
|
||||||
confPaths = _.defaults(confPaths, {
|
|
||||||
config: path.join(wiki.ROOTPATH, 'config.yml'),
|
|
||||||
data: path.join(wiki.SERVERPATH, 'app/data.yml'),
|
|
||||||
dataRegex: path.join(wiki.SERVERPATH, 'app/regex.js')
|
|
||||||
})
|
|
||||||
|
|
||||||
let appconfig = {}
|
/**
|
||||||
let appdata = {}
|
* Load root config from disk
|
||||||
|
*
|
||||||
|
* @param {any} confPaths
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
let confPaths = {
|
||||||
|
config: path.join(wiki.ROOTPATH, 'config.yml'),
|
||||||
|
data: path.join(wiki.SERVERPATH, 'app/data.yml'),
|
||||||
|
dataRegex: path.join(wiki.SERVERPATH, 'app/regex.js')
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
let appconfig = {}
|
||||||
appconfig = yaml.safeLoad(
|
let appdata = {}
|
||||||
cfgHelper.parseConfigValue(
|
|
||||||
fs.readFileSync(confPaths.config, 'utf8')
|
try {
|
||||||
|
appconfig = yaml.safeLoad(
|
||||||
|
cfgHelper.parseConfigValue(
|
||||||
|
fs.readFileSync(confPaths.config, 'utf8')
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
appdata = yaml.safeLoad(fs.readFileSync(confPaths.data, 'utf8'))
|
||||||
appdata = yaml.safeLoad(fs.readFileSync(confPaths.data, 'utf8'))
|
appdata.regex = require(confPaths.dataRegex)
|
||||||
appdata.regex = require(confPaths.dataRegex)
|
} catch (ex) {
|
||||||
} catch (ex) {
|
console.error(ex)
|
||||||
console.error(ex)
|
process.exit(1)
|
||||||
process.exit(1)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Merge with defaults
|
// Merge with defaults
|
||||||
|
|
||||||
appconfig = _.defaultsDeep(appconfig, appdata.defaults.config)
|
appconfig = _.defaultsDeep(appconfig, appdata.defaults.config)
|
||||||
|
|
||||||
// Check port
|
// Check port
|
||||||
|
|
||||||
if (appconfig.port < 1) {
|
if (appconfig.port < 1) {
|
||||||
appconfig.port = process.env.PORT || 80
|
appconfig.port = process.env.PORT || 80
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert booleans
|
// Convert booleans
|
||||||
|
|
||||||
appconfig.public = (appconfig.public === true || _.toLower(appconfig.public) === 'true')
|
appconfig.public = (appconfig.public === true || _.toLower(appconfig.public) === 'true')
|
||||||
|
|
||||||
// List authentication strategies
|
// List authentication strategies
|
||||||
|
wiki.config = appconfig
|
||||||
|
wiki.data = appdata
|
||||||
|
|
||||||
appconfig.authStrategies = {
|
// List authentication strategies
|
||||||
list: _.filter(appconfig.auth, ['enabled', true]),
|
|
||||||
socialEnabled: (_.chain(appconfig.auth).omit(['local', 'ldap']).filter(['enabled', true]).value().length > 0)
|
|
||||||
}
|
|
||||||
if (appconfig.authStrategies.list.length < 1) {
|
|
||||||
console.error(new Error('You must enable at least 1 authentication strategy!'))
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
// appconfig.authStrategies = {
|
||||||
config: appconfig,
|
// list: _.filter(appconfig.auth, ['enabled', true]),
|
||||||
data: appdata
|
// socialEnabled: (_.chain(appconfig.auth).omit('local').filter(['enabled', true]).value().length > 0)
|
||||||
|
// }
|
||||||
|
// if (appconfig.authStrategies.list.length < 1) {
|
||||||
|
// console.error(new Error('You must enable at least 1 authentication strategy!'))
|
||||||
|
// process.exit(1)
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load config from DB
|
||||||
|
*
|
||||||
|
* @param {Array} subsets Array of subsets to load
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
loadFromDb(subsets) {
|
||||||
|
if (!_.isArray(subsets) || subsets.length === 0) {
|
||||||
|
subsets = this.SUBSETS
|
||||||
|
}
|
||||||
|
|
||||||
|
return wiki.db.Setting.findAll({
|
||||||
|
attributes: ['key', 'config'],
|
||||||
|
where: {
|
||||||
|
key: {
|
||||||
|
$in: subsets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).then(results => {
|
||||||
|
if (_.isArray(results) && results.length > 0) {
|
||||||
|
results.forEach(result => {
|
||||||
|
wiki.config[result.key] = result.config
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error('Invalid DB Configuration result set'))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
const Promise = require('bluebird')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PostgreSQL DB module
|
* PostgreSQL DB module
|
||||||
@ -33,7 +34,8 @@ module.exports = {
|
|||||||
max: 10,
|
max: 10,
|
||||||
min: 0,
|
min: 0,
|
||||||
idle: 10000
|
idle: 10000
|
||||||
}
|
},
|
||||||
|
logging: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// Attempt to connect and authenticate to DB
|
// Attempt to connect and authenticate to DB
|
||||||
@ -63,10 +65,10 @@ module.exports = {
|
|||||||
|
|
||||||
// Sync DB
|
// Sync DB
|
||||||
|
|
||||||
self.onReady = self.inst.sync({
|
self.onReady = (wiki.IS_MASTER) ? self.inst.sync({
|
||||||
force: false,
|
force: false,
|
||||||
logging: false
|
logging: false
|
||||||
})
|
}) : Promise.resolve()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ module.exports = {
|
|||||||
this._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
|
this._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
|
||||||
|
|
||||||
this.createBaseDirectories()
|
this.createBaseDirectories()
|
||||||
this.initMulter()
|
// this.initMulter()
|
||||||
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
@ -37,8 +37,10 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
initMulter () {
|
initMulter () {
|
||||||
let maxFileSizes = {
|
let maxFileSizes = {
|
||||||
img: wiki.config.uploads.maxImageFileSize * 1024 * 1024,
|
// img: wiki.config.uploads.maxImageFileSize * 1024 * 1024,
|
||||||
file: wiki.config.uploads.maxOtherFileSize * 1024 * 1024
|
// file: wiki.config.uploads.maxOtherFileSize * 1024 * 1024
|
||||||
|
img: 3 * 1024 * 1024,
|
||||||
|
file: 10 * 1024 * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
// -> IMAGES
|
// -> IMAGES
|
||||||
|
@ -51,7 +51,7 @@ module.exports = {
|
|||||||
|
|
||||||
// -> Initialize repository
|
// -> Initialize repository
|
||||||
|
|
||||||
self.onReady = self._initRepo()
|
self.onReady = (wiki.IS_MASTER) ? self._initRepo() : Promise.resolve()
|
||||||
|
|
||||||
// Define signature
|
// Define signature
|
||||||
|
|
||||||
|
@ -4,75 +4,77 @@
|
|||||||
|
|
||||||
const cluster = require('cluster')
|
const cluster = require('cluster')
|
||||||
|
|
||||||
module.exports = () => {
|
module.exports = {
|
||||||
let winston = require('winston')
|
init() {
|
||||||
|
let winston = require('winston')
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
|
|
||||||
let logger = new (winston.Logger)({
|
let logger = new (winston.Logger)({
|
||||||
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
|
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
|
||||||
transports: [
|
transports: [
|
||||||
new (winston.transports.Console)({
|
new (winston.transports.Console)({
|
||||||
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
|
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
|
||||||
prettyPrint: true,
|
prettyPrint: true,
|
||||||
colorize: true,
|
colorize: true,
|
||||||
silent: false,
|
silent: false,
|
||||||
timestamp: true
|
timestamp: true
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
})
|
|
||||||
|
|
||||||
logger.filters.push((level, msg) => {
|
|
||||||
let processName = (cluster.isMaster) ? 'MASTER' : `WORKER-${cluster.worker.id}`
|
|
||||||
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) {
|
logger.filters.push((level, msg) => {
|
||||||
require('winston-loggly-bulk')
|
let processName = (cluster.isMaster) ? 'MASTER' : `WORKER-${cluster.worker.id}`
|
||||||
logger.add(winston.transports.Loggly, {
|
return '[' + processName + '] ' + msg
|
||||||
token: wiki.config.externalLogging.loggly.token,
|
|
||||||
subdomain: wiki.config.externalLogging.loggly.subdomain,
|
|
||||||
tags: ['wiki-js'],
|
|
||||||
level: 'warn',
|
|
||||||
json: true
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if (wiki.config.externalLogging.papertrail) {
|
// External services
|
||||||
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) {
|
// if (wiki.config.externalLogging.bugsnag) {
|
||||||
const rollbarTransport = require('./winston-transports/rollbar')
|
// const bugsnagTransport = require('./winston-transports/bugsnag')
|
||||||
logger.add(rollbarTransport, {
|
// logger.add(bugsnagTransport, {
|
||||||
level: 'warn',
|
// level: 'warn',
|
||||||
key: wiki.config.externalLogging.rollbar
|
// key: wiki.config.externalLogging.bugsnag
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (wiki.config.externalLogging.sentry) {
|
// if (wiki.config.externalLogging.loggly) {
|
||||||
const sentryTransport = require('./winston-transports/sentry')
|
// require('winston-loggly-bulk')
|
||||||
logger.add(sentryTransport, {
|
// logger.add(winston.transports.Loggly, {
|
||||||
level: 'warn',
|
// token: wiki.config.externalLogging.loggly.token,
|
||||||
key: wiki.config.externalLogging.sentry
|
// subdomain: wiki.config.externalLogging.loggly.subdomain,
|
||||||
})
|
// tags: ['wiki-js'],
|
||||||
}
|
// level: 'warn',
|
||||||
|
// json: true
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
return logger
|
// 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
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
return logger
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ const mdRemove = require('remove-markdown')
|
|||||||
|
|
||||||
var mkdown = md({
|
var mkdown = md({
|
||||||
html: true,
|
html: true,
|
||||||
breaks: wiki.config.features.linebreaks,
|
// breaks: wiki.config.features.linebreaks,
|
||||||
|
breaks: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
typography: true,
|
typography: true,
|
||||||
highlight(str, lang) {
|
highlight(str, lang) {
|
||||||
@ -57,7 +58,8 @@ var mkdown = md({
|
|||||||
})
|
})
|
||||||
.use(mdAttrs)
|
.use(mdAttrs)
|
||||||
|
|
||||||
if (wiki.config.features.mathjax) {
|
// if (wiki.config.features.mathjax) {
|
||||||
|
if (true) {
|
||||||
mkdown.use(mdMathjax)
|
mkdown.use(mdMathjax)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/* global db, git, lang, upl */
|
/* global wiki */
|
||||||
|
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const Promise = require('bluebird')
|
const Promise = require('bluebird')
|
||||||
@ -32,8 +32,8 @@ module.exports = {
|
|||||||
init () {
|
init () {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
self._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
|
self._uploadsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.repo, 'uploads')
|
||||||
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
|
self._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
|
||||||
|
|
||||||
return self
|
return self
|
||||||
},
|
},
|
||||||
@ -59,16 +59,16 @@ module.exports = {
|
|||||||
self._watcher.on('add', (p) => {
|
self._watcher.on('add', (p) => {
|
||||||
let pInfo = self.parseUploadsRelPath(p)
|
let pInfo = self.parseUploadsRelPath(p)
|
||||||
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => {
|
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => {
|
||||||
return db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true })
|
return wiki.db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true })
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return git.commitUploads(lang.t('git:uploaded', { path: p }))
|
return wiki.git.commitUploads(wiki.lang.t('git:uploaded', { path: p }))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// -> Remove upload file
|
// -> Remove upload file
|
||||||
|
|
||||||
self._watcher.on('unlink', (p) => {
|
self._watcher.on('unlink', (p) => {
|
||||||
return git.commitUploads(lang.t('git:deleted', { path: p }))
|
return wiki.git.commitUploads(wiki.lang.t('git:deleted', { path: p }))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -91,8 +91,8 @@ module.exports = {
|
|||||||
|
|
||||||
// Add folders to DB
|
// Add folders to DB
|
||||||
|
|
||||||
return db.UplFolder.remove({}).then(() => {
|
return wiki.db.UplFolder.remove({}).then(() => {
|
||||||
return db.UplFolder.insertMany(_.map(folderNames, (f) => {
|
return wiki.db.UplFolder.insertMany(_.map(folderNames, (f) => {
|
||||||
return {
|
return {
|
||||||
_id: 'f:' + f,
|
_id: 'f:' + f,
|
||||||
name: f
|
name: f
|
||||||
@ -107,7 +107,7 @@ module.exports = {
|
|||||||
let fldPath = path.join(self._uploadsPath, fldName)
|
let fldPath = path.join(self._uploadsPath, fldName)
|
||||||
return fs.readdirAsync(fldPath).then((fList) => {
|
return fs.readdirAsync(fldPath).then((fList) => {
|
||||||
return Promise.map(fList, (f) => {
|
return Promise.map(fList, (f) => {
|
||||||
return upl.processFile(fldName, f).then((mData) => {
|
return wiki.upl.processFile(fldName, f).then((mData) => {
|
||||||
if (mData) {
|
if (mData) {
|
||||||
allFiles.push(mData)
|
allFiles.push(mData)
|
||||||
}
|
}
|
||||||
@ -118,9 +118,9 @@ module.exports = {
|
|||||||
}, {concurrency: 1}).finally(() => {
|
}, {concurrency: 1}).finally(() => {
|
||||||
// Add files to DB
|
// Add files to DB
|
||||||
|
|
||||||
return db.UplFile.remove({}).then(() => {
|
return wiki.db.UplFile.remove({}).then(() => {
|
||||||
if (_.isArray(allFiles) && allFiles.length > 0) {
|
if (_.isArray(allFiles) && allFiles.length > 0) {
|
||||||
return db.UplFile.insertMany(allFiles)
|
return wiki.db.UplFile.insertMany(allFiles)
|
||||||
} else {
|
} else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ module.exports = {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Watch for new changes
|
// Watch for new changes
|
||||||
|
|
||||||
return upl.watch()
|
return wiki.upl.watch()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
65
server/queues/git-sync.js
Normal file
65
server/queues/git-sync.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
const Promise = require('bluebird')
|
||||||
|
const fs = Promise.promisifyAll(require('fs-extra'))
|
||||||
|
const klaw = require('klaw')
|
||||||
|
const moment = require('moment')
|
||||||
|
const path = require('path')
|
||||||
|
const entryHelper = require('../helpers/entry')
|
||||||
|
|
||||||
|
module.exports = (job, done) => {
|
||||||
|
return wiki.git.resync().then(() => {
|
||||||
|
// -> Stream all documents
|
||||||
|
|
||||||
|
let cacheJobs = []
|
||||||
|
let jobCbStreamDocsResolve = null
|
||||||
|
let jobCbStreamDocs = new Promise((resolve, reject) => {
|
||||||
|
jobCbStreamDocsResolve = resolve
|
||||||
|
})
|
||||||
|
|
||||||
|
klaw(wiki.REPOPATH).on('data', function (item) {
|
||||||
|
if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
|
||||||
|
let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path))
|
||||||
|
let cachePath = entryHelper.getCachePath(entryPath)
|
||||||
|
|
||||||
|
// -> Purge outdated cache
|
||||||
|
|
||||||
|
cacheJobs.push(
|
||||||
|
fs.statAsync(cachePath).then((st) => {
|
||||||
|
return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
|
||||||
|
}).catch((err) => {
|
||||||
|
return (err.code !== 'EEXIST') ? err : 'new'
|
||||||
|
}).then((fileStatus) => {
|
||||||
|
// -> Delete expired cache file
|
||||||
|
|
||||||
|
if (fileStatus === 'expired') {
|
||||||
|
return fs.unlinkAsync(cachePath).return(fileStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileStatus
|
||||||
|
}).then((fileStatus) => {
|
||||||
|
// -> Update cache and search index
|
||||||
|
|
||||||
|
if (fileStatus !== 'active') {
|
||||||
|
return global.entries.updateCache(entryPath).then(entry => {
|
||||||
|
process.send({
|
||||||
|
action: 'searchAdd',
|
||||||
|
content: entry
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).on('end', () => {
|
||||||
|
jobCbStreamDocsResolve(Promise.all(cacheJobs))
|
||||||
|
})
|
||||||
|
|
||||||
|
return jobCbStreamDocs
|
||||||
|
})
|
||||||
|
}
|
26
server/queues/upl-clear-temp.js
Normal file
26
server/queues/upl-clear-temp.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
const Promise = require('bluebird')
|
||||||
|
const fs = Promise.promisifyAll(require('fs-extra'))
|
||||||
|
const moment = require('moment')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = (job, done) => {
|
||||||
|
return fs.readdirAsync(wiki.UPLTEMPPATH).then((ls) => {
|
||||||
|
let fifteenAgo = moment().subtract(15, 'minutes')
|
||||||
|
|
||||||
|
return Promise.map(ls, (f) => {
|
||||||
|
return fs.statAsync(path.join(wiki.UPLTEMPPATH, f)).then((s) => { return { filename: f, stat: s } })
|
||||||
|
}).filter((s) => { return s.stat.isFile() }).then((arrFiles) => {
|
||||||
|
return Promise.map(arrFiles, (f) => {
|
||||||
|
if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) {
|
||||||
|
return fs.unlinkAsync(path.join(wiki.UPLTEMPPATH, f.filename))
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
170
server/worker.js
170
server/worker.js
@ -4,30 +4,27 @@
|
|||||||
|
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
wiki.REPOPATH = path.resolve(wiki.ROOTPATH, wiki.config.paths.repo)
|
||||||
|
wiki.DATAPATH = path.resolve(wiki.ROOTPATH, wiki.config.paths.data)
|
||||||
|
wiki.UPLTEMPPATH = path.join(wiki.DATAPATH, 'temp-upload')
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Load global modules
|
// Load global modules
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
wiki.db = require('./modules/db').init()
|
// wiki.upl = require('./modules/uploads-agent').init()
|
||||||
wiki.upl = require('./modules/uploads-agent').init()
|
// wiki.git = require('./modules/git').init()
|
||||||
wiki.git = require('./modules/git').init()
|
// wiki.entries = require('./modules/entries').init()
|
||||||
wiki.entries = require('./modules/entries').init()
|
|
||||||
wiki.lang = require('i18next')
|
wiki.lang = require('i18next')
|
||||||
wiki.mark = require('./modules/markdown')
|
wiki.mark = require('./modules/markdown')
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Load modules
|
// Load local modules
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
const moment = require('moment')
|
|
||||||
const Promise = require('bluebird')
|
const Promise = require('bluebird')
|
||||||
const fs = Promise.promisifyAll(require('fs-extra'))
|
|
||||||
const klaw = require('klaw')
|
|
||||||
const Cron = require('cron').CronJob
|
|
||||||
const i18nBackend = require('i18next-node-fs-backend')
|
const i18nBackend = require('i18next-node-fs-backend')
|
||||||
|
|
||||||
const entryHelper = require('./helpers/entry')
|
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Localization Engine
|
// Localization Engine
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@ -46,143 +43,21 @@ wiki.lang.use(i18nBackend).init({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Start Cron
|
// Start Queues
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
let job
|
const Bull = require('bull')
|
||||||
let jobIsBusy = false
|
const autoload = require('auto-load')
|
||||||
let jobUplWatchStarted = false
|
|
||||||
|
|
||||||
wiki.db.onReady.then(() => {
|
let queues = autoload(path.join(wiki.SERVERPATH, 'queues'))
|
||||||
return wiki.db.Entry.remove({})
|
|
||||||
}).then(() => {
|
|
||||||
job = new Cron({
|
|
||||||
cronTime: '0 */5 * * * *',
|
|
||||||
onTick: () => {
|
|
||||||
// Make sure we don't start two concurrent jobs
|
|
||||||
|
|
||||||
if (jobIsBusy) {
|
Promise.join(
|
||||||
wiki.logger.warn('Previous job has not completed gracefully or is still running! Skipping for now. (This is not normal, you should investigate)')
|
wiki.db.onReady
|
||||||
return
|
// wiki.upl.initialScan()
|
||||||
}
|
).then(() => {
|
||||||
wiki.logger.info('Running all jobs...')
|
for (let queueName in queues) {
|
||||||
jobIsBusy = true
|
new Bull(queueName, { redis: wiki.config.redis }).process(queues[queueName])
|
||||||
|
}
|
||||||
// Prepare async job collector
|
|
||||||
|
|
||||||
let jobs = []
|
|
||||||
let repoPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.repo)
|
|
||||||
let dataPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data)
|
|
||||||
let uploadsTempPath = path.join(dataPath, 'temp-upload')
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// REGULAR JOBS
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
//* ****************************************
|
|
||||||
// -> Sync with Git remote
|
|
||||||
//* ****************************************
|
|
||||||
|
|
||||||
jobs.push(wiki.git.resync().then(() => {
|
|
||||||
// -> Stream all documents
|
|
||||||
|
|
||||||
let cacheJobs = []
|
|
||||||
let jobCbStreamDocsResolve = null
|
|
||||||
let jobCbStreamDocs = new Promise((resolve, reject) => {
|
|
||||||
jobCbStreamDocsResolve = resolve
|
|
||||||
})
|
|
||||||
|
|
||||||
klaw(repoPath).on('data', function (item) {
|
|
||||||
if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
|
|
||||||
let entryPath = entryHelper.parsePath(entryHelper.getEntryPathFromFullPath(item.path))
|
|
||||||
let cachePath = entryHelper.getCachePath(entryPath)
|
|
||||||
|
|
||||||
// -> Purge outdated cache
|
|
||||||
|
|
||||||
cacheJobs.push(
|
|
||||||
fs.statAsync(cachePath).then((st) => {
|
|
||||||
return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
|
|
||||||
}).catch((err) => {
|
|
||||||
return (err.code !== 'EEXIST') ? err : 'new'
|
|
||||||
}).then((fileStatus) => {
|
|
||||||
// -> Delete expired cache file
|
|
||||||
|
|
||||||
if (fileStatus === 'expired') {
|
|
||||||
return fs.unlinkAsync(cachePath).return(fileStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileStatus
|
|
||||||
}).then((fileStatus) => {
|
|
||||||
// -> Update cache and search index
|
|
||||||
|
|
||||||
if (fileStatus !== 'active') {
|
|
||||||
return global.entries.updateCache(entryPath).then(entry => {
|
|
||||||
process.send({
|
|
||||||
action: 'searchAdd',
|
|
||||||
content: entry
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}).on('end', () => {
|
|
||||||
jobCbStreamDocsResolve(Promise.all(cacheJobs))
|
|
||||||
})
|
|
||||||
|
|
||||||
return jobCbStreamDocs
|
|
||||||
}))
|
|
||||||
|
|
||||||
//* ****************************************
|
|
||||||
// -> Clear failed temporary upload files
|
|
||||||
//* ****************************************
|
|
||||||
|
|
||||||
jobs.push(
|
|
||||||
fs.readdirAsync(uploadsTempPath).then((ls) => {
|
|
||||||
let fifteenAgo = moment().subtract(15, 'minutes')
|
|
||||||
|
|
||||||
return Promise.map(ls, (f) => {
|
|
||||||
return fs.statAsync(path.join(uploadsTempPath, f)).then((s) => { return { filename: f, stat: s } })
|
|
||||||
}).filter((s) => { return s.stat.isFile() }).then((arrFiles) => {
|
|
||||||
return Promise.map(arrFiles, (f) => {
|
|
||||||
if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) {
|
|
||||||
return fs.unlinkAsync(path.join(uploadsTempPath, f.filename))
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Run
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
Promise.all(jobs).then(() => {
|
|
||||||
wiki.logger.info('All jobs completed successfully! Going to sleep for now.')
|
|
||||||
|
|
||||||
if (!jobUplWatchStarted) {
|
|
||||||
jobUplWatchStarted = true
|
|
||||||
wiki.upl.initialScan().then(() => {
|
|
||||||
job.start()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}).catch((err) => {
|
|
||||||
wiki.logger.error('One or more jobs have failed: ', err)
|
|
||||||
}).finally(() => {
|
|
||||||
jobIsBusy = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
start: false,
|
|
||||||
timeZone: 'UTC',
|
|
||||||
runOnInit: true
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@ -190,11 +65,6 @@ wiki.db.onReady.then(() => {
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
process.on('disconnect', () => {
|
process.on('disconnect', () => {
|
||||||
wiki.logger.warn('Lost connection to main server. Exiting...')
|
wiki.logger.warn('Lost connection to Master. Exiting...')
|
||||||
job.stop()
|
|
||||||
process.exit()
|
process.exit()
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on('exit', () => {
|
|
||||||
job.stop()
|
|
||||||
})
|
|
||||||
|
@ -1609,7 +1609,7 @@ cron-parser@^2.4.1:
|
|||||||
is-nan "^1.2.1"
|
is-nan "^1.2.1"
|
||||||
moment-timezone "^0.5.0"
|
moment-timezone "^0.5.0"
|
||||||
|
|
||||||
cron@1.2.1, cron@~1.2.1:
|
cron@1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/cron/-/cron-1.2.1.tgz#3a86c09b41b8f261ac863a7cc85ea4735857eab2"
|
resolved "https://registry.yarnpkg.com/cron/-/cron-1.2.1.tgz#3a86c09b41b8f261ac863a7cc85ea4735857eab2"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user