} Resolve on success
- */
- deleteDocument(entryPath, author) {
- let self = this
- let gitFilePath = entryPath + '.md'
-
- return this._git.exec('rm', [gitFilePath]).then((cProc) => {
- let out = cProc.stdout.toString()
- if (_.includes(out, 'fatal')) {
- let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ',')))
- throw new Error(errorMsg)
- }
- let commitUsr = securityHelper.sanitizeCommitUser(author)
- return self._git.exec('commit', ['-m', lang.t('git:deleted', { path: gitFilePath }), '--author="' + commitUsr.name + ' <' + commitUsr.email + '>"']).catch((err) => {
- if (_.includes(err.stdout, 'nothing to commit')) { return true }
- })
- })
- },
-
/**
* Commits uploads changes.
*
diff --git a/server/modules/graphql.js b/server/modules/graphql.js
new file mode 100644
index 00000000..aa6a6f7d
--- /dev/null
+++ b/server/modules/graphql.js
@@ -0,0 +1,43 @@
+'use strict'
+
+/* global wiki */
+
+const gqlTools = require('graphql-tools')
+const fs = require('fs')
+const path = require('path')
+const _ = require('lodash')
+
+const typeDefs = fs.readFileSync(path.join(wiki.SERVERPATH, 'schemas/types.graphql'), 'utf8')
+
+const DateScalar = require('../schemas/scalar-date')
+const AuthenticationResolvers = require('../schemas/resolvers-authentication')
+const CommentResolvers = require('../schemas/resolvers-comment')
+const DocumentResolvers = require('../schemas/resolvers-document')
+const FileResolvers = require('../schemas/resolvers-file')
+const FolderResolvers = require('../schemas/resolvers-folder')
+const GroupResolvers = require('../schemas/resolvers-group')
+const SettingResolvers = require('../schemas/resolvers-setting')
+const TagResolvers = require('../schemas/resolvers-tag')
+const TranslationResolvers = require('../schemas/resolvers-translation')
+const UserResolvers = require('../schemas/resolvers-user')
+
+const resolvers = _.merge(
+ AuthenticationResolvers,
+ CommentResolvers,
+ DocumentResolvers,
+ FileResolvers,
+ FolderResolvers,
+ GroupResolvers,
+ SettingResolvers,
+ TagResolvers,
+ TranslationResolvers,
+ UserResolvers,
+ DateScalar
+)
+
+const Schema = gqlTools.makeExecutableSchema({
+ typeDefs,
+ resolvers
+})
+
+module.exports = Schema
diff --git a/server/modules/kernel.js b/server/modules/kernel.js
new file mode 100644
index 00000000..48e1659f
--- /dev/null
+++ b/server/modules/kernel.js
@@ -0,0 +1,91 @@
+const cluster = require('cluster')
+const Promise = require('bluebird')
+const _ = require('lodash')
+
+/* global wiki */
+
+module.exports = {
+ numWorkers: 1,
+ workers: [],
+ init() {
+ if (cluster.isMaster) {
+ wiki.logger.info('=======================================')
+ wiki.logger.info('= Wiki.js =============================')
+ wiki.logger.info('=======================================')
+
+ wiki.redis = require('./redis').init()
+ wiki.queue = require('./queue').init()
+
+ this.setWorkerLimit()
+ this.bootMaster()
+ } else {
+ this.bootWorker()
+ }
+ },
+ /**
+ * Pre-Master Boot Sequence
+ */
+ preBootMaster() {
+ return Promise.mapSeries([
+ () => { return wiki.db.onReady },
+ () => { return wiki.configSvc.loadFromDb() },
+ () => { return wiki.queue.clean() }
+ ], fn => { return fn() })
+ },
+ /**
+ * Boot Master Process
+ */
+ bootMaster() {
+ this.preBootMaster().then(sequenceResults => {
+ if (_.every(sequenceResults, rs => rs === true)) {
+ this.postBootMaster()
+ } else {
+ wiki.logger.info('Starting configuration manager...')
+ require('../configure')()
+ }
+ return true
+ }).catch(err => {
+ wiki.logger.error(err)
+ process.exit(1)
+ })
+ },
+ /**
+ * Post-Master Boot Sequence
+ */
+ postBootMaster() {
+ require('../master')().then(() => {
+ _.times(this.numWorker, this.spawnWorker)
+
+ wiki.queue.uplClearTemp.add({}, {
+ repeat: { cron: '*/15 * * * *' }
+ })
+ })
+
+ cluster.on('exit', (worker, code, signal) => {
+ wiki.logger.info(`Background Worker #${worker.id} was terminated.`)
+ })
+ },
+ /**
+ * Boot Worker Process
+ */
+ bootWorker() {
+ wiki.logger.info(`Background Worker #${cluster.worker.id} is initializing...`)
+ require('../worker')
+ },
+ /**
+ * Spawn new Worker process
+ */
+ spawnWorker() {
+ this.workers.push(cluster.fork())
+ },
+ /**
+ * Set Worker count based on config + system capabilities
+ */
+ setWorkerLimit() {
+ const numCPUs = require('os').cpus().length
+ this.numWorkers = (wiki.config.workers > 0) ? wiki.config.workers : numCPUs
+ if (this.numWorkers > numCPUs) {
+ this.numWorkers = numCPUs
+ }
+ }
+}
diff --git a/server/modules/localization.js b/server/modules/localization.js
new file mode 100644
index 00000000..d832935b
--- /dev/null
+++ b/server/modules/localization.js
@@ -0,0 +1,52 @@
+const _ = require('lodash')
+const dotize = require('dotize')
+const i18nBackend = require('i18next-node-fs-backend')
+const i18next = require('i18next')
+const path = require('path')
+const Promise = require('bluebird')
+
+/* global wiki */
+
+module.exports = {
+ engine: null,
+ namespaces: ['common', 'admin', 'auth', 'errors', 'git'],
+ init() {
+ this.engine = i18next
+ this.engine.use(i18nBackend).init({
+ load: 'languageOnly',
+ ns: this.namespaces,
+ defaultNS: 'common',
+ saveMissing: false,
+ preload: [wiki.config.site.lang],
+ lng: wiki.config.site.lang,
+ fallbackLng: 'en',
+ backend: {
+ loadPath: path.join(wiki.SERVERPATH, 'locales/{{lng}}/{{ns}}.json')
+ }
+ })
+ return this
+ },
+ getByNamespace(locale, namespace) {
+ if (this.engine.hasResourceBundle(locale, namespace)) {
+ let data = this.engine.getResourceBundle(locale, namespace)
+ return _.map(dotize.convert(data), (value, key) => {
+ return {
+ key,
+ value
+ }
+ })
+ } else {
+ throw new Error('Invalid locale or namespace')
+ }
+ },
+ loadLocale(locale) {
+ return Promise.fromCallback(cb => {
+ return this.engine.loadLanguages(locale, cb)
+ })
+ },
+ setCurrentLocale(locale) {
+ return Promise.fromCallback(cb => {
+ return this.engine.changeLanguage(locale, cb)
+ })
+ }
+}
diff --git a/server/modules/logger.js b/server/modules/logger.js
new file mode 100644
index 00000000..8b134d05
--- /dev/null
+++ b/server/modules/logger.js
@@ -0,0 +1,80 @@
+'use strict'
+
+/* global wiki */
+
+const cluster = require('cluster')
+
+module.exports = {
+ init() {
+ let winston = require('winston')
+
+ // Console
+
+ let logger = new (winston.Logger)({
+ level: (wiki.IS_DEBUG) ? 'debug' : 'info',
+ transports: [
+ new (winston.transports.Console)({
+ level: (wiki.IS_DEBUG) ? 'debug' : 'info',
+ prettyPrint: true,
+ colorize: true,
+ silent: false,
+ 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) {
+ // 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
+ // })
+ // }
+
+ return logger
+ }
+}
diff --git a/server/libs/markdown.js b/server/modules/markdown.js
similarity index 96%
rename from server/libs/markdown.js
rename to server/modules/markdown.js
index 14b0c887..71e0ea54 100644
--- a/server/libs/markdown.js
+++ b/server/modules/markdown.js
@@ -1,6 +1,6 @@
'use strict'
-/* global winston */
+/* global wiki */
const Promise = require('bluebird')
const md = require('markdown-it')
@@ -23,11 +23,12 @@ const mdRemove = require('remove-markdown')
var mkdown = md({
html: true,
- breaks: appconfig.features.linebreaks,
+ // breaks: wiki.config.features.linebreaks,
+ breaks: true,
linkify: true,
typography: true,
highlight(str, lang) {
- if (appconfig.theme.code.colorize && lang && hljs.getLanguage(lang)) {
+ if (wiki.config.theme.code.colorize && lang && hljs.getLanguage(lang)) {
try {
return '' + hljs.highlight(lang, str, true).value + '
'
} catch (err) {
@@ -57,7 +58,8 @@ var mkdown = md({
})
.use(mdAttrs)
-if (appconfig.features.mathjax) {
+// if (wiki.config.features.mathjax) {
+if (true) {
mkdown.use(mdMathjax)
}
@@ -94,7 +96,7 @@ const videoRules = [
// Regex
-const textRegex = new RegExp('\\b[a-z0-9-.,' + appdata.regex.cjk + appdata.regex.arabic + ']+\\b', 'g')
+const textRegex = new RegExp('\\b[a-z0-9-.,' + wiki.data.regex.cjk + wiki.data.regex.arabic + ']+\\b', 'g')
const mathRegex = [
{
format: 'TeX',
@@ -301,7 +303,7 @@ const parseContent = (content) => {
// Mathjax Post-processor
- if (appconfig.features.mathjax) {
+ if (wiki.config.features.mathjax) {
return processMathjax(cr.html())
} else {
return Promise.resolve(cr.html())
@@ -339,7 +341,7 @@ const processMathjax = (content) => {
resolve(result.svg)
} else {
resolve(currentMatch[0])
- winston.warn(result.errors.join(', '))
+ wiki.logger.warn(result.errors.join(', '))
}
})
})
diff --git a/server/modules/queue.js b/server/modules/queue.js
new file mode 100644
index 00000000..4bbb6dc0
--- /dev/null
+++ b/server/modules/queue.js
@@ -0,0 +1,37 @@
+'use strict'
+
+/* global wiki */
+
+const Bull = require('bull')
+const Promise = require('bluebird')
+
+module.exports = {
+ init() {
+ wiki.data.queues.forEach(queueName => {
+ this[queueName] = new Bull(queueName, {
+ prefix: `q-${wiki.config.ha.nodeuid}`,
+ redis: wiki.config.redis
+ })
+ })
+ return this
+ },
+ clean() {
+ return Promise.each(wiki.data.queues, queueName => {
+ return new Promise((resolve, reject) => {
+ let keyStream = wiki.redis.scanStream({
+ match: `q-${wiki.config.ha.nodeuid}:${queueName}:*`
+ })
+ keyStream.on('data', resultKeys => {
+ if (resultKeys.length > 0) {
+ wiki.redis.del(resultKeys)
+ }
+ })
+ keyStream.on('end', resolve)
+ })
+ }).then(() => {
+ wiki.logger.info('Purging old queue jobs: OK')
+ }).return(true).catch(err => {
+ wiki.logger.error(err)
+ })
+ }
+}
diff --git a/server/modules/redis.js b/server/modules/redis.js
new file mode 100644
index 00000000..6eb7999b
--- /dev/null
+++ b/server/modules/redis.js
@@ -0,0 +1,33 @@
+'use strict'
+
+/* global wiki */
+
+const Redis = require('ioredis')
+const { isPlainObject } = require('lodash')
+
+/**
+ * Redis module
+ *
+ * @return {Object} Redis client wrapper instance
+ */
+module.exports = {
+
+ /**
+ * Initialize Redis client
+ *
+ * @return {Object} Redis client instance
+ */
+ init() {
+ if (isPlainObject(wiki.config.redis)) {
+ let red = new Redis(wiki.config.redis)
+ red.on('ready', () => {
+ wiki.logger.info('Redis connection: OK')
+ })
+ return red
+ } else {
+ wiki.logger.error('Invalid Redis configuration!')
+ process.exit(1)
+ }
+ }
+
+}
diff --git a/server/libs/rights.js b/server/modules/rights.js
similarity index 94%
rename from server/libs/rights.js
rename to server/modules/rights.js
index ce82882b..d06de758 100644
--- a/server/libs/rights.js
+++ b/server/modules/rights.js
@@ -1,6 +1,6 @@
'use strict'
-/* global db */
+/* global wiki */
const _ = require('lodash')
@@ -32,8 +32,8 @@ module.exports = {
init () {
let self = this
- db.onReady.then(() => {
- db.User.findOne({ provider: 'local', email: 'guest' }).then((u) => {
+ wiki.db.onReady.then(() => {
+ wiki.db.User.findOne({ provider: 'local', email: 'guest' }).then((u) => {
if (u) {
self.guest = u
}
diff --git a/server/libs/search.js b/server/modules/search.js
similarity index 87%
rename from server/libs/search.js
rename to server/modules/search.js
index 7c446b17..7801a739 100644
--- a/server/libs/search.js
+++ b/server/modules/search.js
@@ -1,13 +1,13 @@
'use strict'
-/* global winston */
+/* global wiki */
const Promise = require('bluebird')
const _ = require('lodash')
-const searchIndex = require('./search-index')
+// const searchIndex = require('./search-index')
const stopWord = require('stopword')
const streamToPromise = require('stream-to-promise')
-const searchAllowedChars = new RegExp('[^a-z0-9' + appdata.regex.cjk + appdata.regex.arabic + ' ]', 'g')
+const searchAllowedChars = new RegExp('[^a-z0-9' + wiki.data.regex.cjk + wiki.data.regex.arabic + ' ]', 'g')
module.exports = {
@@ -22,24 +22,24 @@ module.exports = {
init () {
let self = this
self._isReady = new Promise((resolve, reject) => {
- searchIndex({
+ /*searchIndex({
deletable: true,
fieldedSearch: true,
indexPath: 'wiki',
logLevel: 'error',
- stopwords: _.get(stopWord, appconfig.lang, [])
+ stopwords: _.get(stopWord, wiki.config.lang, [])
}, (err, si) => {
if (err) {
- winston.error('Failed to initialize search index.', err)
+ wiki.logger.error('Failed to initialize search index.', err)
reject(err)
} else {
self._si = Promise.promisifyAll(si)
self._si.flushAsync().then(() => {
- winston.info('Search index flushed and ready.')
+ wiki.logger.info('Search index flushed and ready.')
resolve(true)
})
}
- })
+ }) */
})
return self
@@ -95,13 +95,13 @@ module.exports = {
parent: content.parent || '',
content: content.text || ''
}]).then(() => {
- winston.log('verbose', 'Entry ' + content._id + ' added/updated to search index.')
+ wiki.logger.log('verbose', 'Entry ' + content._id + ' added/updated to search index.')
return true
}).catch((err) => {
- winston.error(err)
+ wiki.logger.error(err)
})
}).catch((err) => {
- winston.error(err)
+ wiki.logger.error(err)
})
})
},
@@ -131,7 +131,7 @@ module.exports = {
if (err.type === 'NotFoundError') {
return true
} else {
- winston.error(err)
+ wiki.logger.error(err)
}
})
})
@@ -204,7 +204,7 @@ module.exports = {
suggest: []
}
} else {
- winston.error(err)
+ wiki.logger.error(err)
}
})
}
diff --git a/server/libs/system.js b/server/modules/system.js
similarity index 100%
rename from server/libs/system.js
rename to server/modules/system.js
diff --git a/server/libs/uploads-agent.js b/server/modules/uploads-agent.js
similarity index 88%
rename from server/libs/uploads-agent.js
rename to server/modules/uploads-agent.js
index a8ae79ec..6b861733 100644
--- a/server/libs/uploads-agent.js
+++ b/server/modules/uploads-agent.js
@@ -1,6 +1,6 @@
'use strict'
-/* global db, git, lang, upl */
+/* global wiki */
const path = require('path')
const Promise = require('bluebird')
@@ -32,8 +32,8 @@ module.exports = {
init () {
let self = this
- self._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
- self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
+ self._uploadsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.repo, 'uploads')
+ self._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
return self
},
@@ -59,16 +59,16 @@ module.exports = {
self._watcher.on('add', (p) => {
let pInfo = self.parseUploadsRelPath(p)
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(() => {
- return git.commitUploads(lang.t('git:uploaded', { path: p }))
+ return wiki.git.commitUploads(wiki.lang.t('git:uploaded', { path: p }))
})
})
// -> Remove upload file
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
- return db.UplFolder.remove({}).then(() => {
- return db.UplFolder.insertMany(_.map(folderNames, (f) => {
+ return wiki.db.UplFolder.remove({}).then(() => {
+ return wiki.db.UplFolder.insertMany(_.map(folderNames, (f) => {
return {
_id: 'f:' + f,
name: f
@@ -107,7 +107,7 @@ module.exports = {
let fldPath = path.join(self._uploadsPath, fldName)
return fs.readdirAsync(fldPath).then((fList) => {
return Promise.map(fList, (f) => {
- return upl.processFile(fldName, f).then((mData) => {
+ return wiki.upl.processFile(fldName, f).then((mData) => {
if (mData) {
allFiles.push(mData)
}
@@ -118,9 +118,9 @@ module.exports = {
}, {concurrency: 1}).finally(() => {
// Add files to DB
- return db.UplFile.remove({}).then(() => {
+ return wiki.db.UplFile.remove({}).then(() => {
if (_.isArray(allFiles) && allFiles.length > 0) {
- return db.UplFile.insertMany(allFiles)
+ return wiki.db.UplFile.insertMany(allFiles)
} else {
return true
}
@@ -131,7 +131,7 @@ module.exports = {
}).then(() => {
// Watch for new changes
- return upl.watch()
+ return wiki.upl.watch()
})
},
diff --git a/server/libs/uploads.js b/server/modules/uploads.js
similarity index 81%
rename from server/libs/uploads.js
rename to server/modules/uploads.js
index 2bee6322..f56ecab3 100644
--- a/server/libs/uploads.js
+++ b/server/modules/uploads.js
@@ -1,6 +1,6 @@
'use strict'
-/* global db, lang, lcdata, upl, winston */
+/* global wiki */
const path = require('path')
const Promise = require('bluebird')
@@ -27,8 +27,8 @@ module.exports = {
* @return {Object} Uploads model instance
*/
init () {
- this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
- this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
+ this._uploadsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.repo, 'uploads')
+ this._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
return this
},
@@ -48,7 +48,7 @@ module.exports = {
* @return {Array} The uploads folders.
*/
getUploadsFolders () {
- return db.UplFolder.find({}, 'name').sort('name').exec().then((results) => {
+ return wiki.db.Folder.find({}, 'name').sort('name').exec().then((results) => {
return (results) ? _.map(results, 'name') : [{ name: '' }]
})
},
@@ -69,7 +69,7 @@ module.exports = {
}
return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => {
- return db.UplFolder.findOneAndUpdate({
+ return wiki.db.UplFolder.findOneAndUpdate({
_id: 'f:' + folderName
}, {
name: folderName
@@ -88,7 +88,7 @@ module.exports = {
* @return {Boolean} True if valid
*/
validateUploadsFolder (folderName) {
- return db.UplFolder.findOne({ name: folderName }).then((f) => {
+ return wiki.db.UplFolder.findOne({ name: folderName }).then((f) => {
return (f) ? path.resolve(this._uploadsPath, folderName) : false
})
},
@@ -101,7 +101,7 @@ module.exports = {
*/
addUploadsFiles (arrFiles) {
if (_.isArray(arrFiles) || _.isPlainObject(arrFiles)) {
- // this._uploadsDb.Files.insert(arrFiles);
+ // this._uploadswiki.Db.Files.insert(arrFiles);
}
},
@@ -113,7 +113,7 @@ module.exports = {
* @return {Array