chore: DB ref refactor + add scheduler db model + remove search modules (#5699)

pull/5702/head
Nicolas Giard 2 years ago committed by GitHub
parent 5c6ae08b84
commit 10cc2ef4b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -129,6 +129,5 @@ bodyParserLimit: 5mb
scheduler:
# Maximum number of workers to run background cpu-intensive jobs.
# Make sure your PostgreSQL server can handle an extra connection
# for each worker!
workers: 3
# Leave 'auto' to use number of CPU cores as maximum.
workers: auto

@ -80,6 +80,7 @@ defaults:
maintainerEmail: security@requarks.io
jobs:
- task: background
identifier: purge-uploads
pattern: '*/15 * * * *'
payload:
name: purgeUploads

@ -10,7 +10,7 @@ const path = require('path')
const bruteforce = new ExpressBrute(new BruteKnex({
createTable: true,
knex: WIKI.models.knex
knex: WIKI.db.knex
}), {
freeRetries: 5,
minWait: 5 * 60 * 1000, // 5 minutes
@ -26,7 +26,7 @@ const bruteforce = new ExpressBrute(new BruteKnex({
router.get('/login', async (req, res, next) => {
// -> Bypass Login
if (WIKI.config.auth.autoLogin && !req.query.all) {
const stg = await WIKI.models.authentication.query().orderBy('order').first()
const stg = await WIKI.db.authentication.query().orderBy('order').first()
const stgInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey])
if (!stgInfo.useForm) {
return res.redirect(`/login/${stg.key}`)
@ -41,7 +41,7 @@ router.get('/login', async (req, res, next) => {
*/
router.get('/login/:strategy', async (req, res, next) => {
try {
await WIKI.models.users.login({
await WIKI.db.users.login({
strategy: req.params.strategy
}, { req, res })
} catch (err) {
@ -56,7 +56,7 @@ router.all('/login/:strategy/callback', async (req, res, next) => {
if (req.method !== 'GET' && req.method !== 'POST') { return next() }
try {
const authResult = await WIKI.models.users.login({
const authResult = await WIKI.db.users.login({
strategy: req.params.strategy
}, { req, res })
res.cookie('jwt', authResult.jwt, { expires: moment().add(1, 'y').toDate() })
@ -82,7 +82,7 @@ router.all('/login/:strategy/callback', async (req, res, next) => {
* Logout
*/
router.get('/logout', async (req, res, next) => {
const redirURL = await WIKI.models.users.logout({ req, res })
const redirURL = await WIKI.db.users.logout({ req, res })
req.logout((err) => {
if (err) { return next(err) }
res.clearCookie('jwt')
@ -95,7 +95,7 @@ router.get('/logout', async (req, res, next) => {
*/
router.get('/register', async (req, res, next) => {
_.set(res.locals, 'pageMeta.title', 'Register')
const localStrg = await WIKI.models.authentication.getStrategy('local')
const localStrg = await WIKI.db.authentication.getStrategy('local')
if (localStrg.selfRegistration) {
res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
} else {
@ -108,13 +108,13 @@ router.get('/register', async (req, res, next) => {
*/
router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
try {
const usr = await WIKI.models.userKeys.validateToken({ kind: 'verify', token: req.params.token })
await WIKI.models.users.query().patch({ isVerified: true }).where('id', usr.id)
const usr = await WIKI.db.userKeys.validateToken({ kind: 'verify', token: req.params.token })
await WIKI.db.users.query().patch({ isVerified: true }).where('id', usr.id)
req.brute.reset()
if (WIKI.config.auth.enforce2FA) {
res.redirect('/login')
} else {
const result = await WIKI.models.users.refreshToken(usr)
const result = await WIKI.db.users.refreshToken(usr)
res.cookie('jwt', result.token, { expires: moment().add(1, 'years').toDate() })
res.redirect('/')
}
@ -128,13 +128,13 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
*/
router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) => {
try {
const usr = await WIKI.models.userKeys.validateToken({ kind: 'resetPwd', token: req.params.token })
const usr = await WIKI.db.userKeys.validateToken({ kind: 'resetPwd', token: req.params.token })
if (!usr) {
throw new Error('Invalid Token')
}
req.brute.reset()
const changePwdContinuationToken = await WIKI.models.userKeys.generateToken({
const changePwdContinuationToken = await WIKI.db.userKeys.generateToken({
userId: usr.id,
kind: 'changePwd'
})

@ -6,8 +6,6 @@ const CleanCSS = require('clean-css')
const moment = require('moment')
const path = require('path')
/* global WIKI */
const tmplCreateRegex = /^[0-9]+(,[0-9]+)?$/
const siteAssetsPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets')
@ -27,7 +25,7 @@ router.get('/robots.txt', (req, res, next) => {
* Health Endpoint
*/
router.get('/healthz', (req, res, next) => {
if (WIKI.models.knex.client.pool.numFree() < 1 && WIKI.models.knex.client.pool.numUsed() < 1) {
if (WIKI.db.knex.client.pool.numFree() < 1 && WIKI.db.knex.client.pool.numUsed() < 1) {
res.status(503).json({ ok: false }).end()
} else {
res.status(200).json({ ok: true }).end()
@ -38,7 +36,7 @@ router.get('/healthz', (req, res, next) => {
* Site Asset
*/
router.get('/_site/:siteId?/:resource', async (req, res, next) => {
const site = req.params.siteId ? WIKI.sites[req.params.siteId] : await WIKI.models.sites.getSiteByHostname({ hostname: req.hostname })
const site = req.params.siteId ? WIKI.sites[req.params.siteId] : await WIKI.db.sites.getSiteByHostname({ hostname: req.hostname })
if (!site) {
return res.status(404).send('Site Not Found')
}
@ -115,7 +113,7 @@ router.get(['/d', '/d/*'], async (req, res, next) => {
const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
const page = await WIKI.models.pages.getPageFromDb({
const page = await WIKI.db.pages.getPageFromDb({
path: pageArgs.path,
locale: pageArgs.locale,
userId: req.user.id,
@ -140,7 +138,7 @@ router.get(['/d', '/d/*'], async (req, res, next) => {
const fileName = _.last(page.path.split('/')) + '.' + pageHelper.getFileExtension(page.contentType)
res.attachment(fileName)
if (versionId > 0) {
const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: page.id, versionId })
const pageVersion = await WIKI.db.pageHistory.getVersion({ pageId: page.id, versionId })
res.send(pageHelper.injectPageMetadata(pageVersion))
} else {
res.send(pageHelper.injectPageMetadata(page))
@ -176,7 +174,7 @@ router.get(['/_edit', '/_edit/*'], async (req, res, next) => {
}
// -> Get page data from DB
let page = await WIKI.models.pages.getPageFromDb({
let page = await WIKI.db.pages.getPageFromDb({
path: pageArgs.path,
locale: pageArgs.locale,
userId: req.user.id,
@ -255,7 +253,7 @@ router.get(['/_edit', '/_edit/*'], async (req, res, next) => {
if (tmplVersionId > 0) {
// -> From Page Version
const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: tmplPageId, versionId: tmplVersionId })
const pageVersion = await WIKI.db.pageHistory.getVersion({ pageId: tmplPageId, versionId: tmplVersionId })
if (!pageVersion) {
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
return res.status(404).render('notfound', { action: 'template' })
@ -270,7 +268,7 @@ router.get(['/_edit', '/_edit/*'], async (req, res, next) => {
page.description = pageVersion.description
} else {
// -> From Page Live
const pageOriginal = await WIKI.models.pages.query().findById(tmplPageId)
const pageOriginal = await WIKI.db.pages.query().findById(tmplPageId)
if (!pageOriginal) {
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
return res.status(404).render('notfound', { action: 'template' })
@ -305,7 +303,7 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
_.set(res, 'locals.siteConfig.lang', pageArgs.locale)
_.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
const page = await WIKI.models.pages.getPageFromDb({
const page = await WIKI.db.pages.getPageFromDb({
path: pageArgs.path,
locale: pageArgs.locale,
userId: req.user.id,
@ -345,7 +343,7 @@ router.get(['/i', '/i/:id'], async (req, res, next) => {
return res.redirect('/')
}
const page = await WIKI.models.pages.query().column(['path', 'localeCode', 'isPrivate', 'privateNS']).findById(pageId)
const page = await WIKI.db.pages.query().column(['path', 'localeCode', 'isPrivate', 'privateNS']).findById(pageId)
if (!page) {
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
return res.status(404).render('notfound', { action: 'view' })
@ -377,7 +375,7 @@ router.get(['/s', '/s/*'], async (req, res, next) => {
const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
const page = await WIKI.models.pages.getPageFromDb({
const page = await WIKI.db.pages.getPageFromDb({
path: pageArgs.path,
locale: pageArgs.locale,
userId: req.user.id,
@ -410,7 +408,7 @@ router.get(['/s', '/s/*'], async (req, res, next) => {
if (page) {
if (versionId > 0) {
const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: page.id, versionId })
const pageVersion = await WIKI.db.pageHistory.getVersion({ pageId: page.id, versionId })
_.set(res.locals, 'pageMeta.title', pageVersion.title)
_.set(res.locals, 'pageMeta.description', pageVersion.description)
res.render('source', {
@ -446,7 +444,7 @@ router.get('/_userav/:uid', async (req, res, next) => {
if (!WIKI.auth.checkAccess(req.user, ['read:pages'])) {
return res.sendStatus(403)
}
const av = await WIKI.models.users.getUserAvatarData(req.params.uid)
const av = await WIKI.db.users.getUserAvatarData(req.params.uid)
if (av) {
res.set('Content-Type', 'image/jpeg')
res.send(av)
@ -472,7 +470,7 @@ router.get('/*', async (req, res, next) => {
try {
// -> Get Page from cache
const page = await WIKI.models.pages.getPage({
const page = await WIKI.db.pages.getPage({
path: pageArgs.path,
locale: pageArgs.locale,
userId: req.user.id
@ -519,7 +517,7 @@ router.get('/*', async (req, res, next) => {
// -> Build sidebar navigation
let sdi = 1
const sidebar = (await WIKI.models.navigation.getTree({ cache: true, locale: pageArgs.locale, groups: req.user.groups })).map(n => ({
const sidebar = (await WIKI.db.navigation.getTree({ cache: true, locale: pageArgs.locale, groups: req.user.groups })).map(n => ({
i: `sdi-${sdi++}`,
k: n.kind,
l: n.label,
@ -595,7 +593,7 @@ router.get('/*', async (req, res, next) => {
return res.sendStatus(403)
}
await WIKI.models.assets.getAsset(pageArgs.path, res)
await WIKI.db.assets.getAsset(pageArgs.path, res)
}
})

@ -3,8 +3,6 @@ const router = express.Router()
const _ = require('lodash')
const qs = require('querystring')
/* global WIKI */
/**
* Let's Encrypt Challenge
*/

@ -5,8 +5,6 @@ const multer = require('multer')
const path = require('path')
const sanitize = require('sanitize-filename')
/* global WIKI */
/**
* Upload files
*/
@ -66,7 +64,7 @@ router.post('/u', (req, res, next) => {
let hierarchy = []
if (folderId) {
try {
hierarchy = await WIKI.models.assetFolders.getHierarchy(folderId)
hierarchy = await WIKI.db.assetFolders.getHierarchy(folderId)
} catch (err) {
return res.status(400).json({
succeeded: false,
@ -88,7 +86,7 @@ router.post('/u', (req, res, next) => {
}
// Process upload file
await WIKI.models.assets.upload({
await WIKI.db.assets.upload({
...fileMeta,
mode: 'upload',
folderId: folderId,

@ -1,8 +1,6 @@
const chalk = require('chalk')
const os = require('node:os')
/* global WIKI */
module.exports = () => {
WIKI.servers.ws.on('connection', (socket) => {
// TODO: Validate token + permissions

@ -3,8 +3,6 @@ const path = require('path')
const UINT64 = require('cuint').UINT64
const fs = require('fs')
/* global WIKI */
/**
* Based of express-serve-asar (https://github.com/toyobayashi/express-serve-asar)
* by Fenglin Li (https://github.com/toyobayashi)

@ -10,8 +10,6 @@ const pem2jwk = require('pem-jwk').pem2jwk
const securityHelper = require('../helpers/security')
/* global WIKI */
module.exports = {
strategies: {},
guest: {
@ -33,7 +31,7 @@ module.exports = {
passport.deserializeUser(async (id, done) => {
try {
const user = await WIKI.models.users.query().findById(id).withGraphFetched('groups').modifyGraph('groups', builder => {
const user = await WIKI.db.users.query().findById(id).withGraphFetched('groups').modifyGraph('groups', builder => {
builder.select('groups.id', 'permissions')
})
if (user) {
@ -75,7 +73,7 @@ module.exports = {
}))
// Load enabled strategies
const enabledStrategies = await WIKI.models.authentication.getStrategies({ enabledOnly: true })
const enabledStrategies = await WIKI.db.authentication.getStrategies({ enabledOnly: true })
for (const stg of enabledStrategies) {
try {
const strategy = require(`../modules/authentication/${stg.module}/authentication.js`)
@ -144,7 +142,7 @@ module.exports = {
if (mustRevalidate) {
const jwtPayload = jwt.decode(securityHelper.extractJWT(req))
try {
const newToken = await WIKI.models.users.refreshToken(jwtPayload.id, jwtPayload.pvd)
const newToken = await WIKI.db.users.refreshToken(jwtPayload.id, jwtPayload.pvd)
user = newToken.user
user.permissions = user.getPermissions()
user.groups = user.getGroups()
@ -162,7 +160,7 @@ module.exports = {
return next()
}
} else if (user) {
user = await WIKI.models.users.getById(user.id)
user = await WIKI.db.users.getById(user.id)
user.permissions = user.getPermissions()
user.groups = user.getGroups()
user.strategyId = strategyId
@ -170,7 +168,7 @@ module.exports = {
} else {
// JWT is NOT valid, set as guest
if (WIKI.auth.guest.cacheExpiration <= DateTime.utc()) {
WIKI.auth.guest = await WIKI.models.users.getGuestUser()
WIKI.auth.guest = await WIKI.db.users.getGuestUser()
WIKI.auth.guest.cacheExpiration = DateTime.utc().plus({ minutes: 1 })
}
req.user = WIKI.auth.guest
@ -349,7 +347,7 @@ module.exports = {
* Reload Groups from DB
*/
async reloadGroups () {
const groupsArray = await WIKI.models.groups.query()
const groupsArray = await WIKI.db.groups.query()
this.groups = _.keyBy(groupsArray, 'id')
WIKI.auth.guest.cacheExpiration = DateTime.utc().minus({ days: 1 })
},
@ -358,7 +356,7 @@ module.exports = {
* Reload valid API Keys from DB
*/
async reloadApiKeys () {
const keys = await WIKI.models.apiKeys.query().select('id').where('isRevoked', false).andWhere('expiration', '>', DateTime.utc().toISO())
const keys = await WIKI.db.apiKeys.query().select('id').where('isRevoked', false).andWhere('expiration', '>', DateTime.utc().toISO())
this.validApiKeys = _.map(keys, 'id')
},
@ -405,14 +403,14 @@ module.exports = {
*/
async resetGuestUser() {
WIKI.logger.info('Resetting guest account...')
const guestGroup = await WIKI.models.groups.query().where('id', 2).first()
const guestGroup = await WIKI.db.groups.query().where('id', 2).first()
await WIKI.models.users.query().delete().where({
await WIKI.db.users.query().delete().where({
providerKey: 'local',
email: 'guest@example.com'
}).orWhere('id', 2)
const guestUser = await WIKI.models.users.query().insert({
const guestUser = await WIKI.db.users.query().insert({
id: 2,
provider: 'local',
email: 'guest@example.com',

@ -5,8 +5,6 @@ const fs = require('fs')
const path = require('path')
const yaml = require('js-yaml')
/* global WIKI */
module.exports = {
/**
* Load root config from disk
@ -52,10 +50,18 @@ module.exports = {
appconfig = _.defaultsDeep(appconfig, appdata.defaults.config)
// Override port
if (appconfig.port < 1 || process.env.HEROKU) {
appconfig.port = process.env.PORT || 80
}
if (process.env.WIKI_PORT) {
appconfig.port = process.env.WIKI_PORT || 80
}
// Load package info
const packageInfo = require(path.join(WIKI.ROOTPATH, 'package.json'))
// Load DB Password from Docker Secret File
@ -81,12 +87,12 @@ module.exports = {
* Load config from DB
*/
async loadFromDb() {
let conf = await WIKI.models.settings.getConfig()
let conf = await WIKI.db.settings.getConfig()
if (conf) {
WIKI.config = _.defaultsDeep(conf, WIKI.config)
} else {
WIKI.logger.warn('DB Configuration is empty or incomplete. Switching to Setup mode...')
WIKI.config.setup = true
WIKI.logger.warn('Missing DB Configuration!')
process.exit(1)
}
},
/**
@ -102,9 +108,9 @@ module.exports = {
if (!_.isPlainObject(value)) {
value = { v: value }
}
let affectedRows = await WIKI.models.settings.query().patch({ value }).where('key', key)
let affectedRows = await WIKI.db.settings.query().patch({ value }).where('key', key)
if (affectedRows === 0 && value) {
await WIKI.models.settings.query().insert({ key, value })
await WIKI.db.settings.query().insert({ key, value })
}
}
if (propagate) {
@ -121,7 +127,7 @@ module.exports = {
* Apply Dev Flags
*/
async applyFlags() {
WIKI.models.knex.client.config.debug = WIKI.config.flags.sqllog
WIKI.db.knex.client.config.debug = WIKI.config.flags.sqllog
},
/**

@ -9,8 +9,6 @@ const Objection = require('objection')
const migrationSource = require('../db/migrator-source')
const migrateFromLegacy = require('../db/legacy')
/* global WIKI */
/**
* ORM DB module
*/
@ -144,17 +142,13 @@ module.exports = {
}
}
let initTasksQueue = (WIKI.IS_MASTER) ? [
initTasks.connect,
initTasks.migrateFromLegacy,
initTasks.syncSchemas
] : [
() => { return Promise.resolve() }
]
// Perform init tasks
this.onReady = Promise.each(initTasksQueue, t => t()).return(true)
this.onReady = (async () => {
await initTasks.connect()
await initTasks.migrateFromLegacy()
await initTasks.syncSchemas()
})()
return {
...this,
@ -187,7 +181,7 @@ module.exports = {
WIKI.auth.subscribeToEvents()
WIKI.configSvc.subscribeToEvents()
WIKI.models.pages.subscribeToEvents()
WIKI.db.pages.subscribeToEvents()
WIKI.logger.info(`PG PubSub Listener initialized successfully: [ OK ]`)
},
@ -208,7 +202,7 @@ module.exports = {
* @param {object} value Payload of the event
*/
notifyViaDB (event, value) {
WIKI.models.listener.publish('wiki', {
WIKI.db.listener.publish('wiki', {
source: WIKI.INSTANCE_ID,
event,
value

@ -1,8 +1,6 @@
const fs = require('fs-extra')
const path = require('path')
/* global WIKI */
module.exports = {
ext: {},
async init () {

@ -3,8 +3,6 @@ const EventEmitter = require('eventemitter2').EventEmitter2
let isShuttingDown = false
/* global WIKI */
module.exports = {
async init() {
WIKI.logger.info('=======================================')
@ -12,10 +10,10 @@ module.exports = {
WIKI.logger.info('=======================================')
WIKI.logger.info('Initializing...')
WIKI.models = require('./db').init()
WIKI.db = require('./db').init()
try {
await WIKI.models.onReady
await WIKI.db.onReady
await WIKI.configSvc.loadFromDb()
await WIKI.configSvc.applyFlags()
} catch (err) {
@ -64,21 +62,21 @@ module.exports = {
* Post-Web Boot Sequence
*/
async postBootWeb() {
await WIKI.models.analytics.refreshProvidersFromDisk()
await WIKI.models.authentication.refreshStrategiesFromDisk()
await WIKI.models.commentProviders.refreshProvidersFromDisk()
await WIKI.models.renderers.refreshRenderersFromDisk()
await WIKI.models.storage.refreshTargetsFromDisk()
await WIKI.db.analytics.refreshProvidersFromDisk()
await WIKI.db.authentication.refreshStrategiesFromDisk()
await WIKI.db.commentProviders.refreshProvidersFromDisk()
await WIKI.db.renderers.refreshRenderersFromDisk()
await WIKI.db.storage.refreshTargetsFromDisk()
await WIKI.extensions.init()
await WIKI.auth.activateStrategies()
await WIKI.models.commentProviders.initProvider()
await WIKI.models.sites.reloadCache()
await WIKI.models.storage.initTargets()
await WIKI.db.commentProviders.initProvider()
await WIKI.db.sites.reloadCache()
await WIKI.db.storage.initTargets()
await WIKI.scheduler.start()
await WIKI.models.subscribeToNotifications()
await WIKI.db.subscribeToNotifications()
},
/**
* Graceful shutdown
@ -93,9 +91,9 @@ module.exports = {
await WIKI.scheduler.stop()
}
if (WIKI.models) {
await WIKI.models.unsubscribeToNotifications()
if (WIKI.models.knex) {
await WIKI.models.knex.destroy()
await WIKI.db.unsubscribeToNotifications()
if (WIKI.db.knex) {
await WIKI.db.knex.destroy()
}
}
if (WIKI.asar) {

@ -6,8 +6,6 @@ const CSR = require('@root/csr')
const PEM = require('@root/pem')
const punycode = require('punycode/')
/* global WIKI */
module.exports = {
apiDirectory: WIKI.dev ? 'https://acme-staging-v02.api.letsencrypt.org/directory' : 'https://acme-v02.api.letsencrypt.org/directory',
acme: null,

@ -1,8 +1,6 @@
const chalk = require('chalk')
const EventEmitter = require('events')
/* global WIKI */
const LEVELS = ['error', 'warn', 'info', 'debug']
const LEVELSIGNORED = ['verbose', 'silly']
const LEVELCOLORS = {

@ -3,8 +3,6 @@ const _ = require('lodash')
const fs = require('fs-extra')
const path = require('path')
/* global WIKI */
module.exports = {
transport: null,
templates: {},

@ -1,5 +1,3 @@
const { run, parseCronItems, Logger } = require('graphile-worker')
const { Pool } = require('pg')
const { DynamicThreadPool } = require('poolifier')
const { v4: uuid } = require('uuid')
const os = require('node:os')
@ -21,49 +19,46 @@ module.exports = {
},
async start () {
WIKI.logger.info('Starting Scheduler...')
this.runner = await run({
pgPool: new Pool({
...(typeof WIKI.models.config === 'string') ? {
connectionString: WIKI.models.config
} : WIKI.models.config,
max: this.maxWorkers + 2
}),
schema: WIKI.config.db.schemas.scheduler,
concurrency: this.maxWorkers,
noHandleSignals: true,
logger: new Logger(scope => {
return (level, message, meta) => {
const prefix = (scope?.workerId) ? `[${scope.workerId}] ` : ''
WIKI.logger[level](`${prefix}${message}`, meta)
}
}),
parsedCronItems: parseCronItems(WIKI.data.jobs.map(j => ({
...j,
identifier: uuid()
}))),
taskList: {
simple: async (payload, helpers) => {
// TODO: Handle task
},
background: async (payload, helpers) => {
try {
await this.pool.execute({
id: helpers.job.id,
name: payload.name,
data: payload.data
})
} catch (err) {
helpers.logger.warn(`Failed job: ${err.message}`)
throw err
}
}
}
})
// this.runner = await run({
// pgPool: new Pool({
// ...(typeof WIKI.db.config === 'string') ? {
// connectionString: WIKI.db.config
// } : WIKI.db.config,
// max: this.maxWorkers + 2
// }),
// schema: WIKI.config.db.schemas.scheduler,
// concurrency: this.maxWorkers,
// noHandleSignals: true,
// logger: new Logger(scope => {
// return (level, message, meta) => {
// const prefix = (scope?.workerId) ? `[${scope.workerId}] ` : ''
// WIKI.logger[level](`${prefix}${message}`, meta)
// }
// }),
// parsedCronItems: parseCronItems(WIKI.data.jobs),
// taskList: {
// simple: async (payload, helpers) => {
// // TODO: Handle task
// },
// background: async (payload, helpers) => {
// try {
// await this.pool.execute({
// id: helpers.job.id,
// name: payload.name,
// data: payload.data
// })
// } catch (err) {
// helpers.logger.warn(`Failed job: ${err.message}`)
// throw err
// }
// }
// }
// })
WIKI.logger.info('Scheduler: [ STARTED ]')
},
async stop () {
WIKI.logger.info('Stopping Scheduler...')
await this.runner.stop()
// await this.runner.stop()
WIKI.logger.info('Scheduler: [ STOPPED ]')
}
}

@ -8,8 +8,6 @@ const io = require('socket.io')
const { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } = require('apollo-server-core')
const { graphqlUploadExpress } = require('graphql-upload')
/* global WIKI */
module.exports = {
graph: null,
http: null,

@ -2,8 +2,6 @@ const fs = require('fs-extra')
const path = require('path')
const _ = require('lodash')
/* global WIKI */
module.exports = {
async init () {
if (!WIKI.config.offline) {
@ -45,9 +43,9 @@ module.exports = {
_.set(lcObj, key.replace(':', '.'), value)
})
const localeDbExists = await WIKI.models.locales.query().select('code').where('code', locale.code).first()
const localeDbExists = await WIKI.db.locales.query().select('code').where('code', locale.code).first()
if (localeDbExists) {
await WIKI.models.locales.query().update({
await WIKI.db.locales.query().update({
code: locale.code,
strings: lcObj,
isRTL: locale.isRTL,
@ -55,7 +53,7 @@ module.exports = {
nativeName: locale.nativeName
}).where('code', locale.code)
} else {
await WIKI.models.locales.query().insert({
await WIKI.db.locales.query().insert({
code: locale.code,
strings: lcObj,
isRTL: locale.isRTL,

@ -1,8 +1,6 @@
const fs = require('fs-extra')
const path = require('path')
/* global WIKI */
module.exports = {
updates: {
channel: 'BETA',

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
module.exports = {
async migrate (knex) {
const migrationsTableExists = await knex.schema.hasTable('migrations')

@ -3,8 +3,6 @@ const bcrypt = require('bcryptjs-then')
const crypto = require('crypto')
const pem2jwk = require('pem-jwk').pem2jwk
/* global WIKI */
exports.up = async knex => {
WIKI.logger.info('Running 3.0.0 database migration...')
@ -122,6 +120,35 @@ exports.up = async knex => {
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
})
// JOB SCHEDULE ------------------------
.createTable('jobSchedule', table => {
table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
table.string('task').notNullable()
table.string('cron').notNullable()
table.string('type').notNullable().defaultTo('system')
table.jsonb('payload')
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
})
// JOB HISTORY -------------------------
.createTable('jobHistory', table => {
table.uuid('id').notNullable().primary()
table.string('task').notNullable()
table.string('state').notNullable()
table.jsonb('payload')
table.string('lastErrorMessage')
table.timestamp('createdAt').notNullable()
table.timestamp('startedAt').notNullable()
table.timestamp('completedAt').notNullable().defaultTo(knex.fn.now())
})
// JOBS --------------------------------
.createTable('jobs', table => {
table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
table.string('task').notNullable()
table.jsonb('payload')
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
})
// LOCALES -----------------------------
.createTable('locales', table => {
table.string('code', 5).notNullable().primary()
@ -636,6 +663,21 @@ exports.up = async knex => {
}
})
// -> SCHEDULED JOBS
await knex('jobSchedule').insert([
{
task: 'update-locales',
cron: '0 0 * * *',
type: 'system'
},
{
task: 'check-version',
cron: '0 0 * * *',
type: 'system'
}
])
WIKI.logger.info('Completed 3.0.0 database migration.')
}

@ -4,8 +4,6 @@ const semver = require('semver')
const baseMigrationPath = path.join(WIKI.SERVERPATH, 'db/migrations')
/* global WIKI */
module.exports = {
/**
* Gets the migration names

@ -6,8 +6,6 @@ const { makeExecutableSchema } = require('@graphql-tools/schema')
const { defaultKeyGenerator, rateLimitDirective } = require('graphql-rate-limit-directive')
const { GraphQLUpload } = require('graphql-upload')
/* global WIKI */
// Rate Limiter
const { rateLimitDirectiveTypeDefs, rateLimitDirectiveTransformer } = rateLimitDirective({

@ -1,12 +1,10 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
async analyticsProviders(obj, args, context, info) {
let providers = await WIKI.models.analytics.getProviders(args.isEnabled)
let providers = await WIKI.db.analytics.getProviders(args.isEnabled)
providers = providers.map(stg => {
const providerInfo = _.find(WIKI.data.analytics, ['key', stg.key]) || {}
return {
@ -31,7 +29,7 @@ module.exports = {
async updateAnalyticsProviders(obj, args, context) {
try {
for (let str of args.providers) {
await WIKI.models.analytics.query().patch({
await WIKI.db.analytics.query().patch({
isEnabled: str.isEnabled,
config: _.reduce(str.config, (result, value, key) => {
_.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))

@ -3,8 +3,6 @@ const sanitize = require('sanitize-filename')
const graphHelper = require('../../helpers/graph')
const assetHelper = require('../../helpers/asset')
/* global WIKI */
module.exports = {
Query: {
async assets(obj, args, context) {
@ -14,9 +12,9 @@ module.exports = {
if (args.kind !== 'ALL') {
cond.kind = args.kind.toLowerCase()
}
const folderHierarchy = await WIKI.models.assetFolders.getHierarchy(args.folderId)
const folderHierarchy = await WIKI.db.assetFolders.getHierarchy(args.folderId)
const folderPath = folderHierarchy.map(h => h.slug).join('/')
const results = await WIKI.models.assets.query().where(cond)
const results = await WIKI.db.assets.query().where(cond)
return _.filter(results, r => {
const path = folderPath ? `${folderPath}/${r.filename}` : r.filename
return WIKI.auth.checkAccess(context.req.user, ['read:assets'], { path })
@ -26,10 +24,10 @@ module.exports = {
}))
},
async assetsFolders(obj, args, context) {
const results = await WIKI.models.assetFolders.query().where({
const results = await WIKI.db.assetFolders.query().where({
parentId: args.parentFolderId === 0 ? null : args.parentFolderId
})
const parentHierarchy = await WIKI.models.assetFolders.getHierarchy(args.parentFolderId)
const parentHierarchy = await WIKI.db.assetFolders.getHierarchy(args.parentFolderId)
const parentPath = parentHierarchy.map(h => h.slug).join('/')
return _.filter(results, r => {
const path = parentPath ? `${parentPath}/${r.slug}` : r.slug
@ -45,12 +43,12 @@ module.exports = {
try {
const folderSlug = sanitize(args.slug).toLowerCase()
const parentFolderId = args.parentFolderId === 0 ? null : args.parentFolderId
const result = await WIKI.models.assetFolders.query().where({
const result = await WIKI.db.assetFolders.query().where({
parentId: parentFolderId,
slug: folderSlug
}).first()
if (!result) {
await WIKI.models.assetFolders.query().insert({
await WIKI.db.assetFolders.query().insert({
slug: folderSlug,
name: folderSlug,
parentId: parentFolderId
@ -72,7 +70,7 @@ module.exports = {
try {
const filename = sanitize(args.filename).toLowerCase()
const asset = await WIKI.models.assets.query().findById(args.id)
const asset = await WIKI.db.assets.query().findById(args.id)
if (asset) {
// Check for extension mismatch
if (!_.endsWith(filename, asset.ext)) {
@ -85,7 +83,7 @@ module.exports = {
}
// Check for collision
const assetCollision = await WIKI.models.assets.query().where({
const assetCollision = await WIKI.db.assets.query().where({
filename,
folderId: asset.folderId
}).first()
@ -96,7 +94,7 @@ module.exports = {
// Get asset folder path
let hierarchy = []
if (asset.folderId) {
hierarchy = await WIKI.models.assetFolders.getHierarchy(asset.folderId)
hierarchy = await WIKI.db.assetFolders.getHierarchy(asset.folderId)
}
// Check source asset permissions
@ -113,7 +111,7 @@ module.exports = {
// Update filename + hash
const fileHash = assetHelper.generateHash(assetTargetPath)
await WIKI.models.assets.query().patch({
await WIKI.db.assets.query().patch({
filename: filename,
hash: fileHash
}).findById(args.id)
@ -122,7 +120,7 @@ module.exports = {
await asset.deleteAssetCache()
// Rename in Storage
await WIKI.models.storage.assetEvent({
await WIKI.db.storage.assetEvent({
event: 'renamed',
asset: {
...asset,
@ -149,7 +147,7 @@ module.exports = {
*/
async deleteAsset(obj, args, context) {
try {
const asset = await WIKI.models.assets.query().findById(args.id)
const asset = await WIKI.db.assets.query().findById(args.id)
if (asset) {
// Check permissions
const assetPath = await asset.getAssetPath()
@ -157,12 +155,12 @@ module.exports = {
throw new WIKI.Error.AssetDeleteForbidden()
}
await WIKI.models.knex('assetData').where('id', args.id).del()
await WIKI.models.assets.query().deleteById(args.id)
await WIKI.db.knex('assetData').where('id', args.id).del()
await WIKI.db.assets.query().deleteById(args.id)
await asset.deleteAssetCache()
// Delete from Storage
await WIKI.models.storage.assetEvent({
await WIKI.db.storage.assetEvent({
event: 'deleted',
asset: {
...asset,
@ -188,7 +186,7 @@ module.exports = {
*/
async flushTempUploads(obj, args, context) {
try {
await WIKI.models.assets.flushTempUploads()
await WIKI.db.assets.flushTempUploads()
return {
responseResult: graphHelper.generateSuccess('Temporary Uploads have been flushed successfully.')
}

@ -1,15 +1,13 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
/**
* List of API Keys
*/
async apiKeys (obj, args, context) {
const keys = await WIKI.models.apiKeys.query().orderBy(['isRevoked', 'name'])
const keys = await WIKI.db.apiKeys.query().orderBy(['isRevoked', 'name'])
return keys.map(k => ({
id: k.id,
name: k.name,
@ -39,14 +37,14 @@ module.exports = {
* Fetch active authentication strategies
*/
async authActiveStrategies (obj, args, context) {
return WIKI.models.authentication.getStrategies({ enabledOnly: args.enabledOnly })
return WIKI.db.authentication.getStrategies({ enabledOnly: args.enabledOnly })
},
/**
* Fetch site authentication strategies
*/
async authSiteStrategies (obj, args, context, info) {
const site = await WIKI.models.sites.query().findById(args.siteId)
const activeStrategies = await WIKI.models.authentication.getStrategies({ enabledOnly: true })
const site = await WIKI.db.sites.query().findById(args.siteId)
const activeStrategies = await WIKI.db.authentication.getStrategies({ enabledOnly: true })
return activeStrategies.map(str => {
const siteAuth = _.find(site.config.authStrategies, ['id', str.id]) || {}
return {
@ -64,7 +62,7 @@ module.exports = {
*/
async createApiKey (obj, args, context) {
try {
const key = await WIKI.models.apiKeys.createNewKey(args)
const key = await WIKI.db.apiKeys.createNewKey(args)
await WIKI.auth.reloadApiKeys()
WIKI.events.outbound.emit('reloadApiKeys')
return {
@ -81,7 +79,7 @@ module.exports = {
*/
async login (obj, args, context) {
try {
const authResult = await WIKI.models.users.login(args, context)
const authResult = await WIKI.db.users.login(args, context)
return {
...authResult,
operation: graphHelper.generateSuccess('Login success')
@ -101,7 +99,7 @@ module.exports = {
*/
async loginTFA (obj, args, context) {
try {
const authResult = await WIKI.models.users.loginTFA(args, context)
const authResult = await WIKI.db.users.loginTFA(args, context)
return {
...authResult,
responseResult: graphHelper.generateSuccess('TFA success')
@ -115,7 +113,7 @@ module.exports = {
*/
async changePassword (obj, args, context) {
try {
const authResult = await WIKI.models.users.loginChangePassword(args, context)
const authResult = await WIKI.db.users.loginChangePassword(args, context)
return {
...authResult,
responseResult: graphHelper.generateSuccess('Password changed successfully')
@ -129,7 +127,7 @@ module.exports = {
*/
async forgotPassword (obj, args, context) {
try {
await WIKI.models.users.loginForgotPassword(args, context)
await WIKI.db.users.loginForgotPassword(args, context)
return {
responseResult: graphHelper.generateSuccess('Password reset request processed.')
}
@ -142,7 +140,7 @@ module.exports = {
*/
async register (obj, args, context) {
try {
await WIKI.models.users.register({ ...args, verify: true }, context)
await WIKI.db.users.register({ ...args, verify: true }, context)
return {
responseResult: graphHelper.generateSuccess('Registration success')
}
@ -169,7 +167,7 @@ module.exports = {
*/
async revokeApiKey (obj, args, context) {
try {
await WIKI.models.apiKeys.query().findById(args.id).patch({
await WIKI.db.apiKeys.query().findById(args.id).patch({
isRevoked: true
})
await WIKI.auth.reloadApiKeys()
@ -186,7 +184,7 @@ module.exports = {
*/
async updateAuthStrategies (obj, args, context) {
try {
const previousStrategies = await WIKI.models.authentication.getStrategies()
const previousStrategies = await WIKI.db.authentication.getStrategies()
for (const str of args.strategies) {
const newStr = {
displayName: str.displayName,
@ -202,13 +200,13 @@ module.exports = {
}
if (_.some(previousStrategies, ['key', str.key])) {
await WIKI.models.authentication.query().patch({
await WIKI.db.authentication.query().patch({
key: str.key,
strategyKey: str.strategyKey,
...newStr
}).where('key', str.key)
} else {
await WIKI.models.authentication.query().insert({
await WIKI.db.authentication.query().insert({
key: str.key,
strategyKey: str.strategyKey,
...newStr
@ -217,11 +215,11 @@ module.exports = {
}
for (const str of _.differenceBy(previousStrategies, args.strategies, 'key')) {
const hasUsers = await WIKI.models.users.query().count('* as total').where({ providerKey: str.key }).first()
const hasUsers = await WIKI.db.users.query().count('* as total').where({ providerKey: str.key }).first()
if (_.toSafeInteger(hasUsers.total) > 0) {
throw new Error(`Cannot delete ${str.displayName} as 1 or more users are still using it.`)
} else {
await WIKI.models.authentication.query().delete().where('key', str.key)
await WIKI.db.authentication.query().delete().where('key', str.key)
}
}

@ -1,15 +1,13 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
/**
* Fetch list of Comments Providers
*/
async commentsProviders(obj, args, context, info) {
const providers = await WIKI.models.commentProviders.getProviders()
const providers = await WIKI.db.commentProviders.getProviders()
return providers.map(provider => {
const providerInfo = _.find(WIKI.data.commentProviders, ['key', provider.key]) || {}
return {
@ -34,10 +32,10 @@ module.exports = {
* Fetch list of comments for a page
*/
async comments (obj, args, context) {
const page = await WIKI.models.pages.query().select('id').findOne({ localeCode: args.locale, path: args.path })
const page = await WIKI.db.pages.query().select('id').findOne({ localeCode: args.locale, path: args.path })
if (page) {
if (WIKI.auth.checkAccess(context.req.user, ['read:comments'], args)) {
const comments = await WIKI.models.comments.query().where('pageId', page.id).orderBy('createdAt')
const comments = await WIKI.db.comments.query().where('pageId', page.id).orderBy('createdAt')
return comments.map(c => ({
...c,
authorName: c.name,
@ -59,7 +57,7 @@ module.exports = {
if (!cm || !cm.pageId) {
throw new WIKI.Error.CommentNotFound()
}
const page = await WIKI.models.pages.query().select('localeCode', 'path').findById(cm.pageId)
const page = await WIKI.db.pages.query().select('localeCode', 'path').findById(cm.pageId)
if (page) {
if (WIKI.auth.checkAccess(context.req.user, ['read:comments'], {
path: page.path,
@ -86,7 +84,7 @@ module.exports = {
*/
async createComment (obj, args, context) {
try {
const cmId = await WIKI.models.comments.postNewComment({
const cmId = await WIKI.db.comments.postNewComment({
...args,
user: context.req.user,
ip: context.req.ip
@ -104,7 +102,7 @@ module.exports = {
*/
async updateComment (obj, args, context) {
try {
const cmRender = await WIKI.models.comments.updateComment({
const cmRender = await WIKI.db.comments.updateComment({
...args,
user: context.req.user,
ip: context.req.ip
@ -122,7 +120,7 @@ module.exports = {
*/
async deleteComment (obj, args, context) {
try {
await WIKI.models.comments.deleteComment({
await WIKI.db.comments.deleteComment({
id: args.id,
user: context.req.user,
ip: context.req.ip
@ -140,7 +138,7 @@ module.exports = {
async updateCommentsProviders(obj, args, context) {
try {
for (let provider of args.providers) {
await WIKI.models.commentProviders.query().patch({
await WIKI.db.commentProviders.query().patch({
isEnabled: provider.isEnabled,
config: _.reduce(provider.config, (result, value, key) => {
_.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))
@ -148,7 +146,7 @@ module.exports = {
}, {})
}).where('key', provider.key)
}
await WIKI.models.commentProviders.initProvider()
await WIKI.db.commentProviders.initProvider()
return {
responseResult: graphHelper.generateSuccess('Comment Providers updated successfully')
}

@ -3,24 +3,22 @@ const safeRegex = require('safe-regex')
const _ = require('lodash')
const { v4: uuid } = require('uuid')
/* global WIKI */
module.exports = {
Query: {
/**
* FETCH ALL GROUPS
*/
async groups () {
return WIKI.models.groups.query().select(
return WIKI.db.groups.query().select(
'groups.*',
WIKI.models.groups.relatedQuery('users').count().as('userCount')
WIKI.db.groups.relatedQuery('users').count().as('userCount')
)
},
/**
* FETCH A SINGLE GROUP
*/
async groupById(obj, args) {
return WIKI.models.groups.query().findById(args.id)
return WIKI.db.groups.query().findById(args.id)
}
},
Mutation: {
@ -34,7 +32,7 @@ module.exports = {
}
// Check for valid group
const grp = await WIKI.models.groups.query().findById(args.groupId)
const grp = await WIKI.db.groups.query().findById(args.groupId)
if (!grp) {
throw new Error('Invalid Group ID')
}
@ -51,13 +49,13 @@ module.exports = {
}
// Check for valid user
const usr = await WIKI.models.users.query().findById(args.userId)
const usr = await WIKI.db.users.query().findById(args.userId)
if (!usr) {
throw new Error('Invalid User ID')
}
// Check for existing relation
const relExist = await WIKI.models.knex('userGroups').where({
const relExist = await WIKI.db.knex('userGroups').where({
userId: args.userId,
groupId: args.groupId
}).first()
@ -80,7 +78,7 @@ module.exports = {
* CREATE NEW GROUP
*/
async createGroup (obj, args, { req }) {
const group = await WIKI.models.groups.query().insertAndFetch({
const group = await WIKI.db.groups.query().insertAndFetch({
name: args.name,
permissions: JSON.stringify(WIKI.data.groups.defaultPermissions),
rules: JSON.stringify(WIKI.data.groups.defaultRules.map(r => ({
@ -104,7 +102,7 @@ module.exports = {
throw new Error('Cannot delete this group.')
}
await WIKI.models.groups.query().deleteById(args.id)
await WIKI.db.groups.query().deleteById(args.id)
WIKI.auth.revokeUserTokens({ id: args.id, kind: 'g' })
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'g' })
@ -126,11 +124,11 @@ module.exports = {
if (args.userId === 1 && args.groupId === 1) {
throw new Error('Cannot unassign Administrator user from Administrators group.')
}
const grp = await WIKI.models.groups.query().findById(args.groupId)
const grp = await WIKI.db.groups.query().findById(args.groupId)
if (!grp) {
throw new Error('Invalid Group ID')
}
const usr = await WIKI.models.users.query().findById(args.userId)
const usr = await WIKI.db.users.query().findById(args.userId)
if (!usr) {
throw new Error('Invalid User ID')
}
@ -179,7 +177,7 @@ module.exports = {
}
// Update group
await WIKI.models.groups.query().patch({
await WIKI.db.groups.query().patch({
name: args.name,
redirectOnLogin: args.redirectOnLogin,
permissions: JSON.stringify(args.permissions),

@ -1,15 +1,13 @@
const graphHelper = require('../../helpers/graph')
const _ = require('lodash')
/* global WIKI */
module.exports = {
Query: {
async hooks () {
return WIKI.models.hooks.query().orderBy('name')
return WIKI.db.hooks.query().orderBy('name')
},
async hookById (obj, args) {
return WIKI.models.hooks.query().findById(args.id)
return WIKI.db.hooks.query().findById(args.id)
}
},
Mutation: {
@ -29,7 +27,7 @@ module.exports = {
throw new WIKI.Error.Custom('HookCreateInvalidURL', 'Invalid Hook URL')
}
// -> Create hook
const newHook = await WIKI.models.hooks.createHook(args)
const newHook = await WIKI.db.hooks.createHook(args)
WIKI.logger.debug(`New Hook ${newHook.id} created successfully.`)
return {
@ -46,7 +44,7 @@ module.exports = {
async updateHook (obj, args) {
try {
// -> Load hook
const hook = await WIKI.models.hooks.query().findById(args.id)
const hook = await WIKI.db.hooks.query().findById(args.id)
if (!hook) {
throw new WIKI.Error.Custom('HookInvalidId', 'Invalid Hook ID')
}
@ -61,7 +59,7 @@ module.exports = {
throw new WIKI.Error.Custom('HookInvalidURL', 'URL is invalid.')
}
// -> Update hook
await WIKI.models.hooks.query().findById(args.id).patch(args.patch)
await WIKI.db.hooks.query().findById(args.id).patch(args.patch)
WIKI.logger.debug(`Hook ${args.id} updated successfully.`)
return {
@ -76,7 +74,7 @@ module.exports = {
*/
async deleteHook (obj, args) {
try {
await WIKI.models.hooks.deleteHook(args.id)
await WIKI.db.hooks.deleteHook(args.id)
WIKI.logger.debug(`Hook ${args.id} deleted successfully.`)
return {
operation: graphHelper.generateSuccess('Hook deleted successfully')

@ -1,13 +1,11 @@
const graphHelper = require('../../helpers/graph')
const _ = require('lodash')
/* global WIKI */
module.exports = {
Query: {
async locales(obj, args, context, info) {
let remoteLocales = await WIKI.cache.get('locales')
let localLocales = await WIKI.models.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt', 'availability')
let localLocales = await WIKI.db.locales.query().select('code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt', 'availability')
remoteLocales = remoteLocales || localLocales
return _.map(remoteLocales, rl => {
let isInstalled = _.some(localLocales, ['code', rl.code])
@ -44,7 +42,7 @@ module.exports = {
WIKI.config.lang.namespacing = args.namespacing
WIKI.config.lang.namespaces = _.union(args.namespaces, [args.locale])
const newLocale = await WIKI.models.locales.query().select('isRTL').where('code', args.locale).first()
const newLocale = await WIKI.db.locales.query().select('isRTL').where('code', args.locale).first()
WIKI.config.lang.rtl = newLocale.isRTL
await WIKI.configSvc.saveToDb(['lang'])

@ -1,8 +1,6 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
async mailConfig(obj, args, context, info) {

@ -1,11 +1,9 @@
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
async navigationTree (obj, args, context, info) {
return WIKI.models.navigation.getTree({ cache: false, locale: 'all', bypassAuth: true })
return WIKI.db.navigation.getTree({ cache: false, locale: 'all', bypassAuth: true })
},
navigationConfig (obj, args, context, info) {
return WIKI.config.nav
@ -14,7 +12,7 @@ module.exports = {
Mutation: {
async updateNavigationTree (obj, args, context) {
try {
await WIKI.models.navigation.query().patch({
await WIKI.db.navigation.query().patch({
config: args.tree
}).where('key', 'site')
for (const tree of args.tree) {

@ -1,20 +1,18 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
/**
* PAGE HISTORY
*/
async pageHistoryById (obj, args, context, info) {
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.id)
const page = await WIKI.db.pages.query().select('path', 'localeCode').findById(args.id)
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
path: page.path,
locale: page.localeCode
})) {
return WIKI.models.pageHistory.getHistory({
return WIKI.db.pageHistory.getHistory({
pageId: args.id,
offsetPage: args.offsetPage || 0,
offsetSize: args.offsetSize || 100
@ -27,12 +25,12 @@ module.exports = {
* PAGE VERSION
*/
async pageVersionById (obj, args, context, info) {
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
const page = await WIKI.db.pages.query().select('path', 'localeCode').findById(args.pageId)
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
path: page.path,
locale: page.localeCode
})) {
return WIKI.models.pageHistory.getVersion({
return WIKI.db.pageHistory.getVersion({
pageId: args.pageId,
versionId: args.versionId
})
@ -68,7 +66,7 @@ module.exports = {
* LIST PAGES
*/
async pages (obj, args, context, info) {
let results = await WIKI.models.pages.query().column([
let results = await WIKI.db.pages.query().column([
'pages.id',
'path',
{ locale: 'localeCode' },
@ -144,7 +142,7 @@ module.exports = {
* FETCH SINGLE PAGE
*/
async pageById (obj, args, context, info) {
let page = await WIKI.models.pages.getPageFromDb(args.id)
let page = await WIKI.db.pages.getPageFromDb(args.id)
if (page) {
if (WIKI.auth.checkAccess(context.req.user, ['manage:pages', 'delete:pages'], {
path: page.path,
@ -166,7 +164,7 @@ module.exports = {
* FETCH TAGS
*/
async tags (obj, args, context, info) {
const pages = await WIKI.models.pages.query()
const pages = await WIKI.db.pages.query()
.column([
'path',
{ locale: 'localeCode' }
@ -185,7 +183,7 @@ module.exports = {
*/
async searchTags (obj, args, context, info) {
const query = _.trim(args.query)
const pages = await WIKI.models.pages.query()
const pages = await WIKI.db.pages.query()
.column([
'path',
{ locale: 'localeCode' }
@ -220,7 +218,7 @@ module.exports = {
if (!args.locale) { args.locale = WIKI.config.lang.code }
if (args.path && !args.parent) {
curPage = await WIKI.models.knex('pageTree').first('parent', 'ancestors').where({
curPage = await WIKI.db.knex('pageTree').first('parent', 'ancestors').where({
path: args.path,
localeCode: args.locale
})
@ -231,7 +229,7 @@ module.exports = {
}
}
const results = await WIKI.models.knex('pageTree').where(builder => {
const results = await WIKI.db.knex('pageTree').where(builder => {
builder.where('localeCode', args.locale)
switch (args.mode) {
case 'FOLDERS':
@ -268,14 +266,14 @@ module.exports = {
let results
if (WIKI.config.db.type === 'mysql' || WIKI.config.db.type === 'mariadb' || WIKI.config.db.type === 'sqlite') {
results = await WIKI.models.knex('pages')
results = await WIKI.db.knex('pages')
.column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
.leftJoin('pageLinks', 'pages.id', 'pageLinks.pageId')
.where({
'pages.localeCode': args.locale
})
.unionAll(
WIKI.models.knex('pageLinks')
WIKI.db.knex('pageLinks')
.column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
.leftJoin('pages', 'pageLinks.pageId', 'pages.id')
.where({
@ -283,7 +281,7 @@ module.exports = {
})
)
} else {
results = await WIKI.models.knex('pages')
results = await WIKI.db.knex('pages')
.column({ id: 'pages.id' }, { path: 'pages.path' }, 'title', { link: 'pageLinks.path' }, { locale: 'pageLinks.localeCode' })
.fullOuterJoin('pageLinks', 'pages.id', 'pageLinks.pageId')
.where({
@ -320,7 +318,7 @@ module.exports = {
* CHECK FOR EDITING CONFLICT
*/
async checkConflicts (obj, args, context, info) {
let page = await WIKI.models.pages.query().select('path', 'localeCode', 'updatedAt').findById(args.id)
let page = await WIKI.db.pages.query().select('path', 'localeCode', 'updatedAt').findById(args.id)
if (page) {
if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
path: page.path,
@ -338,7 +336,7 @@ module.exports = {
* FETCH LATEST VERSION FOR CONFLICT COMPARISON
*/
async checkConflictsLatest (obj, args, context, info) {
let page = await WIKI.models.pages.getPageFromDb(args.id)
let page = await WIKI.db.pages.getPageFromDb(args.id)
if (page) {
if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
path: page.path,
@ -363,7 +361,7 @@ module.exports = {
*/
async createPage(obj, args, context) {
try {
const page = await WIKI.models.pages.createPage({
const page = await WIKI.db.pages.createPage({
...args,
user: context.req.user
})
@ -380,7 +378,7 @@ module.exports = {
*/
async updatePage(obj, args, context) {
try {
const page = await WIKI.models.pages.updatePage({
const page = await WIKI.db.pages.updatePage({
...args,
user: context.req.user
})
@ -397,7 +395,7 @@ module.exports = {
*/
async convertPage(obj, args, context) {
try {
await WIKI.models.pages.convertPage({
await WIKI.db.pages.convertPage({
...args,
user: context.req.user
})
@ -413,7 +411,7 @@ module.exports = {
*/
async renamePage(obj, args, context) {
try {
await WIKI.models.pages.movePage({
await WIKI.db.pages.movePage({
...args,
user: context.req.user
})
@ -429,7 +427,7 @@ module.exports = {
*/
async deletePage(obj, args, context) {
try {
await WIKI.models.pages.deletePage({
await WIKI.db.pages.deletePage({
...args,
user: context.req.user
})
@ -445,10 +443,10 @@ module.exports = {
*/
async deleteTag (obj, args, context) {
try {
const tagToDel = await WIKI.models.tags.query().findById(args.id)
const tagToDel = await WIKI.db.tags.query().findById(args.id)
if (tagToDel) {
await tagToDel.$relatedQuery('pages').unrelate()
await WIKI.models.tags.query().deleteById(args.id)
await WIKI.db.tags.query().deleteById(args.id)
} else {
throw new Error('This tag does not exist.')
}
@ -464,7 +462,7 @@ module.exports = {
*/
async updateTag (obj, args, context) {
try {
const affectedRows = await WIKI.models.tags.query()
const affectedRows = await WIKI.db.tags.query()
.findById(args.id)
.patch({
tag: _.trim(args.tag).toLowerCase(),
@ -485,7 +483,7 @@ module.exports = {
*/
async flushCache(obj, args, context) {
try {
await WIKI.models.pages.flushCache()
await WIKI.db.pages.flushCache()
WIKI.events.outbound.emit('flushCache')
return {
responseResult: graphHelper.generateSuccess('Pages Cache has been flushed successfully.')
@ -499,7 +497,7 @@ module.exports = {
*/
async migrateToLocale(obj, args, context) {
try {
const count = await WIKI.models.pages.migrateToLocale(args)
const count = await WIKI.db.pages.migrateToLocale(args)
return {
responseResult: graphHelper.generateSuccess('Migrated content to target locale successfully.'),
count
@ -513,7 +511,7 @@ module.exports = {
*/
async rebuildPageTree(obj, args, context) {
try {
await WIKI.models.pages.rebuildTree()
await WIKI.db.pages.rebuildTree()
return {
responseResult: graphHelper.generateSuccess('Page tree rebuilt successfully.')
}
@ -526,11 +524,11 @@ module.exports = {
*/
async renderPage (obj, args, context) {
try {
const page = await WIKI.models.pages.query().findById(args.id)
const page = await WIKI.db.pages.query().findById(args.id)
if (!page) {
throw new WIKI.Error.PageNotFound()
}
await WIKI.models.pages.renderPage(page)
await WIKI.db.pages.renderPage(page)
return {
responseResult: graphHelper.generateSuccess('Page rendered successfully.')
}
@ -543,7 +541,7 @@ module.exports = {
*/
async restorePage (obj, args, context) {
try {
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
const page = await WIKI.db.pages.query().select('path', 'localeCode').findById(args.pageId)
if (!page) {
throw new WIKI.Error.PageNotFound()
}
@ -555,12 +553,12 @@ module.exports = {
throw new WIKI.Error.PageRestoreForbidden()
}
const targetVersion = await WIKI.models.pageHistory.getVersion({ pageId: args.pageId, versionId: args.versionId })
const targetVersion = await WIKI.db.pageHistory.getVersion({ pageId: args.pageId, versionId: args.versionId })
if (!targetVersion) {
throw new WIKI.Error.PageNotFound()
}
await WIKI.models.pages.updatePage({
await WIKI.db.pages.updatePage({
...targetVersion,
id: targetVersion.pageId,
user: context.req.user,
@ -579,7 +577,7 @@ module.exports = {
*/
async purgePagesHistory (obj, args, context) {
try {
await WIKI.models.pageHistory.purge(args.olderThan)
await WIKI.db.pageHistory.purge(args.olderThan)
return {
responseResult: graphHelper.generateSuccess('Page history purged successfully.')
}
@ -590,7 +588,7 @@ module.exports = {
},
Page: {
async tags (obj) {
return WIKI.models.pages.relatedQuery('tags').for(obj.id)
return WIKI.db.pages.relatedQuery('tags').for(obj.id)
}
// comments(pg) {
// return pg.$relatedQuery('comments')

@ -1,12 +1,10 @@
const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Query: {
async renderers(obj, args, context, info) {
let renderers = await WIKI.models.renderers.getRenderers()
let renderers = await WIKI.db.renderers.getRenderers()
renderers = renderers.map(rdr => {
const rendererInfo = _.find(WIKI.data.renderers, ['key', rdr.key]) || {}
return {
@ -35,7 +33,7 @@ module.exports = {
async updateRenderers(obj, args, context) {
try {
for (let rdr of args.renderers) {
await WIKI.models.renderers.query().patch({
await WIKI.db.renderers.query().patch({
isEnabled: rdr.isEnabled,
config: _.reduce(rdr.config, (result, value, key) => {
_.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null))

@ -1,7 +1,5 @@
const graphHelper = require('../../helpers/graph')
/* global WIKI */
module.exports = {
Mutation: {
async rebuildSearchIndex (obj, args, context) {

@ -5,12 +5,10 @@ const path = require('path')
const fs = require('fs-extra')
const { v4: uuid } = require('uuid')
/* global WIKI */
module.exports = {
Query: {
async sites () {
const sites = await WIKI.models.sites.query().orderBy('hostname')
const sites = await WIKI.db.sites.query().orderBy('hostname')
return sites.map(s => ({
...s.config,
id: s.id,
@ -20,7 +18,7 @@ module.exports = {
}))
},
async siteById (obj, args) {
const site = await WIKI.models.sites.query().findById(args.id)
const site = await WIKI.db.sites.query().findById(args.id)
return site ? {
...site.config,
id: site.id,
@ -30,11 +28,11 @@ module.exports = {
} : null
},
async siteByHostname (obj, args) {
let site = await WIKI.models.sites.query().where({
let site = await WIKI.db.sites.query().where({
hostname: args.hostname
}).first()
if (!site && !args.exact) {
site = await WIKI.models.sites.query().where({
site = await WIKI.db.sites.query().where({
hostname: '*'
}).first()
}
@ -62,7 +60,7 @@ module.exports = {
}
// -> Check for duplicate catch-all
if (args.hostname === '*') {
const site = await WIKI.models.sites.query().where({
const site = await WIKI.db.sites.query().where({
hostname: args.hostname
}).first()
if (site) {
@ -70,7 +68,7 @@ module.exports = {
}
}
// -> Create site
const newSite = await WIKI.models.sites.createSite(args.hostname, {
const newSite = await WIKI.db.sites.createSite(args.hostname, {
title: args.title
})
return {
@ -88,7 +86,7 @@ module.exports = {
async updateSite (obj, args) {
try {
// -> Load site
const site = await WIKI.models.sites.query().findById(args.id)
const site = await WIKI.db.sites.query().findById(args.id)
if (!site) {
throw new WIKI.Error.Custom('SiteInvalidId', 'Invalid Site ID')
}
@ -98,7 +96,7 @@ module.exports = {
}
// -> Check for duplicate catch-all
if (args.patch.hostname === '*' && site.hostname !== '*') {
const dupSite = await WIKI.models.sites.query().where({ hostname: '*' }).first()
const dupSite = await WIKI.db.sites.query().where({ hostname: '*' }).first()
if (dupSite) {
throw new WIKI.Error.Custom('SiteUpdateDuplicateCatchAll', `Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`)
}
@ -112,7 +110,7 @@ module.exports = {
args.patch.pageExtensions = args.patch.pageExtensions.split(',').map(ext => ext.trim().toLowerCase()).filter(ext => ext.length > 0)
}
// -> Update site
await WIKI.models.sites.updateSite(args.id, {
await WIKI.db.sites.updateSite(args.id, {
hostname: args.patch.hostname ?? site.hostname,
isEnabled: args.patch.isEnabled ?? site.isEnabled,
config: _.defaultsDeep(_.omit(args.patch, ['hostname', 'isEnabled']), site.config)
@ -132,12 +130,12 @@ module.exports = {
async deleteSite (obj, args) {
try {
// -> Ensure site isn't last one
const sitesCount = await WIKI.models.sites.query().count('id').first()
const sitesCount = await WIKI.db.sites.query().count('id').first()
if (sitesCount?.count && _.toNumber(sitesCount?.count) <= 1) {
throw new WIKI.Error.Custom('SiteDeleteLastSite', 'Cannot delete the last site. At least 1 site must exists at all times.')
}
// -> Delete site
await WIKI.models.sites.deleteSite(args.id)
await WIKI.db.sites.deleteSite(args.id)
return {
operation: graphHelper.generateSuccess('Site deleted successfully')
}
@ -175,16 +173,16 @@ module.exports = {
height: 72
})
// -> Save logo meta to DB
const site = await WIKI.models.sites.query().findById(args.id)
const site = await WIKI.db.sites.query().findById(args.id)
if (!site.config.assets.logo) {
site.config.assets.logo = uuid()
}
site.config.assets.logoExt = destFormat
await WIKI.models.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.models.sites.reloadCache()
await WIKI.db.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.db.sites.reloadCache()
// -> Save image data to DB
const imgBuffer = await fs.readFile(destPath)
await WIKI.models.knex('assetData').insert({
await WIKI.db.knex('assetData').insert({
id: site.config.assets.logo,
data: imgBuffer
}).onConflict('id').merge()
@ -227,16 +225,16 @@ module.exports = {
height: 64
})
// -> Save favicon meta to DB
const site = await WIKI.models.sites.query().findById(args.id)
const site = await WIKI.db.sites.query().findById(args.id)
if (!site.config.assets.favicon) {
site.config.assets.favicon = uuid()
}
site.config.assets.faviconExt = destFormat
await WIKI.models.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.models.sites.reloadCache()
await WIKI.db.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.db.sites.reloadCache()
// -> Save image data to DB
const imgBuffer = await fs.readFile(destPath)
await WIKI.models.knex('assetData').insert({
await WIKI.db.knex('assetData').insert({
id: site.config.assets.favicon,
data: imgBuffer
}).onConflict('id').merge()
@ -277,15 +275,15 @@ module.exports = {
width: 1920
})
// -> Save login bg meta to DB
const site = await WIKI.models.sites.query().findById(args.id)
const site = await WIKI.db.sites.query().findById(args.id)
if (!site.config.assets.loginBg) {
site.config.assets.loginBg = uuid()
await WIKI.models.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.models.sites.reloadCache()
await WIKI.db.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.db.sites.reloadCache()
}
// -> Save image data to DB
const imgBuffer = await fs.readFile(destPath)
await WIKI.models.knex('assetData').insert({
await WIKI.db.knex('assetData').insert({
id: site.config.assets.loginBg,
data: imgBuffer
}).onConflict('id').merge()

@ -2,12 +2,10 @@ const _ = require('lodash')
const graphHelper = require('../../helpers/graph')
const { v4: uuid } = require('uuid')
/* global WIKI */
module.exports = {
Query: {
async storageTargets (obj, args, context, info) {
const dbTargets = await WIKI.models.storage.getTargets({ siteId: args.siteId })
const dbTargets = await WIKI.db.storage.getTargets({ siteId: args.siteId })
// targets = _.sortBy(targets.map(tgt => {
// const targetInfo = _.find(WIKI.data.storage, ['module', tgt.key]) || {}
// return {
@ -101,7 +99,7 @@ module.exports = {
async updateStorageTargets (obj, args, context) {
WIKI.logger.debug(`Updating storage targets for site ${args.siteId}...`)
try {
const dbTargets = await WIKI.models.storage.getTargets({ siteId: args.siteId })
const dbTargets = await WIKI.db.storage.getTargets({ siteId: args.siteId })
for (const tgt of args.targets) {
const md = _.find(WIKI.storage.defs, ['key', tgt.module])
if (!md) {
@ -124,7 +122,7 @@ module.exports = {
// -> Target doesn't exist yet in the DB, let's create it
if (!dbTarget) {
WIKI.logger.debug(`No existing DB configuration for module ${tgt.module}. Creating a new one...`)
await WIKI.models.storage.query().insert({
await WIKI.db.storage.query().insert({
id: tgt.id,
module: tgt.module,
siteId: args.siteId,
@ -147,7 +145,7 @@ module.exports = {
})
} else {
WIKI.logger.debug(`Updating DB configuration for module ${tgt.module}...`)
await WIKI.models.storage.query().patch({
await WIKI.db.storage.query().patch({
isEnabled: tgt.isEnabled ?? dbTarget.isEnabled ?? false,
contentTypes: {
activeTypes: tgt.contentTypes ?? dbTarget?.contentTypes?.activeTypes ?? [],
@ -164,7 +162,7 @@ module.exports = {
}).where('id', tgt.id)
}
}
// await WIKI.models.storage.initTargets()
// await WIKI.db.storage.initTargets()
return {
status: graphHelper.generateSuccess('Storage targets updated successfully')
}
@ -174,7 +172,7 @@ module.exports = {
},
async setupStorageTarget (obj, args, context) {
try {
const tgt = await WIKI.models.storage.query().findById(args.targetId)
const tgt = await WIKI.db.storage.query().findById(args.targetId)
if (!tgt) {
throw new Error('Not storage target matching this ID')
}
@ -182,7 +180,7 @@ module.exports = {
if (!md) {
throw new Error('No matching storage module installed.')
}
if (!await WIKI.models.storage.ensureModule(md.key)) {
if (!await WIKI.db.storage.ensureModule(md.key)) {
throw new Error('Failed to load storage module. Check logs for details.')
}
const result = await WIKI.storage.modules[md.key].setup(args.targetId, args.state)
@ -197,7 +195,7 @@ module.exports = {
},
async destroyStorageTargetSetup (obj, args, context) {
try {
const tgt = await WIKI.models.storage.query().findById(args.targetId)
const tgt = await WIKI.db.storage.query().findById(args.targetId)
if (!tgt) {
throw new Error('Not storage target matching this ID')
}
@ -205,7 +203,7 @@ module.exports = {
if (!md) {
throw new Error('No matching storage module installed.')
}
if (!await WIKI.models.storage.ensureModule(md.key)) {
if (!await WIKI.db.storage.ensureModule(md.key)) {
throw new Error('Failed to load storage module. Check logs for details.')
}
await WIKI.storage.modules[md.key].setupDestroy(args.targetId)
@ -219,7 +217,7 @@ module.exports = {
},
async executeStorageAction (obj, args, context) {
try {
await WIKI.models.storage.executeAction(args.targetKey, args.handler)
await WIKI.db.storage.executeAction(args.targetKey, args.handler)
return {
status: graphHelper.generateSuccess('Action completed.')
}

@ -9,8 +9,6 @@ const { DateTime } = require('luxon')
const graphHelper = require('../../helpers/graph')
const cronParser = require('cron-parser')
/* global WIKI */
module.exports = {
Query: {
systemFlags () {
@ -183,19 +181,19 @@ module.exports = {
return process.cwd()
},
async groupsTotal () {
const total = await WIKI.models.groups.query().count('* as total').first()
const total = await WIKI.db.groups.query().count('* as total').first()
return _.toSafeInteger(total.total)
},
async pagesTotal () {
const total = await WIKI.models.pages.query().count('* as total').first()
const total = await WIKI.db.pages.query().count('* as total').first()
return _.toSafeInteger(total.total)
},
async usersTotal () {
const total = await WIKI.models.users.query().count('* as total').first()
const total = await WIKI.db.users.query().count('* as total').first()
return _.toSafeInteger(total.total)
},
async tagsTotal () {
const total = await WIKI.models.tags.query().count('* as total').first()
const total = await WIKI.db.tags.query().count('* as total').first()
return _.toSafeInteger(total.total)
}
}

@ -1,8 +1,6 @@
const graphHelper = require('../../helpers/graph')
const _ = require('lodash')
/* global WIKI */
module.exports = {
Query: {
/**
@ -22,7 +20,7 @@ module.exports = {
}
// -> Fetch Users
return WIKI.models.users.query()
return WIKI.db.users.query()
.select('id', 'email', 'name', 'isSystem', 'isActive', 'createdAt', 'lastLoginAt')
.where(builder => {
if (args.filter) {
@ -38,7 +36,7 @@ module.exports = {
* FETCH A SINGLE USER
*/
async userById (obj, args, context, info) {
const usr = await WIKI.models.users.query().findById(args.id)
const usr = await WIKI.db.users.query().findById(args.id)
if (!usr) {
throw new Error('Invalid User')
@ -64,7 +62,7 @@ module.exports = {
// if (!context.req.user || context.req.user.id < 1 || context.req.user.id === 2) {
// throw new WIKI.Error.AuthRequired()
// }
// const usr = await WIKI.models.users.query().findById(context.req.user.id)
// const usr = await WIKI.db.users.query().findById(context.req.user.id)
// if (!usr.isActive) {
// throw new WIKI.Error.AuthAccountBanned()
// }
@ -80,7 +78,7 @@ module.exports = {
// return usr
// },
async lastLogins (obj, args, context, info) {
return WIKI.models.users.query()
return WIKI.db.users.query()
.select('id', 'name', 'lastLoginAt')
.whereNotNull('lastLoginAt')
.orderBy('lastLoginAt', 'desc')
@ -90,7 +88,7 @@ module.exports = {
Mutation: {
async createUser (obj, args) {
try {
await WIKI.models.users.createNewUser({ ...args, passwordRaw: args.password, isVerified: true })
await WIKI.db.users.createNewUser({ ...args, passwordRaw: args.password, isVerified: true })
return {
operation: graphHelper.generateSuccess('User created successfully')
@ -104,7 +102,7 @@ module.exports = {
if (args.id <= 2) {
throw new WIKI.Error.UserDeleteProtected()
}
await WIKI.models.users.deleteUser(args.id, args.replaceId)
await WIKI.db.users.deleteUser(args.id, args.replaceId)
WIKI.auth.revokeUserTokens({ id: args.id, kind: 'u' })
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' })
@ -122,7 +120,7 @@ module.exports = {
},
async updateUser (obj, args) {
try {
await WIKI.models.users.updateUser(args.id, args.patch)
await WIKI.db.users.updateUser(args.id, args.patch)
return {
operation: graphHelper.generateSuccess('User updated successfully')
@ -133,7 +131,7 @@ module.exports = {
},
async verifyUser (obj, args) {
try {
await WIKI.models.users.query().patch({ isVerified: true }).findById(args.id)
await WIKI.db.users.query().patch({ isVerified: true }).findById(args.id)
return {
operation: graphHelper.generateSuccess('User verified successfully')
@ -144,7 +142,7 @@ module.exports = {
},
async activateUser (obj, args) {
try {
await WIKI.models.users.query().patch({ isActive: true }).findById(args.id)
await WIKI.db.users.query().patch({ isActive: true }).findById(args.id)
return {
operation: graphHelper.generateSuccess('User activated successfully')
@ -158,7 +156,7 @@ module.exports = {
if (args.id <= 2) {
throw new Error('Cannot deactivate system accounts.')
}
await WIKI.models.users.query().patch({ isActive: false }).findById(args.id)
await WIKI.db.users.query().patch({ isActive: false }).findById(args.id)
WIKI.auth.revokeUserTokens({ id: args.id, kind: 'u' })
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' })
@ -172,7 +170,7 @@ module.exports = {
},
async enableUserTFA (obj, args) {
try {
await WIKI.models.users.query().patch({ tfaIsActive: true, tfaSecret: null }).findById(args.id)
await WIKI.db.users.query().patch({ tfaIsActive: true, tfaSecret: null }).findById(args.id)
return {
operation: graphHelper.generateSuccess('User 2FA enabled successfully')
@ -183,7 +181,7 @@ module.exports = {
},
async disableUserTFA (obj, args) {
try {
await WIKI.models.users.query().patch({ tfaIsActive: false, tfaSecret: null }).findById(args.id)
await WIKI.db.users.query().patch({ tfaIsActive: false, tfaSecret: null }).findById(args.id)
return {
operation: graphHelper.generateSuccess('User 2FA disabled successfully')
@ -200,7 +198,7 @@ module.exports = {
if (!context.req.user || context.req.user.id === WIKI.auth.guest.id) {
throw new WIKI.Error.AuthRequired()
}
const usr = await WIKI.models.users.query().findById(context.req.user.id)
const usr = await WIKI.db.users.query().findById(context.req.user.id)
if (!usr.isActive) {
throw new WIKI.Error.AuthAccountBanned()
}
@ -216,7 +214,7 @@ module.exports = {
throw new WIKI.Error.InputInvalid()
}
await WIKI.models.users.query().findById(usr.id).patch({
await WIKI.db.users.query().findById(usr.id).patch({
name: args.name?.trim() ?? usr.name,
meta: {
...usr.meta,
@ -245,7 +243,7 @@ module.exports = {
if (!context.req.user || context.req.user.id < 1 || context.req.user.id === 2) {
throw new WIKI.Error.AuthRequired()
}
const usr = await WIKI.models.users.query().findById(context.req.user.id)
const usr = await WIKI.db.users.query().findById(context.req.user.id)
if (!usr.isActive) {
throw new WIKI.Error.AuthAccountBanned()
}
@ -261,12 +259,12 @@ module.exports = {
throw new WIKI.Error.AuthPasswordInvalid()
}
await WIKI.models.users.updateUser({
await WIKI.db.users.updateUser({
id: usr.id,
newPassword: args.new
})
const newToken = await WIKI.models.users.refreshToken(usr)
const newToken = await WIKI.db.users.refreshToken(usr)
return {
responseResult: graphHelper.generateSuccess('Password changed successfully'),
@ -288,7 +286,7 @@ module.exports = {
// return usrGroups.map(g => g.name)
// },
// async pagesTotal (usr) {
// const result = await WIKI.models.pages.query().count('* as total').where('creatorId', usr.id).first()
// const result = await WIKI.db.pages.query().count('* as total').where('creatorId', usr.id).first()
// return _.toSafeInteger(result.total)
// }
// }

@ -14,8 +14,6 @@ const contentToExt = {
}
const extToContent = _.invert(contentToExt)
/* global WIKI */
module.exports = {
/**
* Parse raw url path and make it safe

@ -15,7 +15,6 @@ if (!semver.satisfies(process.version, '>=18')) {
let WIKI = {
IS_DEBUG: process.env.NODE_ENV === 'development',
IS_MASTER: true,
ROOTPATH: process.cwd(),
INSTANCE_ID: nanoid(10),
SERVERPATH: path.join(process.cwd(), 'server'),

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
/**
* SEO Middleware
*

@ -5,8 +5,6 @@ const _ = require('lodash')
const yaml = require('js-yaml')
const commonHelper = require('../helpers/common')
/* global WIKI */
/**
* Analytics model
*/
@ -31,7 +29,7 @@ module.exports = class Analytics extends Model {
}
static async getProviders(isEnabled) {
const providers = await WIKI.models.analytics.query().where(_.isBoolean(isEnabled) ? { isEnabled } : {})
const providers = await WIKI.db.analytics.query().where(_.isBoolean(isEnabled) ? { isEnabled } : {})
return _.sortBy(providers, ['module'])
}
@ -69,7 +67,7 @@ module.exports = class Analytics extends Model {
bodyStart: '',
bodyEnd: ''
}
const providers = await WIKI.models.analytics.getProviders(true)
const providers = await WIKI.db.analytics.getProviders(true)
for (let provider of providers) {
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/analytics', provider.key, 'code.yml'), 'utf8')

@ -43,7 +43,7 @@ module.exports = class ApiKey extends Model {
static async createNewKey ({ name, expiration, groups }) {
console.info(DateTime.utc().plus(ms(expiration)).toISO())
const entry = await WIKI.models.apiKeys.query().insert({
const entry = await WIKI.db.apiKeys.query().insert({
name,
key: 'pending',
expiration: DateTime.utc().plus(ms(expiration)).toISO(),
@ -65,7 +65,7 @@ module.exports = class ApiKey extends Model {
issuer: 'urn:wiki.js'
})
await WIKI.models.apiKeys.query().findById(entry.id).patch({
await WIKI.db.apiKeys.query().findById(entry.id).patch({
key,
isRevoked: false
})

@ -1,8 +1,6 @@
const Model = require('objection').Model
const _ = require('lodash')
/* global WIKI */
/**
* Users model
*/
@ -42,13 +40,13 @@ module.exports = class AssetFolder extends Model {
static async getHierarchy (folderId) {
let hier
if (WIKI.config.db.type === 'mssql') {
hier = await WIKI.models.knex.with('ancestors', qb => {
hier = await WIKI.db.knex.with('ancestors', qb => {
qb.select('id', 'name', 'slug', 'parentId').from('assetFolders').where('id', folderId).unionAll(sqb => {
sqb.select('a.id', 'a.name', 'a.slug', 'a.parentId').from('assetFolders AS a').join('ancestors', 'ancestors.parentId', 'a.id')
})
}).select('*').from('ancestors')
} else {
hier = await WIKI.models.knex.withRecursive('ancestors', qb => {
hier = await WIKI.db.knex.withRecursive('ancestors', qb => {
qb.select('id', 'name', 'slug', 'parentId').from('assetFolders').where('id', folderId).union(sqb => {
sqb.select('a.id', 'a.name', 'a.slug', 'a.parentId').from('assetFolders AS a').join('ancestors', 'ancestors.parentId', 'a.id')
})
@ -62,7 +60,7 @@ module.exports = class AssetFolder extends Model {
* Get full folder paths
*/
static async getAllPaths () {
const all = await WIKI.models.assetFolders.query()
const all = await WIKI.db.assetFolders.query()
let folders = {}
all.forEach(fld => {
_.set(folders, fld.id, fld.slug)

@ -69,7 +69,7 @@ module.exports = class Asset extends Model {
async getAssetPath() {
let hierarchy = []
if (this.folderId) {
hierarchy = await WIKI.models.assetFolders.getHierarchy(this.folderId)
hierarchy = await WIKI.db.assetFolders.getHierarchy(this.folderId)
}
return (this.folderId) ? hierarchy.map(h => h.slug).join('/') + `/${this.filename}` : this.filename
}
@ -83,7 +83,7 @@ module.exports = class Asset extends Model {
const fileHash = assetHelper.generateHash(opts.assetPath)
// Check for existing asset
let asset = await WIKI.models.assets.query().where({
let asset = await WIKI.db.assets.query().where({
hash: fileHash,
folderId: opts.folderId
}).first()
@ -124,8 +124,8 @@ module.exports = class Asset extends Model {
if (opts.mode === 'upload') {
assetRow.authorId = opts.user.id
}
await WIKI.models.assets.query().patch(assetRow).findById(asset.id)
await WIKI.models.knex('assetData').where({
await WIKI.db.assets.query().patch(assetRow).findById(asset.id)
await WIKI.db.knex('assetData').where({
id: asset.id
}).update({
data: fileBuffer
@ -133,8 +133,8 @@ module.exports = class Asset extends Model {
} else {
// Create asset entry
assetRow.authorId = opts.user.id
asset = await WIKI.models.assets.query().insert(assetRow)
await WIKI.models.knex('assetData').insert({
asset = await WIKI.db.assets.query().insert(assetRow)
await WIKI.db.knex('assetData').insert({
id: asset.id,
data: fileBuffer
})
@ -149,7 +149,7 @@ module.exports = class Asset extends Model {
// Add to Storage
if (!opts.skipStorage) {
await WIKI.models.storage.assetEvent({
await WIKI.db.storage.assetEvent({
event: 'uploaded',
asset: {
...asset,
@ -177,13 +177,13 @@ module.exports = class Asset extends Model {
res.set('Content-disposition', 'attachment; filename=' + encodeURIComponent(fileInfo.base))
}
if (await WIKI.models.assets.getAssetFromCache(assetPath, cachePath, res)) {
if (await WIKI.db.assets.getAssetFromCache(assetPath, cachePath, res)) {
return
}
if (await WIKI.models.assets.getAssetFromStorage(assetPath, res)) {
if (await WIKI.db.assets.getAssetFromStorage(assetPath, res)) {
return
}
await WIKI.models.assets.getAssetFromDb(assetPath, fileHash, cachePath, res)
await WIKI.db.assets.getAssetFromDb(assetPath, fileHash, cachePath, res)
} catch (err) {
if (err.code === `ECONNABORTED` || err.code === `EPIPE`) {
return
@ -206,13 +206,13 @@ module.exports = class Asset extends Model {
}
static async getAssetFromStorage(assetPath, res) {
const localLocations = await WIKI.models.storage.getLocalLocations({
const localLocations = await WIKI.db.storage.getLocalLocations({
asset: {
path: assetPath
}
})
for (let location of _.filter(localLocations, location => Boolean(location.path))) {
const assetExists = await WIKI.models.assets.getAssetFromCache(assetPath, location.path, res)
const assetExists = await WIKI.db.assets.getAssetFromCache(assetPath, location.path, res)
if (assetExists) {
return true
}
@ -221,9 +221,9 @@ module.exports = class Asset extends Model {
}
static async getAssetFromDb(assetPath, fileHash, cachePath, res) {
const asset = await WIKI.models.assets.query().where('hash', fileHash).first()
const asset = await WIKI.db.assets.query().where('hash', fileHash).first()
if (asset) {
const assetData = await WIKI.models.knex('assetData').where('id', asset.id).first()
const assetData = await WIKI.db.knex('assetData').where('id', asset.id).first()
res.type(asset.ext)
res.send(assetData.data)
await fs.outputFile(cachePath, assetData.data)

@ -5,8 +5,6 @@ const _ = require('lodash')
const yaml = require('js-yaml')
const commonHelper = require('../helpers/common')
/* global WIKI */
/**
* Authentication model
*/
@ -32,11 +30,11 @@ module.exports = class Authentication extends Model {
}
static async getStrategy(key) {
return WIKI.models.authentication.query().findOne({ key })
return WIKI.db.authentication.query().findOne({ key })
}
static async getStrategies({ enabledOnly = false } = {}) {
const strategies = await WIKI.models.authentication.query().where(enabledOnly ? { isEnabled: true } : {})
const strategies = await WIKI.db.authentication.query().where(enabledOnly ? { isEnabled: true } : {})
return strategies.map(str => ({
...str,
domainWhitelist: _.get(str.domainWhitelist, 'v', []),

@ -5,8 +5,6 @@ const _ = require('lodash')
const yaml = require('js-yaml')
const commonHelper = require('../helpers/common')
/* global WIKI */
/**
* CommentProvider model
*/
@ -31,11 +29,11 @@ module.exports = class CommentProvider extends Model {
}
static async getProvider(key) {
return WIKI.models.commentProviders.query().findOne({ key })
return WIKI.db.commentProviders.query().findOne({ key })
}
static async getProviders(isEnabled) {
const providers = await WIKI.models.commentProviders.query().where(_.isBoolean(isEnabled) ? { isEnabled } : {})
const providers = await WIKI.db.commentProviders.query().where(_.isBoolean(isEnabled) ? { isEnabled } : {})
return _.sortBy(providers, ['module'])
}
@ -61,7 +59,7 @@ module.exports = class CommentProvider extends Model {
}
static async initProvider() {
const commentProvider = await WIKI.models.commentProviders.query().findOne('isEnabled', true)
const commentProvider = await WIKI.db.commentProviders.query().findOne('isEnabled', true)
if (commentProvider) {
WIKI.data.commentProvider = {
..._.find(WIKI.data.commentProviders, ['key', commentProvider.module]),

@ -2,8 +2,6 @@ const Model = require('objection').Model
const validate = require('validate.js')
const _ = require('lodash')
/* global WIKI */
/**
* Comments model
*/
@ -95,7 +93,7 @@ module.exports = class Comment extends Model {
}
// -> Load Page
const page = await WIKI.models.pages.getPageFromDb(pageId)
const page = await WIKI.db.pages.getPageFromDb(pageId)
if (page) {
if (!WIKI.auth.checkAccess(user, ['write:comments'], {
path: page.path,
@ -132,7 +130,7 @@ module.exports = class Comment extends Model {
if (!pageId) {
throw new WIKI.Error.CommentNotFound()
}
const page = await WIKI.models.pages.getPageFromDb(pageId)
const page = await WIKI.db.pages.getPageFromDb(pageId)
if (page) {
if (!WIKI.auth.checkAccess(user, ['manage:comments'], {
path: page.path,
@ -165,7 +163,7 @@ module.exports = class Comment extends Model {
if (!pageId) {
throw new WIKI.Error.CommentNotFound()
}
const page = await WIKI.models.pages.getPageFromDb(pageId)
const page = await WIKI.db.pages.getPageFromDb(pageId)
if (page) {
if (!WIKI.auth.checkAccess(user, ['manage:comments'], {
path: page.path,

@ -1,7 +1,5 @@
const Model = require('objection').Model
/* global WIKI */
/**
* Editor model
*/
@ -26,7 +24,7 @@ module.exports = class Editor extends Model {
}
static async getEditors() {
return WIKI.models.editors.query()
return WIKI.db.editors.query()
}
static async getDefaultEditor(contentType) {

@ -1,7 +1,5 @@
const Model = require('objection').Model
/* global WIKI */
/**
* Hook model
*/
@ -17,7 +15,7 @@ module.exports = class Hook extends Model {
}
static async createHook (data) {
return WIKI.models.hooks.query().insertAndFetch({
return WIKI.db.hooks.query().insertAndFetch({
name: data.name,
events: data.events,
url: data.url,
@ -31,7 +29,7 @@ module.exports = class Hook extends Model {
}
static async updateHook (id, patch) {
return WIKI.models.hooks.query().findById(id).patch({
return WIKI.db.hooks.query().findById(id).patch({
...patch,
state: 'pending',
lastErrorMessage: null
@ -39,6 +37,6 @@ module.exports = class Hook extends Model {
}
static async deleteHook (id) {
return WIKI.models.hooks.query().deleteById(id)
return WIKI.db.hooks.query().deleteById(id)
}
}

@ -1,7 +1,5 @@
const Model = require('objection').Model
/* global WIKI */
/**
* Locales model
*/
@ -50,7 +48,7 @@ module.exports = class Locale extends Model {
// return navLocalesCached
// }
// }
// const navLocales = await WIKI.models.locales.query().select('code', 'nativeName AS name').whereIn('code', WIKI.config.lang.namespaces).orderBy('code')
// const navLocales = await WIKI.db.locales.query().select('code', 'nativeName AS name').whereIn('code', WIKI.config.lang.namespaces).orderBy('code')
// if (navLocales) {
// if (cache) {
// await WIKI.cache.set('nav:locales', navLocales, 300)

@ -1,8 +1,6 @@
const Model = require('objection').Model
const _ = require('lodash')
/* global WIKI */
/**
* Navigation model
*/
@ -26,10 +24,10 @@ module.exports = class Navigation extends Model {
if (cache) {
const navTreeCached = await WIKI.cache.get(`nav:sidebar:${locale}`)
if (navTreeCached) {
return bypassAuth ? navTreeCached : WIKI.models.navigation.getAuthorizedItems(navTreeCached, groups)
return bypassAuth ? navTreeCached : WIKI.db.navigation.getAuthorizedItems(navTreeCached, groups)
}
}
const navTree = await WIKI.models.navigation.query().findOne('key', `site`)
const navTree = await WIKI.db.navigation.query().findOne('key', `site`)
if (navTree) {
// Check for pre-2.3 format
if (_.has(navTree.config[0], 'kind')) {
@ -51,7 +49,7 @@ module.exports = class Navigation extends Model {
if (bypassAuth) {
return locale === 'all' ? navTree.config : WIKI.cache.get(`nav:sidebar:${locale}`)
} else {
return locale === 'all' ? WIKI.models.navigation.getAuthorizedItems(navTree.config, groups) : WIKI.models.navigation.getAuthorizedItems(WIKI.cache.get(`nav:sidebar:${locale}`), groups)
return locale === 'all' ? WIKI.db.navigation.getAuthorizedItems(navTree.config, groups) : WIKI.db.navigation.getAuthorizedItems(WIKI.cache.get(`nav:sidebar:${locale}`), groups)
}
} else {
WIKI.logger.warn('Site Navigation is missing or corrupted.')

@ -2,8 +2,6 @@ const Model = require('objection').Model
const _ = require('lodash')
const { DateTime, Duration } = require('luxon')
/* global WIKI */
/**
* Page History model
*/
@ -89,7 +87,7 @@ module.exports = class PageHistory extends Model {
* Create Page Version
*/
static async addVersion(opts) {
await WIKI.models.pageHistory.query().insert({
await WIKI.db.pageHistory.query().insert({
pageId: opts.id,
authorId: opts.authorId,
content: opts.content,
@ -113,7 +111,7 @@ module.exports = class PageHistory extends Model {
* Get Page Version
*/
static async getVersion({ pageId, versionId }) {
const version = await WIKI.models.pageHistory.query()
const version = await WIKI.db.pageHistory.query()
.column([
'pageHistory.path',
'pageHistory.title',
@ -156,7 +154,7 @@ module.exports = class PageHistory extends Model {
* Get History Trail of a Page
*/
static async getHistory({ pageId, offsetPage = 0, offsetSize = 100 }) {
const history = await WIKI.models.pageHistory.query()
const history = await WIKI.db.pageHistory.query()
.column([
'pageHistory.id',
'pageHistory.path',
@ -178,7 +176,7 @@ module.exports = class PageHistory extends Model {
const upperLimit = (offsetPage + 1) * offsetSize
if (history.total >= upperLimit) {
prevPh = await WIKI.models.pageHistory.query()
prevPh = await WIKI.db.pageHistory.query()
.column([
'pageHistory.id',
'pageHistory.path',
@ -238,6 +236,6 @@ module.exports = class PageHistory extends Model {
static async purge (olderThan) {
const dur = Duration.fromISO(olderThan)
const olderThanISO = DateTime.utc().minus(dur)
await WIKI.models.pageHistory.query().where('versionDate', '<', olderThanISO.toISO()).del()
await WIKI.db.pageHistory.query().where('versionDate', '<', olderThanISO.toISO()).del()
}
}

@ -13,8 +13,6 @@ const TurndownService = require('turndown')
const turndownPluginGfm = require('@joplin/turndown-plugin-gfm').gfm
const cheerio = require('cheerio')
/* global WIKI */
const frontmatterRegex = {
html: /^(<!-{2}(?:\n|\r)([\w\W]+?)(?:\n|\r)-{2}>)?(?:\n|\r)*([\w\W]*)*/,
legacy: /^(<!-- TITLE: ?([\w\W]+?) ?-{2}>)?(?:\n|\r)?(<!-- SUBTITLE: ?([\w\W]+?) ?-{2}>)?(?:\n|\r)*([\w\W]*)*/i,
@ -120,7 +118,7 @@ module.exports = class Page extends Model {
*/
static async beforeDelete({ asFindQuery }) {
const page = await asFindQuery().select('id')
await WIKI.models.comments.query().delete().where('pageId', page[0].id)
await WIKI.db.comments.query().delete().where('pageId', page[0].id)
}
/**
* Cache Schema
@ -256,7 +254,7 @@ module.exports = class Page extends Model {
}
// -> Check for duplicate
const dupCheck = await WIKI.models.pages.query().select('id').where('localeCode', opts.locale).where('path', opts.path).first()
const dupCheck = await WIKI.db.pages.query().select('id').where('localeCode', opts.locale).where('path', opts.path).first()
if (dupCheck) {
throw new WIKI.Error.PageDuplicateCreate()
}
@ -289,7 +287,7 @@ module.exports = class Page extends Model {
}
// -> Create page
await WIKI.models.pages.query().insert({
await WIKI.db.pages.query().insert({
authorId: opts.user.id,
content: opts.content,
creatorId: opts.user.id,
@ -310,7 +308,7 @@ module.exports = class Page extends Model {
css: scriptCss
})
})
const page = await WIKI.models.pages.getPageFromDb({
const page = await WIKI.db.pages.getPageFromDb({
path: opts.path,
locale: opts.locale,
userId: opts.user.id
@ -318,37 +316,37 @@ module.exports = class Page extends Model {
// -> Save Tags
if (opts.tags && opts.tags.length > 0) {
await WIKI.models.tags.associateTags({ tags: opts.tags, page })
await WIKI.db.tags.associateTags({ tags: opts.tags, page })
}
// -> Render page to HTML
await WIKI.models.pages.renderPage(page)
await WIKI.db.pages.renderPage(page)
// -> Rebuild page tree
await WIKI.models.pages.rebuildTree()
await WIKI.db.pages.rebuildTree()
// -> Add to Search Index
const pageContents = await WIKI.models.pages.query().findById(page.id).select('render')
page.safeContent = WIKI.models.pages.cleanHTML(pageContents.render)
const pageContents = await WIKI.db.pages.query().findById(page.id).select('render')
page.safeContent = WIKI.db.pages.cleanHTML(pageContents.render)
await WIKI.data.searchEngine.created(page)
// -> Add to Storage
if (!opts.skipStorage) {
await WIKI.models.storage.pageEvent({
await WIKI.db.storage.pageEvent({
event: 'created',
page
})
}
// -> Reconnect Links
await WIKI.models.pages.reconnectLinks({
await WIKI.db.pages.reconnectLinks({
locale: page.localeCode,
path: page.path,
mode: 'create'
})
// -> Get latest updatedAt
page.updatedAt = await WIKI.models.pages.query().findById(page.id).select('updatedAt').then(r => r.updatedAt)
page.updatedAt = await WIKI.db.pages.query().findById(page.id).select('updatedAt').then(r => r.updatedAt)
return page
}
@ -361,7 +359,7 @@ module.exports = class Page extends Model {
*/
static async updatePage(opts) {
// -> Fetch original page
const ogPage = await WIKI.models.pages.query().findById(opts.id)
const ogPage = await WIKI.db.pages.query().findById(opts.id)
if (!ogPage) {
throw new Error('Invalid Page Id')
}
@ -380,7 +378,7 @@ module.exports = class Page extends Model {
}
// -> Create version snapshot
await WIKI.models.pageHistory.addVersion({
await WIKI.db.pageHistory.addVersion({
...ogPage,
action: opts.action ? opts.action : 'updated',
versionDate: ogPage.updatedAt
@ -414,7 +412,7 @@ module.exports = class Page extends Model {
}
// -> Update page
await WIKI.models.pages.query().patch({
await WIKI.db.pages.query().patch({
authorId: opts.user.id,
content: opts.content,
description: opts.description,
@ -428,23 +426,23 @@ module.exports = class Page extends Model {
css: scriptCss
})
}).where('id', ogPage.id)
let page = await WIKI.models.pages.getPageFromDb(ogPage.id)
let page = await WIKI.db.pages.getPageFromDb(ogPage.id)
// -> Save Tags
await WIKI.models.tags.associateTags({ tags: opts.tags, page })
await WIKI.db.tags.associateTags({ tags: opts.tags, page })
// -> Render page to HTML
await WIKI.models.pages.renderPage(page)
await WIKI.db.pages.renderPage(page)
WIKI.events.outbound.emit('deletePageFromCache', page.hash)
// -> Update Search Index
const pageContents = await WIKI.models.pages.query().findById(page.id).select('render')
page.safeContent = WIKI.models.pages.cleanHTML(pageContents.render)
const pageContents = await WIKI.db.pages.query().findById(page.id).select('render')
page.safeContent = WIKI.db.pages.cleanHTML(pageContents.render)
await WIKI.data.searchEngine.updated(page)
// -> Update on Storage
if (!opts.skipStorage) {
await WIKI.models.storage.pageEvent({
await WIKI.db.storage.pageEvent({
event: 'updated',
page
})
@ -460,7 +458,7 @@ module.exports = class Page extends Model {
throw new WIKI.Error.PageMoveForbidden()
}
await WIKI.models.pages.movePage({
await WIKI.db.pages.movePage({
id: page.id,
destinationLocale: opts.locale,
destinationPath: opts.path,
@ -468,13 +466,13 @@ module.exports = class Page extends Model {
})
} else {
// -> Update title of page tree entry
await WIKI.models.knex.table('pageTree').where({
await WIKI.db.knex.table('pageTree').where({
pageId: page.id
}).update('title', page.title)
}
// -> Get latest updatedAt
page.updatedAt = await WIKI.models.pages.query().findById(page.id).select('updatedAt').then(r => r.updatedAt)
page.updatedAt = await WIKI.db.pages.query().findById(page.id).select('updatedAt').then(r => r.updatedAt)
return page
}
@ -487,7 +485,7 @@ module.exports = class Page extends Model {
*/
static async convertPage(opts) {
// -> Fetch original page
const ogPage = await WIKI.models.pages.query().findById(opts.id)
const ogPage = await WIKI.db.pages.query().findById(opts.id)
if (!ogPage) {
throw new Error('Invalid Page Id')
}
@ -621,7 +619,7 @@ module.exports = class Page extends Model {
// -> Create version snapshot
if (shouldConvert) {
await WIKI.models.pageHistory.addVersion({
await WIKI.db.pageHistory.addVersion({
...ogPage,
action: 'updated',
versionDate: ogPage.updatedAt
@ -629,18 +627,18 @@ module.exports = class Page extends Model {
}
// -> Update page
await WIKI.models.pages.query().patch({
await WIKI.db.pages.query().patch({
contentType: targetContentType,
editor: opts.editor,
...(convertedContent ? { content: convertedContent } : {})
}).where('id', ogPage.id)
const page = await WIKI.models.pages.getPageFromDb(ogPage.id)
const page = await WIKI.db.pages.getPageFromDb(ogPage.id)
await WIKI.models.pages.deletePageFromCache(page.hash)
await WIKI.db.pages.deletePageFromCache(page.hash)
WIKI.events.outbound.emit('deletePageFromCache', page.hash)
// -> Update on Storage
await WIKI.models.storage.pageEvent({
await WIKI.db.storage.pageEvent({
event: 'updated',
page
})
@ -655,9 +653,9 @@ module.exports = class Page extends Model {
static async movePage(opts) {
let page
if (_.has(opts, 'id')) {
page = await WIKI.models.pages.query().findById(opts.id)
page = await WIKI.db.pages.query().findById(opts.id)
} else {
page = await WIKI.models.pages.query().findOne({
page = await WIKI.db.pages.query().findOne({
path: opts.path,
localeCode: opts.locale
})
@ -697,7 +695,7 @@ module.exports = class Page extends Model {
}
// -> Check for existing page at destination path
const destPage = await WIKI.models.pages.query().findOne({
const destPage = await WIKI.db.pages.query().findOne({
path: opts.destinationPath,
localeCode: opts.destinationLocale
})
@ -706,7 +704,7 @@ module.exports = class Page extends Model {
}
// -> Create version snapshot
await WIKI.models.pageHistory.addVersion({
await WIKI.db.pageHistory.addVersion({
...page,
action: 'moved',
versionDate: page.updatedAt
@ -716,21 +714,21 @@ module.exports = class Page extends Model {
// -> Move page
const destinationTitle = (page.title === page.path ? opts.destinationPath : page.title)
await WIKI.models.pages.query().patch({
await WIKI.db.pages.query().patch({
path: opts.destinationPath,
localeCode: opts.destinationLocale,
title: destinationTitle,
hash: destinationHash
}).findById(page.id)
await WIKI.models.pages.deletePageFromCache(page.hash)
await WIKI.db.pages.deletePageFromCache(page.hash)
WIKI.events.outbound.emit('deletePageFromCache', page.hash)
// -> Rebuild page tree
await WIKI.models.pages.rebuildTree()
await WIKI.db.pages.rebuildTree()
// -> Rename in Search Index
const pageContents = await WIKI.models.pages.query().findById(page.id).select('render')
page.safeContent = WIKI.models.pages.cleanHTML(pageContents.render)
const pageContents = await WIKI.db.pages.query().findById(page.id).select('render')
page.safeContent = WIKI.db.pages.cleanHTML(pageContents.render)
await WIKI.data.searchEngine.renamed({
...page,
destinationPath: opts.destinationPath,
@ -740,7 +738,7 @@ module.exports = class Page extends Model {
// -> Rename in Storage
if (!opts.skipStorage) {
await WIKI.models.storage.pageEvent({
await WIKI.db.storage.pageEvent({
event: 'renamed',
page: {
...page,
@ -755,7 +753,7 @@ module.exports = class Page extends Model {
}
// -> Reconnect Links : Changing old links to the new path
await WIKI.models.pages.reconnectLinks({
await WIKI.db.pages.reconnectLinks({
sourceLocale: page.localeCode,
sourcePath: page.path,
locale: opts.destinationLocale,
@ -764,7 +762,7 @@ module.exports = class Page extends Model {
})
// -> Reconnect Links : Validate invalid links to the new path
await WIKI.models.pages.reconnectLinks({
await WIKI.db.pages.reconnectLinks({
locale: opts.destinationLocale,
path: opts.destinationPath,
mode: 'create'
@ -778,7 +776,7 @@ module.exports = class Page extends Model {
* @returns {Promise} Promise with no value
*/
static async deletePage(opts) {
const page = await WIKI.models.pages.getPageFromDb(_.has(opts, 'id') ? opts.id : opts)
const page = await WIKI.db.pages.getPageFromDb(_.has(opts, 'id') ? opts.id : opts)
if (!page) {
throw new WIKI.Error.PageNotFound()
}
@ -792,33 +790,33 @@ module.exports = class Page extends Model {
}
// -> Create version snapshot
await WIKI.models.pageHistory.addVersion({
await WIKI.db.pageHistory.addVersion({
...page,
action: 'deleted',
versionDate: page.updatedAt
})
// -> Delete page
await WIKI.models.pages.query().delete().where('id', page.id)
await WIKI.models.pages.deletePageFromCache(page.hash)
await WIKI.db.pages.query().delete().where('id', page.id)
await WIKI.db.pages.deletePageFromCache(page.hash)
WIKI.events.outbound.emit('deletePageFromCache', page.hash)
// -> Rebuild page tree
await WIKI.models.pages.rebuildTree()
await WIKI.db.pages.rebuildTree()
// -> Delete from Search Index
await WIKI.data.searchEngine.deleted(page)
// -> Delete from Storage
if (!opts.skipStorage) {
await WIKI.models.storage.pageEvent({
await WIKI.db.storage.pageEvent({
event: 'deleted',
page
})
}
// -> Reconnect Links
await WIKI.models.pages.reconnectLinks({
await WIKI.db.pages.reconnectLinks({
locale: page.localeCode,
path: page.path,
mode: 'delete'
@ -863,10 +861,10 @@ module.exports = class Page extends Model {
let affectedHashes = []
// -> Perform replace and return affected page hashes (POSTGRES only)
if (WIKI.config.db.type === 'postgres') {
const qryHashes = await WIKI.models.pages.query()
const qryHashes = await WIKI.db.pages.query()
.returning('hash')
.patch({
render: WIKI.models.knex.raw('REPLACE(??, ?, ?)', ['render', replaceArgs.from, replaceArgs.to])
render: WIKI.db.knex.raw('REPLACE(??, ?, ?)', ['render', replaceArgs.from, replaceArgs.to])
})
.whereIn('pages.id', function () {
this.select('pageLinks.pageId').from('pageLinks').where({
@ -877,9 +875,9 @@ module.exports = class Page extends Model {
affectedHashes = qryHashes.map(h => h.hash)
} else {
// -> Perform replace, then query affected page hashes (MYSQL, MARIADB, MSSQL, SQLITE only)
await WIKI.models.pages.query()
await WIKI.db.pages.query()
.patch({
render: WIKI.models.knex.raw('REPLACE(??, ?, ?)', ['render', replaceArgs.from, replaceArgs.to])
render: WIKI.db.knex.raw('REPLACE(??, ?, ?)', ['render', replaceArgs.from, replaceArgs.to])
})
.whereIn('pages.id', function () {
this.select('pageLinks.pageId').from('pageLinks').where({
@ -887,7 +885,7 @@ module.exports = class Page extends Model {
'pageLinks.localeCode': opts.locale
})
})
const qryHashes = await WIKI.models.pages.query()
const qryHashes = await WIKI.db.pages.query()
.column('hash')
.whereIn('pages.id', function () {
this.select('pageLinks.pageId').from('pageLinks').where({
@ -898,7 +896,7 @@ module.exports = class Page extends Model {
affectedHashes = qryHashes.map(h => h.hash)
}
for (const hash of affectedHashes) {
await WIKI.models.pages.deletePageFromCache(hash)
await WIKI.db.pages.deletePageFromCache(hash)
WIKI.events.outbound.emit('deletePageFromCache', hash)
}
}
@ -940,14 +938,14 @@ module.exports = class Page extends Model {
*/
static async getPage(opts) {
// -> Get from cache first
let page = await WIKI.models.pages.getPageFromCache(opts)
let page = await WIKI.db.pages.getPageFromCache(opts)
if (!page) {
// -> Get from DB
page = await WIKI.models.pages.getPageFromDb(opts)
page = await WIKI.db.pages.getPageFromDb(opts)
if (page) {
if (page.render) {
// -> Save render to cache
await WIKI.models.pages.savePageToCache(page)
await WIKI.db.pages.savePageToCache(page)
} else {
// -> No render? Possible duplicate issue
/* TODO: Detect duplicate and delete */
@ -967,7 +965,7 @@ module.exports = class Page extends Model {
static async getPageFromDb(opts) {
const queryModeID = _.isNumber(opts)
try {
return WIKI.models.pages.query()
return WIKI.db.pages.query()
.column([
'pages.id',
'pages.path',
@ -1031,7 +1029,7 @@ module.exports = class Page extends Model {
*/
static async savePageToCache(page) {
const cachePath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${page.hash}.bin`)
await fs.outputFile(cachePath, WIKI.models.pages.cacheSchema.encode({
await fs.outputFile(cachePath, WIKI.db.pages.cacheSchema.encode({
id: page.id,
authorId: page.authorId,
authorName: page.authorName,
@ -1067,7 +1065,7 @@ module.exports = class Page extends Model {
try {
const pageBuffer = await fs.readFile(cachePath)
let page = WIKI.models.pages.cacheSchema.decode(pageBuffer)
let page = WIKI.db.pages.cacheSchema.decode(pageBuffer)
return {
...page,
path: opts.path,
@ -1108,7 +1106,7 @@ module.exports = class Page extends Model {
* @returns {Promise} Promise with no value
*/
static async migrateToLocale({ sourceLocale, targetLocale }) {
return WIKI.models.pages.query()
return WIKI.db.pages.query()
.patch({
localeCode: targetLocale
})
@ -1142,10 +1140,10 @@ module.exports = class Page extends Model {
*/
static subscribeToEvents() {
WIKI.events.inbound.on('deletePageFromCache', hash => {
WIKI.models.pages.deletePageFromCache(hash)
WIKI.db.pages.deletePageFromCache(hash)
})
WIKI.events.inbound.on('flushCache', () => {
WIKI.models.pages.flushCache()
WIKI.db.pages.flushCache()
})
}
}

@ -6,8 +6,6 @@ const yaml = require('js-yaml')
const DepGraph = require('dependency-graph').DepGraph
const commonHelper = require('../helpers/common')
/* global WIKI */
/**
* Renderer model
*/
@ -32,7 +30,7 @@ module.exports = class Renderer extends Model {
}
static async getRenderers() {
return WIKI.models.renderers.query()
return WIKI.db.renderers.query()
}
static async fetchDefinitions() {
@ -57,16 +55,16 @@ module.exports = class Renderer extends Model {
}
static async refreshRenderersFromDisk() {
// const dbRenderers = await WIKI.models.renderers.query()
// const dbRenderers = await WIKI.db.renderers.query()
// -> Fetch definitions from disk
await WIKI.models.renderers.fetchDefinitions()
await WIKI.db.renderers.fetchDefinitions()
// TODO: Merge existing configs with updated modules
}
static async getRenderingPipeline(contentType) {
const renderersDb = await WIKI.models.renderers.query().where('isEnabled', true)
const renderersDb = await WIKI.db.renderers.query().where('isEnabled', true)
if (renderersDb && renderersDb.length > 0) {
const renderers = renderersDb.map(rdr => {
const renderer = _.find(WIKI.data.renderers, ['key', rdr.key])

@ -1,8 +1,6 @@
const Model = require('objection').Model
const _ = require('lodash')
/* global WIKI */
/**
* Settings model
*/
@ -26,7 +24,7 @@ module.exports = class Setting extends Model {
}
static async getConfig() {
const settings = await WIKI.models.settings.query()
const settings = await WIKI.db.settings.query()
if (settings.length > 0) {
return _.reduce(settings, (res, val, key) => {
_.set(res, val.key, (_.has(val.value, 'v')) ? val.value.v : val.value)

@ -3,8 +3,6 @@ const crypto = require('crypto')
const pem2jwk = require('pem-jwk').pem2jwk
const _ = require('lodash')
/* global WIKI */
/**
* Site model
*/
@ -30,7 +28,7 @@ module.exports = class Site extends Model {
static async getSiteByHostname ({ hostname, forceReload = false }) {
if (forceReload) {
await WIKI.models.sites.reloadCache()
await WIKI.db.sites.reloadCache()
}
const siteId = WIKI.sitesMappings[hostname] || WIKI.sitesMappings['*']
if (siteId) {
@ -41,7 +39,7 @@ module.exports = class Site extends Model {
static async reloadCache () {
WIKI.logger.info('Reloading site configurations...')
const sites = await WIKI.models.sites.query().orderBy('id')
const sites = await WIKI.db.sites.query().orderBy('id')
WIKI.sites = _.keyBy(sites, 'id')
WIKI.sitesMappings = {}
for (const site of sites) {
@ -51,7 +49,7 @@ module.exports = class Site extends Model {
}
static async createSite (hostname, config) {
const newSite = await WIKI.models.sites.query().insertAndFetch({
const newSite = await WIKI.db.sites.query().insertAndFetch({
hostname,
isEnabled: true,
config: _.defaultsDeep(config, {
@ -114,7 +112,7 @@ module.exports = class Site extends Model {
WIKI.logger.debug(`Creating new DB storage for site ${newSite.id}`)
await WIKI.models.storage.query().insert({
await WIKI.db.storage.query().insert({
module: 'db',
siteId: newSite.id,
isEnabled: true,
@ -135,11 +133,11 @@ module.exports = class Site extends Model {
}
static async updateSite (id, patch) {
return WIKI.models.sites.query().findById(id).patch(patch)
return WIKI.db.sites.query().findById(id).patch(patch)
}
static async deleteSite (id) {
await WIKI.models.storage.query().delete().where('siteId', id)
return WIKI.models.sites.query().deleteById(id)
await WIKI.db.storage.query().delete().where('siteId', id)
return WIKI.db.sites.query().deleteById(id)
}
}

@ -5,8 +5,6 @@ const _ = require('lodash')
const yaml = require('js-yaml')
const commonHelper = require('../helpers/common')
/* global WIKI */
/**
* Storage model
*/
@ -31,7 +29,7 @@ module.exports = class Storage extends Model {
}
static async getTargets ({ siteId }) {
return WIKI.models.storage.query().where(builder => {
return WIKI.db.storage.query().where(builder => {
if (siteId) {
builder.where('siteId', siteId)
}
@ -85,7 +83,7 @@ module.exports = class Storage extends Model {
* Initialize active storage targets
*/
static async initTargets () {
const dbTargets = await WIKI.models.storage.query().where('isEnabled', true)
const dbTargets = await WIKI.db.storage.query().where('isEnabled', true)
const activeModules = _.uniq(dbTargets.map(t => t.module))
try {
// -> Stop and delete existing jobs
@ -109,7 +107,7 @@ module.exports = class Storage extends Model {
// await target.fn.init()
// // -> Save succeeded init state
// await WIKI.models.storage.query().patch({
// await WIKI.db.storage.query().patch({
// state: {
// status: 'operational',
// message: '',
@ -138,7 +136,7 @@ module.exports = class Storage extends Model {
// }
// } catch (err) {
// // -> Save initialization error
// await WIKI.models.storage.query().patch({
// await WIKI.db.storage.query().patch({
// state: {
// status: 'error',
// message: err.message,

@ -1,8 +1,6 @@
const Model = require('objection').Model
const _ = require('lodash')
/* global WIKI */
/**
* Tags model
*/
@ -50,7 +48,7 @@ module.exports = class Tag extends Model {
}
static async associateTags ({ tags, page }) {
let existingTags = await WIKI.models.tags.query().column('id', 'tag')
let existingTags = await WIKI.db.tags.query().column('id', 'tag')
// Format tags
@ -61,11 +59,11 @@ module.exports = class Tag extends Model {
const newTags = _.filter(tags, t => !_.some(existingTags, ['tag', t])).map(t => ({ tag: t }))
if (newTags.length > 0) {
if (WIKI.config.db.type === 'postgres') {
const createdTags = await WIKI.models.tags.query().insert(newTags)
const createdTags = await WIKI.db.tags.query().insert(newTags)
existingTags = _.concat(existingTags, createdTags)
} else {
for (const newTag of newTags) {
const createdTag = await WIKI.models.tags.query().insert(newTag)
const createdTag = await WIKI.db.tags.query().insert(newTag)
existingTags.push(createdTag)
}
}

@ -46,7 +46,7 @@ module.exports = class UserKey extends Model {
static async generateToken ({ userId, kind, meta }, context) {
const token = await nanoid()
await WIKI.models.userKeys.query().insert({
await WIKI.db.userKeys.query().insert({
kind,
token,
meta,
@ -57,10 +57,10 @@ module.exports = class UserKey extends Model {
}
static async validateToken ({ kind, token, skipDelete }, context) {
const res = await WIKI.models.userKeys.query().findOne({ kind, token }).withGraphJoined('user')
const res = await WIKI.db.userKeys.query().findOne({ kind, token }).withGraphJoined('user')
if (res) {
if (skipDelete !== true) {
await WIKI.models.userKeys.query().deleteById(res.id)
await WIKI.db.userKeys.query().deleteById(res.id)
}
if (DateTime.utc() > DateTime.fromISO(res.validUntil)) {
throw new WIKI.Error.AuthValidationTokenInvalid()
@ -75,6 +75,6 @@ module.exports = class UserKey extends Model {
}
static async destroyToken ({ token }) {
return WIKI.models.userKeys.query().findOne({ token }).delete()
return WIKI.db.userKeys.query().findOne({ token }).delete()
}
}

@ -90,7 +90,7 @@ module.exports = class User extends Model {
name: WIKI.config.title,
account: this.email
})
await WIKI.models.users.query().findById(this.id).patch({
await WIKI.db.users.query().findById(this.id).patch({
tfaIsActive: false,
tfaSecret: tfaInfo.secret
})
@ -99,7 +99,7 @@ module.exports = class User extends Model {
}
async enableTFA() {
return WIKI.models.users.query().findById(this.id).patch({
return WIKI.db.users.query().findById(this.id).patch({
tfaIsActive: true
})
}
@ -129,7 +129,7 @@ module.exports = class User extends Model {
// ------------------------------------------------
static async getById(id) {
return WIKI.models.users.query().findById(id).withGraphFetched('groups').modifyGraph('groups', builder => {
return WIKI.db.users.query().findById(id).withGraphFetched('groups').modifyGraph('groups', builder => {
builder.select('groups.id', 'permissions')
})
}
@ -139,7 +139,7 @@ module.exports = class User extends Model {
provider.info = _.find(WIKI.data.authentication, ['key', provider.stategyKey])
// Find existing user
let user = await WIKI.models.users.query().findOne({
let user = await WIKI.db.users.query().findOne({
providerId: _.toString(profile.id),
providerKey
})
@ -164,7 +164,7 @@ module.exports = class User extends Model {
// Find pending social user
if (!user) {
user = await WIKI.models.users.query().findOne({
user = await WIKI.db.users.query().findOne({
email: primaryEmail,
providerId: null,
providerKey
@ -213,7 +213,7 @@ module.exports = class User extends Model {
})
if (pictureUrl === 'internal') {
await WIKI.models.users.updateUserAvatarData(user.id, profile.picture)
await WIKI.db.users.updateUserAvatarData(user.id, profile.picture)
}
return user
@ -230,7 +230,7 @@ module.exports = class User extends Model {
}
// Create account
user = await WIKI.models.users.query().insertAndFetch({
user = await WIKI.db.users.query().insertAndFetch({
providerKey: providerKey,
providerId: _.toString(profile.id),
email: primaryEmail,
@ -250,7 +250,7 @@ module.exports = class User extends Model {
}
if (pictureUrl === 'internal') {
await WIKI.models.users.updateUserAvatarData(user.id, profile.picture)
await WIKI.db.users.updateUserAvatarData(user.id, profile.picture)
}
return user
@ -288,7 +288,7 @@ module.exports = class User extends Model {
if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) }
try {
const resp = await WIKI.models.users.afterLoginChecks(user, selStrategy.id, context, {
const resp = await WIKI.db.users.afterLoginChecks(user, selStrategy.id, context, {
skipTFA: !strInfo.useForm,
skipChangePwd: !strInfo.useForm
})
@ -326,7 +326,7 @@ module.exports = class User extends Model {
if (!skipTFA) {
if (authStr.tfaRequired && authStr.tfaSecret) {
try {
const tfaToken = await WIKI.models.userKeys.generateToken({
const tfaToken = await WIKI.db.userKeys.generateToken({
kind: 'tfa',
userId: user.id
})
@ -342,7 +342,7 @@ module.exports = class User extends Model {
} else if (WIKI.config.auth.enforce2FA || (authStr.tfaIsActive && !authStr.tfaSecret)) {
try {
const tfaQRImage = await user.generateTFA()
const tfaToken = await WIKI.models.userKeys.generateToken({
const tfaToken = await WIKI.db.userKeys.generateToken({
kind: 'tfaSetup',
userId: user.id
})
@ -362,7 +362,7 @@ module.exports = class User extends Model {
// Must Change Password?
if (!skipChangePwd && authStr.mustChangePwd) {
try {
const pwdChangeToken = await WIKI.models.userKeys.generateToken({
const pwdChangeToken = await WIKI.db.userKeys.generateToken({
kind: 'changePwd',
userId: user.id
})
@ -381,7 +381,7 @@ module.exports = class User extends Model {
return new Promise((resolve, reject) => {
context.req.login(user, { session: false }, async errc => {
if (errc) { return reject(errc) }
const jwtToken = await WIKI.models.users.refreshToken(user, strategyId)
const jwtToken = await WIKI.db.users.refreshToken(user, strategyId)
resolve({ jwt: jwtToken.token, redirect })
})
})
@ -392,7 +392,7 @@ module.exports = class User extends Model {
*/
static async refreshToken(user, provider) {
if (_.isString(user)) {
user = await WIKI.models.users.query().findById(user).withGraphFetched('groups').modifyGraph('groups', builder => {
user = await WIKI.db.users.query().findById(user).withGraphFetched('groups').modifyGraph('groups', builder => {
builder.select('groups.id', 'permissions')
})
if (!user) {
@ -409,7 +409,7 @@ module.exports = class User extends Model {
// Update Last Login Date
// -> Bypass Objection.js to avoid updating the updatedAt field
await WIKI.models.knex('users').where('id', user.id).update({ lastLoginAt: new Date().toISOString() })
await WIKI.db.knex('users').where('id', user.id).update({ lastLoginAt: new Date().toISOString() })
return {
token: jwt.sign({
@ -435,7 +435,7 @@ module.exports = class User extends Model {
*/
static async loginTFA ({ securityCode, continuationToken, setup }, context) {
if (securityCode.length === 6 && continuationToken.length > 1) {
const user = await WIKI.models.userKeys.validateToken({
const user = await WIKI.db.userKeys.validateToken({
kind: setup ? 'tfaSetup' : 'tfa',
token: continuationToken,
skipDelete: setup
@ -445,7 +445,7 @@ module.exports = class User extends Model {
if (setup) {
await user.enableTFA()
}
return WIKI.models.users.afterLoginChecks(user, context, { skipTFA: true })
return WIKI.db.users.afterLoginChecks(user, context, { skipTFA: true })
} else {
throw new WIKI.Error.AuthTFAFailed()
}
@ -461,13 +461,13 @@ module.exports = class User extends Model {
if (!newPassword || newPassword.length < 6) {
throw new WIKI.Error.InputInvalid('Password must be at least 6 characters!')
}
const usr = await WIKI.models.userKeys.validateToken({
const usr = await WIKI.db.userKeys.validateToken({
kind: 'changePwd',
token: continuationToken
})
if (usr) {
await WIKI.models.users.query().patch({
await WIKI.db.users.query().patch({
password: newPassword,
mustChangePwd: false
}).findById(usr.id)
@ -475,7 +475,7 @@ module.exports = class User extends Model {
return new Promise((resolve, reject) => {
context.req.logIn(usr, { session: false }, async err => {
if (err) { return reject(err) }
const jwtToken = await WIKI.models.users.refreshToken(usr)
const jwtToken = await WIKI.db.users.refreshToken(usr)
resolve({ jwt: jwtToken.token })
})
})
@ -488,7 +488,7 @@ module.exports = class User extends Model {
* Send a password reset request
*/
static async loginForgotPassword ({ email }, context) {
const usr = await WIKI.models.users.query().where({
const usr = await WIKI.db.users.query().where({
email,
providerKey: 'local'
}).first()
@ -496,7 +496,7 @@ module.exports = class User extends Model {
WIKI.logger.debug(`Password reset attempt on nonexistant local account ${email}: [DISCARDED]`)
return
}
const resetToken = await WIKI.models.userKeys.generateToken({
const resetToken = await WIKI.db.userKeys.generateToken({
userId: usr.id,
kind: 'resetPwd'
})
@ -585,7 +585,7 @@ module.exports = class User extends Model {
}
// Check if email already exists
const usr = await WIKI.models.users.query().findOne({ email, providerKey })
const usr = await WIKI.db.users.query().findOne({ email, providerKey })
if (!usr) {
// Create the account
let newUsrData = {
@ -606,7 +606,7 @@ module.exports = class User extends Model {
newUsrData.mustChangePwd = (mustChangePassword === true)
}
const newUsr = await WIKI.models.users.query().insert(newUsrData)
const newUsr = await WIKI.db.users.query().insert(newUsrData)
// Assign to group(s)
if (groups.length > 0) {
@ -640,11 +640,11 @@ module.exports = class User extends Model {
* @param {Object} param0 User ID and fields to update
*/
static async updateUser ({ id, email, name, newPassword, groups, location, jobTitle, timezone, dateFormat, appearance }) {
const usr = await WIKI.models.users.query().findById(id)
const usr = await WIKI.db.users.query().findById(id)
if (usr) {
let usrData = {}
if (!_.isEmpty(email) && email !== usr.email) {
const dupUsr = await WIKI.models.users.query().select('id').where({
const dupUsr = await WIKI.db.users.query().select('id').where({
email,
providerKey: usr.providerKey
}).first()
@ -691,7 +691,7 @@ module.exports = class User extends Model {
if (!_.isNil(appearance) && appearance !== usr.appearance) {
usrData.appearance = appearance
}
await WIKI.models.users.query().patch(usrData).findById(id)
await WIKI.db.users.query().patch(usrData).findById(id)
} else {
throw new WIKI.Error.UserNotFound()
}
@ -703,16 +703,16 @@ module.exports = class User extends Model {
* @param {*} id User ID
*/
static async deleteUser (id, replaceId) {
const usr = await WIKI.models.users.query().findById(id)
const usr = await WIKI.db.users.query().findById(id)
if (usr) {
await WIKI.models.assets.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.models.comments.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.models.pageHistory.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.models.pages.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.models.pages.query().patch({ creatorId: replaceId }).where('creatorId', id)
await WIKI.models.userKeys.query().delete().where('userId', id)
await WIKI.models.users.query().deleteById(id)
await WIKI.db.assets.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.db.comments.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.db.pageHistory.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.db.pages.query().patch({ authorId: replaceId }).where('authorId', id)
await WIKI.db.pages.query().patch({ creatorId: replaceId }).where('creatorId', id)
await WIKI.db.userKeys.query().delete().where('userId', id)
await WIKI.db.users.query().deleteById(id)
} else {
throw new WIKI.Error.UserNotFound()
}
@ -725,7 +725,7 @@ module.exports = class User extends Model {
* @param {Object} context GraphQL Context
*/
static async register ({ email, password, name, verify = false, bypassChecks = false }, context) {
const localStrg = await WIKI.models.authentication.getStrategy('local')
const localStrg = await WIKI.db.authentication.getStrategy('local')
// Check if self-registration is enabled
if (localStrg.selfRegistration || bypassChecks) {
// Input sanitization
@ -773,10 +773,10 @@ module.exports = class User extends Model {
}
}
// Check if email already exists
const usr = await WIKI.models.users.query().findOne({ email, providerKey: 'local' })
const usr = await WIKI.db.users.query().findOne({ email, providerKey: 'local' })
if (!usr) {
// Create the account
const newUsr = await WIKI.models.users.query().insert({
const newUsr = await WIKI.db.users.query().insert({
provider: 'local',
email,
name,
@ -796,7 +796,7 @@ module.exports = class User extends Model {
if (verify) {
// Create verification token
const verificationToken = await WIKI.models.userKeys.generateToken({
const verificationToken = await WIKI.db.userKeys.generateToken({
kind: 'verify',
userId: newUsr.id
})
@ -846,7 +846,7 @@ module.exports = class User extends Model {
}
static async getGuestUser () {
const user = await WIKI.models.users.query().findById(WIKI.config.auth.guestUserId).withGraphJoined('groups').modifyGraph('groups', builder => {
const user = await WIKI.db.users.query().findById(WIKI.config.auth.guestUserId).withGraphJoined('groups').modifyGraph('groups', builder => {
builder.select('groups.id', 'permissions')
})
if (!user) {
@ -858,7 +858,7 @@ module.exports = class User extends Model {
}
static async getRootUser () {
let user = await WIKI.models.users.query().findById(WIKI.config.auth.rootAdminUserId)
let user = await WIKI.db.users.query().findById(WIKI.config.auth.rootAdminUserId)
if (!user) {
WIKI.logger.error('CRITICAL ERROR: Root Administrator user is missing!')
process.exit(1)
@ -876,15 +876,15 @@ module.exports = class User extends Model {
if (data.length > 1024 * 1024) {
throw new Error('Avatar image filesize is too large. 1MB max.')
}
const existing = await WIKI.models.knex('userAvatars').select('id').where('id', userId).first()
const existing = await WIKI.db.knex('userAvatars').select('id').where('id', userId).first()
if (existing) {
await WIKI.models.knex('userAvatars').where({
await WIKI.db.knex('userAvatars').where({
id: userId
}).update({
data
})
} else {
await WIKI.models.knex('userAvatars').insert({
await WIKI.db.knex('userAvatars').insert({
id: userId,
data
})
@ -896,7 +896,7 @@ module.exports = class User extends Model {
static async getUserAvatarData (userId) {
try {
const usrData = await WIKI.models.knex('userAvatars').where('id', userId).first()
const usrData = await WIKI.db.knex('userAvatars').where('id', userId).first()
if (usrData) {
return usrData.data
} else {

@ -17,7 +17,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, extraParams, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile
})

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
// ------------------------------------
// Azure AD Account
// ------------------------------------
@ -39,7 +37,7 @@ module.exports = {
}, async (req, iss, sub, profile, cb) => {
const usrEmail = _.get(profile, '_json.email', null) || _.get(profile, '_json.preferred_username')
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
id: profile.oid,

@ -15,7 +15,7 @@ module.exports = {
passReqToCallback: true
}, async (req, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile
})

@ -22,7 +22,7 @@ module.exports = {
if (conf.guildId && !_.some(profile.guilds, { id: conf.guildId })) {
throw new WIKI.Error.AuthLoginFailed()
}
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -18,7 +18,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -19,7 +19,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -27,7 +27,7 @@ module.exports = {
passport.use(conf.key,
new GitHubStrategy(githubConfig, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -19,7 +19,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -19,7 +19,7 @@ module.exports = {
if (conf.hostedDomain && conf.hostedDomain != profile._json.hd) {
throw new Error('Google authentication should have been performed with domain ' + conf.hostedDomain)
}
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
// ------------------------------------
// Keycloak Account
// ------------------------------------
@ -27,7 +25,7 @@ module.exports = {
displayName = profile.fullName
}
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
id: profile.keycloakId,

@ -31,7 +31,7 @@ module.exports = {
throw new Error('Invalid Unique ID field mapping!')
}
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
id: userId,

@ -15,7 +15,7 @@ module.exports = {
passwordField: 'password'
}, async (uEmail, uPassword, done) => {
try {
const user = await WIKI.models.users.query().findOne({
const user = await WIKI.db.users.query().findOne({
email: uEmail.toLowerCase()
})
if (user) {

@ -18,7 +18,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
// ------------------------------------
// OAuth2 Account
// ------------------------------------
@ -21,7 +19,7 @@ module.exports = {
scope: conf.scope
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
// ------------------------------------
// OpenID Connect Account
// ------------------------------------
@ -22,7 +20,7 @@ module.exports = {
passReqToCallback: true
}, async (req, iss, sub, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -20,7 +20,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
// ------------------------------------
// Rocket.chat Account
// ------------------------------------
@ -43,7 +41,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile
})

@ -1,7 +1,5 @@
const _ = require('lodash')
/* global WIKI */
// ------------------------------------
// SAML Account
// ------------------------------------
@ -47,7 +45,7 @@ module.exports = {
throw new Error('Invalid or Missing Unique ID field!')
}
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
id: userId,

@ -19,7 +19,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, { user: userProfile }, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...userProfile,

@ -17,7 +17,7 @@ module.exports = {
passReqToCallback: true
}, async (req, accessToken, refreshToken, profile, cb) => {
try {
const user = await WIKI.models.users.processProfile({
const user = await WIKI.db.users.processProfile({
providerKey: req.params.strategy,
profile: {
...profile,

@ -6,8 +6,6 @@ const _ = require('lodash')
const { AkismetClient } = require('akismet-api')
const moment = require('moment')
/* global WIKI */
const window = new JSDOM('').window
const DOMPurify = createDOMPurify(window)
@ -108,14 +106,14 @@ module.exports = {
// -> Check for minimum delay between posts
if (WIKI.data.commentProvider.config.minDelay > 0) {
const lastComment = await WIKI.models.comments.query().select('updatedAt').findOne('authorId', user.id).orderBy('updatedAt', 'desc')
const lastComment = await WIKI.db.comments.query().select('updatedAt').findOne('authorId', user.id).orderBy('updatedAt', 'desc')
if (lastComment && moment().subtract(WIKI.data.commentProvider.config.minDelay, 'seconds').isBefore(lastComment.updatedAt)) {
throw new Error('Your administrator has set a time limit before you can post another comment. Try again later.')
}
}
// -> Save Comment to DB
const cm = await WIKI.models.comments.query().insert(newComment)
const cm = await WIKI.db.comments.query().insert(newComment)
// -> Return Comment ID
return cm.id
@ -125,7 +123,7 @@ module.exports = {
*/
async update ({ id, content, user }) {
const renderedContent = DOMPurify.sanitize(mkdown.render(content))
await WIKI.models.comments.query().findById(id).patch({
await WIKI.db.comments.query().findById(id).patch({
render: renderedContent
})
return renderedContent
@ -134,26 +132,26 @@ module.exports = {
* Delete an existing comment by ID
*/
async remove ({ id, user }) {
return WIKI.models.comments.query().findById(id).delete()
return WIKI.db.comments.query().findById(id).delete()
},
/**
* Get the page ID from a comment ID
*/
async getPageIdFromCommentId (id) {
const result = await WIKI.models.comments.query().select('pageId').findById(id)
const result = await WIKI.db.comments.query().select('pageId').findById(id)
return (result) ? result.pageId : false
},
/**
* Get a comment by ID
*/
async getCommentById (id) {
return WIKI.models.comments.query().findById(id)
return WIKI.db.comments.query().findById(id)
},
/**
* Get the total comments count for a page ID
*/
async count (pageId) {
const result = await WIKI.models.comments.query().count('* as total').where('pageId', pageId).first()
const result = await WIKI.db.comments.query().count('* as total').where('pageId', pageId).first()
return _.toSafeInteger(result.total)
}
}

@ -4,8 +4,6 @@ const util = require('util')
const exec = util.promisify(require('child_process').exec)
const fs = require('fs-extra')
/* global WIKI */
module.exports = {
key: 'puppeteer',
title: 'Puppeteer',

@ -5,8 +5,6 @@ const util = require('util')
const exec = util.promisify(require('child_process').exec)
const { pipeline } = require('stream/promises')
/* global WIKI */
module.exports = {
key: 'sharp',
title: 'Sharp',

@ -6,8 +6,6 @@ const URL = require('url').URL
const mustacheRegExp = /(\{|&#x7b;?){2}(.+?)(\}|&#x7d;?){2}/i
/* global WIKI */
module.exports = {
async render() {
const $ = cheerio.load(this.input, {
@ -133,7 +131,7 @@ module.exports = {
if (internalRefs.length > 0) {
// -> Find matching pages
const results = await WIKI.models.pages.query().column('id', 'path', 'localeCode').where(builder => {
const results = await WIKI.db.pages.query().column('id', 'path', 'localeCode').where(builder => {
internalRefs.forEach((ref, idx) => {
if (idx < 1) {
builder.where(ref)
@ -168,14 +166,14 @@ module.exports = {
})
if (missingLinks.length > 0) {
if (WIKI.config.db.type === 'postgres') {
await WIKI.models.pageLinks.query().insert(missingLinks.map(lnk => ({
await WIKI.db.pageLinks.query().insert(missingLinks.map(lnk => ({
pageId: this.page.id,
path: lnk.path,
localeCode: lnk.localeCode
})))
} else {
for (const lnk of missingLinks) {
await WIKI.models.pageLinks.query().insert({
await WIKI.db.pageLinks.query().insert({
pageId: this.page.id,
path: lnk.path,
localeCode: lnk.localeCode
@ -191,7 +189,7 @@ module.exports = {
return nLink.localeCode === pLink.localeCode && nLink.path === pLink.path
})
if (outdatedLinks.length > 0) {
await WIKI.models.pageLinks.query().delete().whereIn('id', _.map(outdatedLinks, 'id'))
await WIKI.db.pageLinks.query().delete().whereIn('id', _.map(outdatedLinks, 'id'))
}
}

@ -1,8 +1,6 @@
const katex = require('katex')
const chemParse = require('./mhchem')
/* global WIKI */
// ------------------------------------
// Markdown - KaTeX Renderer
// ------------------------------------

@ -1,7 +1,5 @@
const mjax = require('mathjax')
/* global WIKI */
// ------------------------------------
// Markdown - MathJax Renderer
// ------------------------------------

@ -1,24 +0,0 @@
key: algolia
title: Algolia
description: Algolia is a powerful search-as-a-service solution, made easy to use with API clients, UI libraries, and pre-built integrations.
author: requarks.io
logo: https://static.requarks.io/logo/algolia.svg
website: https://www.algolia.com/
isAvailable: true
props:
appId:
type: String
title: App ID
hint: Your Algolia Application ID, found under API Keys
order: 1
apiKey:
type: String
title: Admin API Key
hint: Your Algolia Admin API Key, found under API Keys.
order: 2
indexName:
type: String
title: Index Name
hint: The name of the index you created under Indices.
default: wiki
order: 3

@ -1,203 +0,0 @@
const _ = require('lodash')
const algoliasearch = require('algoliasearch')
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)
/* global WIKI */
module.exports = {
async activate() {
// not used
},
async deactivate() {
// not used
},
/**
* INIT
*/
async init() {
WIKI.logger.info(`(SEARCH/ALGOLIA) Initializing...`)
this.client = algoliasearch(this.config.appId, this.config.apiKey)
this.index = this.client.initIndex(this.config.indexName)
// -> Create Search Index
WIKI.logger.info(`(SEARCH/ALGOLIA) Setting index configuration...`)
await this.index.setSettings({
searchableAttributes: [
'title',
'description',
'content'
],
attributesToRetrieve: [
'locale',
'path',
'title',
'description'
],
advancedSyntax: true
})
WIKI.logger.info(`(SEARCH/ALGOLIA) Initialization completed.`)
},
/**
* QUERY
*
* @param {String} q Query
* @param {Object} opts Additional options
*/
async query(q, opts) {
try {
const results = await this.index.search(q, {
hitsPerPage: 50
})
return {
results: _.map(results.hits, r => ({
id: r.objectID,
locale: r.locale,
path: r.path,
title: r.title,
description: r.description
})),
suggestions: [],
totalHits: results.nbHits
}
} catch (err) {
WIKI.logger.warn('Search Engine Error:')
WIKI.logger.warn(err)
}
},
/**
* CREATE
*
* @param {Object} page Page to create
*/
async created(page) {
await this.index.saveObject({
objectID: page.hash,
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent
})
},
/**
* UPDATE
*
* @param {Object} page Page to update
*/
async updated(page) {
await this.index.partialUpdateObject({
objectID: page.hash,
title: page.title,
description: page.description,
content: page.safeContent
})
},
/**
* DELETE
*
* @param {Object} page Page to delete
*/
async deleted(page) {
await this.index.deleteObject(page.hash)
},
/**
* RENAME
*
* @param {Object} page Page to rename
*/
async renamed(page) {
await this.index.deleteObject(page.hash)
await this.index.saveObject({
objectID: page.destinationHash,
locale: page.destinationLocaleCode,
path: page.destinationPath,
title: page.title,
description: page.description,
content: page.safeContent
})
},
/**
* REBUILD INDEX
*/
async rebuild() {
WIKI.logger.info(`(SEARCH/ALGOLIA) Rebuilding Index...`)
await this.index.clearObjects()
const MAX_DOCUMENT_BYTES = 10 * Math.pow(2, 10) // 10 KB
const MAX_INDEXING_BYTES = 10 * Math.pow(2, 20) - Buffer.from('[').byteLength - Buffer.from(']').byteLength // 10 MB
const MAX_INDEXING_COUNT = 1000
const COMMA_BYTES = Buffer.from(',').byteLength
let chunks = []
let bytes = 0
const processDocument = async (cb, doc) => {
try {
if (doc) {
const docBytes = Buffer.from(JSON.stringify(doc)).byteLength
// -> Document too large
if (docBytes >= MAX_DOCUMENT_BYTES) {
throw new Error('Document exceeds maximum size allowed by Algolia.')
}
// -> Current batch exceeds size hard limit, flush
if (docBytes + COMMA_BYTES + bytes >= MAX_INDEXING_BYTES) {
await flushBuffer()
}
if (chunks.length > 0) {
bytes += COMMA_BYTES
}
bytes += docBytes
chunks.push(doc)
// -> Current batch exceeds count soft limit, flush
if (chunks.length >= MAX_INDEXING_COUNT) {
await flushBuffer()
}
} else {
// -> End of stream, flush
await flushBuffer()
}
cb()
} catch (err) {
cb(err)
}
}
const flushBuffer = async () => {
WIKI.logger.info(`(SEARCH/ALGOLIA) Sending batch of ${chunks.length}...`)
try {
await this.index.saveObjects(
_.map(chunks, doc => ({
objectID: doc.id,
locale: doc.locale,
path: doc.path,
title: doc.title,
description: doc.description,
content: WIKI.models.pages.cleanHTML(doc.render)
}))
)
} catch (err) {
WIKI.logger.warn('(SEARCH/ALGOLIA) Failed to send batch to Algolia: ', err)
}
chunks.length = 0
bytes = 0
}
await pipeline(
WIKI.models.knex.column({ id: 'hash' }, 'path', { locale: 'localeCode' }, 'title', 'description', 'render').select().from('pages').where({
isPublished: true,
isPrivate: false
}).stream(),
new stream.Transform({
objectMode: true,
transform: async (chunk, enc, cb) => processDocument(cb, chunk),
flush: async (cb) => processDocument(cb)
})
)
WIKI.logger.info(`(SEARCH/ALGOLIA) Index rebuilt successfully.`)
}
}

@ -1,88 +0,0 @@
key: aws
title: AWS CloudSearch
description: Amazon CloudSearch is a managed service in the AWS Cloud that makes it simple and cost-effective to set up, manage, and scale a search solution for your website or application.
author: requarks.io
logo: https://static.requarks.io/logo/aws-cloudsearch.svg
website: https://aws.amazon.com/cloudsearch/
isAvailable: true
props:
domain:
type: String
title: Search Domain
hint: The name of your CloudSearch service.
order: 1
endpoint:
type: String
title: Document Endpoint
hint: The Document Endpoint specified in the domain AWS console dashboard.
order: 2
region:
type: String
title: Region
hint: The AWS datacenter region where the instance was created.
default: us-east-1
enum:
- ap-northeast-1
- ap-northeast-2
- ap-southeast-1
- ap-southeast-2
- eu-central-1
- eu-west-1
- sa-east-1
- us-east-1
- us-west-1
- us-west-2
order: 3
accessKeyId:
type: String
title: Access Key ID
hint: The Access Key ID with CloudSearchFullAccess role access to the CloudSearch instance.
order: 4
secretAccessKey :
type: String
title: Secret Access Key
hint: The Secret Access Key for the Access Key ID provided above.
order: 5
AnalysisSchemeLang:
type: String
title: Analysis Scheme Language
hint: The language used to analyse content.
default: en
enum:
- 'ar'
- 'bg'
- 'ca'
- 'cs'
- 'da'
- 'de'
- 'el'
- 'en'
- 'es'
- 'eu'
- 'fa'
- 'fi'
- 'fr'
- 'ga'
- 'gl'
- 'he'
- 'hi'
- 'hu'
- 'hy'
- 'id'
- 'it'
- 'ja'
- 'ko'
- 'lv'
- 'mul'
- 'nl'
- 'no'
- 'pt'
- 'ro'
- 'ru'
- 'sv'
- 'th'
- 'tr'
- 'zh-Hans'
- 'zh-Hant'
order: 6

@ -1,370 +0,0 @@
const _ = require('lodash')
const AWS = require('aws-sdk')
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)
/* global WIKI */
module.exports = {
async activate() {
// not used
},
async deactivate() {
// not used
},
/**
* INIT
*/
async init() {
WIKI.logger.info(`(SEARCH/AWS) Initializing...`)
this.client = new AWS.CloudSearch({
apiVersion: '2013-01-01',
accessKeyId: this.config.accessKeyId,
secretAccessKey: this.config.secretAccessKey,
region: this.config.region
})
this.clientDomain = new AWS.CloudSearchDomain({
apiVersion: '2013-01-01',
endpoint: this.config.endpoint,
accessKeyId: this.config.accessKeyId,
secretAccessKey: this.config.secretAccessKey,
region: this.config.region
})
let rebuildIndex = false
// -> Define Analysis Schemes
const schemes = await this.client.describeAnalysisSchemes({
DomainName: this.config.domain,
AnalysisSchemeNames: ['default_anlscheme']
}).promise()
if (_.get(schemes, 'AnalysisSchemes', []).length < 1) {
WIKI.logger.info(`(SEARCH/AWS) Defining Analysis Scheme...`)
await this.client.defineAnalysisScheme({
DomainName: this.config.domain,
AnalysisScheme: {
AnalysisSchemeLanguage: this.config.AnalysisSchemeLang,
AnalysisSchemeName: 'default_anlscheme'
}
}).promise()
rebuildIndex = true
}
// -> Define Index Fields
const fields = await this.client.describeIndexFields({
DomainName: this.config.domain
}).promise()
if (_.get(fields, 'IndexFields', []).length < 1) {
WIKI.logger.info(`(SEARCH/AWS) Defining Index Fields...`)
await this.client.defineIndexField({
DomainName: this.config.domain,
IndexField: {
IndexFieldName: 'id',
IndexFieldType: 'literal'
}
}).promise()
await this.client.defineIndexField({
DomainName: this.config.domain,
IndexField: {
IndexFieldName: 'path',
IndexFieldType: 'literal'
}
}).promise()
await this.client.defineIndexField({
DomainName: this.config.domain,
IndexField: {
IndexFieldName: 'locale',
IndexFieldType: 'literal'
}
}).promise()
await this.client.defineIndexField({
DomainName: this.config.domain,
IndexField: {
IndexFieldName: 'title',
IndexFieldType: 'text',
TextOptions: {
ReturnEnabled: true,
AnalysisScheme: 'default_anlscheme'
}
}
}).promise()
await this.client.defineIndexField({
DomainName: this.config.domain,
IndexField: {
IndexFieldName: 'description',
IndexFieldType: 'text',
TextOptions: {
ReturnEnabled: true,
AnalysisScheme: 'default_anlscheme'
}
}
}).promise()
await this.client.defineIndexField({
DomainName: this.config.domain,
IndexField: {
IndexFieldName: 'content',
IndexFieldType: 'text',
TextOptions: {
ReturnEnabled: false,
AnalysisScheme: 'default_anlscheme'
}
}
}).promise()
rebuildIndex = true
}
// -> Define suggester
const suggesters = await this.client.describeSuggesters({
DomainName: this.config.domain,
SuggesterNames: ['default_suggester']
}).promise()
if (_.get(suggesters, 'Suggesters', []).length < 1) {
WIKI.logger.info(`(SEARCH/AWS) Defining Suggester...`)
await this.client.defineSuggester({
DomainName: this.config.domain,
Suggester: {
SuggesterName: 'default_suggester',
DocumentSuggesterOptions: {
SourceField: 'title',
FuzzyMatching: 'high'
}
}
}).promise()
rebuildIndex = true
}
// -> Rebuild Index
if (rebuildIndex) {
WIKI.logger.info(`(SEARCH/AWS) Requesting Index Rebuild...`)
await this.client.indexDocuments({
DomainName: this.config.domain
}).promise()
}
WIKI.logger.info(`(SEARCH/AWS) Initialization completed.`)
},
/**
* QUERY
*
* @param {String} q Query
* @param {Object} opts Additional options
*/
async query(q, opts) {
try {
let suggestions = []
const results = await this.clientDomain.search({
query: q,
partial: true,
size: 50
}).promise()
if (results.hits.found < 5) {
const suggestResults = await this.clientDomain.suggest({
query: q,
suggester: 'default_suggester',
size: 5
}).promise()
suggestions = suggestResults.suggest.suggestions.map(s => s.suggestion)
}
return {
results: _.map(results.hits.hit, r => ({
id: r.id,
path: _.head(r.fields.path),
locale: _.head(r.fields.locale),
title: _.head(r.fields.title) || '',
description: _.head(r.fields.description) || ''
})),
suggestions: suggestions,
totalHits: results.hits.found
}
} catch (err) {
WIKI.logger.warn('Search Engine Error:')
WIKI.logger.warn(err)
}
},
/**
* CREATE
*
* @param {Object} page Page to create
*/
async created(page) {
await this.clientDomain.uploadDocuments({
contentType: 'application/json',
documents: JSON.stringify([
{
type: 'add',
id: page.hash,
fields: {
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent
}
}
])
}).promise()
},
/**
* UPDATE
*
* @param {Object} page Page to update
*/
async updated(page) {
await this.clientDomain.uploadDocuments({
contentType: 'application/json',
documents: JSON.stringify([
{
type: 'add',
id: page.hash,
fields: {
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent
}
}
])
}).promise()
},
/**
* DELETE
*
* @param {Object} page Page to delete
*/
async deleted(page) {
await this.clientDomain.uploadDocuments({
contentType: 'application/json',
documents: JSON.stringify([
{
type: 'delete',
id: page.hash
}
])
}).promise()
},
/**
* RENAME
*
* @param {Object} page Page to rename
*/
async renamed(page) {
await this.clientDomain.uploadDocuments({
contentType: 'application/json',
documents: JSON.stringify([
{
type: 'delete',
id: page.hash
}
])
}).promise()
await this.clientDomain.uploadDocuments({
contentType: 'application/json',
documents: JSON.stringify([
{
type: 'add',
id: page.destinationHash,
fields: {
locale: page.destinationLocaleCode,
path: page.destinationPath,
title: page.title,
description: page.description,
content: page.safeContent
}
}
])
}).promise()
},
/**
* REBUILD INDEX
*/
async rebuild() {
WIKI.logger.info(`(SEARCH/AWS) Rebuilding Index...`)
const MAX_DOCUMENT_BYTES = Math.pow(2, 20)
const MAX_INDEXING_BYTES = 5 * Math.pow(2, 20) - Buffer.from('[').byteLength - Buffer.from(']').byteLength
const MAX_INDEXING_COUNT = 1000
const COMMA_BYTES = Buffer.from(',').byteLength
let chunks = []
let bytes = 0
const processDocument = async (cb, doc) => {
try {
if (doc) {
const docBytes = Buffer.from(JSON.stringify(doc)).byteLength
// -> Document too large
if (docBytes >= MAX_DOCUMENT_BYTES) {
throw new Error('Document exceeds maximum size allowed by AWS CloudSearch.')
}
// -> Current batch exceeds size hard limit, flush
if (docBytes + COMMA_BYTES + bytes >= MAX_INDEXING_BYTES) {
await flushBuffer()
}
if (chunks.length > 0) {
bytes += COMMA_BYTES
}
bytes += docBytes
chunks.push(doc)
// -> Current batch exceeds count soft limit, flush
if (chunks.length >= MAX_INDEXING_COUNT) {
await flushBuffer()
}
} else {
// -> End of stream, flush
await flushBuffer()
}
cb()
} catch (err) {
cb(err)
}
}
const flushBuffer = async () => {
WIKI.logger.info(`(SEARCH/AWS) Sending batch of ${chunks.length}...`)
try {
await this.clientDomain.uploadDocuments({
contentType: 'application/json',
documents: JSON.stringify(_.map(chunks, doc => ({
type: 'add',
id: doc.id,
fields: {
locale: doc.locale,
path: doc.path,
title: doc.title,
description: doc.description,
content: WIKI.models.pages.cleanHTML(doc.render)
}
})))
}).promise()
} catch (err) {
WIKI.logger.warn('(SEARCH/AWS) Failed to send batch to AWS CloudSearch: ', err)
}
chunks.length = 0
bytes = 0
}
await pipeline(
WIKI.models.knex.column({ id: 'hash' }, 'path', { locale: 'localeCode' }, 'title', 'description', 'render').select().from('pages').where({
isPublished: true,
isPrivate: false
}).stream(),
new stream.Transform({
objectMode: true,
transform: async (chunk, enc, cb) => processDocument(cb, chunk),
flush: async (cb) => processDocument(cb)
})
)
WIKI.logger.info(`(SEARCH/AWS) Requesting Index Rebuild...`)
await this.client.indexDocuments({
DomainName: this.config.domain
}).promise()
WIKI.logger.info(`(SEARCH/AWS) Index rebuilt successfully.`)
}
}

@ -1,24 +0,0 @@
key: azure
title: Azure Search
description: AI-Powered cloud search service for web and mobile app development.
author: requarks.io
logo: https://static.requarks.io/logo/azure.svg
website: https://azure.microsoft.com/services/search/
isAvailable: true
props:
serviceName:
type: String
title: Service Name
hint: The name of the Azure Search Service. Found under Properties.
order: 1
adminKey:
type: String
title: Admin API Key
hint: Either the primary or secondary admin key. Found under Keys.
order: 2
indexName:
type: String
title: Index Name
hint: 'Name to use when creating the index. (default: wiki)'
default: wiki
order: 3

@ -1,235 +0,0 @@
const _ = require('lodash')
const { SearchService, QueryType } = require('azure-search-client')
const request = require('request-promise')
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)
/* global WIKI */
module.exports = {
async activate() {
// not used
},
async deactivate() {
// not used
},
/**
* INIT
*/
async init() {
WIKI.logger.info(`(SEARCH/AZURE) Initializing...`)
this.client = new SearchService(this.config.serviceName, this.config.adminKey)
// -> Create Search Index
const indexes = await this.client.indexes.list()
if (!_.find(_.get(indexes, 'result.value', []), ['name', this.config.indexName])) {
WIKI.logger.info(`(SEARCH/AZURE) Creating index...`)
await this.client.indexes.create({
name: this.config.indexName,
fields: [
{
name: 'id',
type: 'Edm.String',
key: true,
searchable: false
},
{
name: 'locale',
type: 'Edm.String',
searchable: false
},
{
name: 'path',
type: 'Edm.String',
searchable: false
},
{
name: 'title',
type: 'Edm.String',
searchable: true
},
{
name: 'description',
type: 'Edm.String',
searchable: true
},
{
name: 'content',
type: 'Edm.String',
searchable: true
}
],
scoringProfiles: [
{
name: 'fieldWeights',
text: {
weights: {
title: 4,
description: 3,
content: 1
}
}
}
],
suggesters: [
{
name: 'suggestions',
searchMode: 'analyzingInfixMatching',
sourceFields: ['title', 'description', 'content']
}
]
})
}
WIKI.logger.info(`(SEARCH/AZURE) Initialization completed.`)
},
/**
* QUERY
*
* @param {String} q Query
* @param {Object} opts Additional options
*/
async query(q, opts) {
try {
let suggestions = []
const results = await this.client.indexes.use(this.config.indexName).search({
count: true,
scoringProfile: 'fieldWeights',
search: q,
select: 'id, locale, path, title, description',
queryType: QueryType.simple,
top: 50
})
if (results.result.value.length < 5) {
// Using plain request, not yet available in library...
try {
const suggestResults = await request({
uri: `https://${this.config.serviceName}.search.windows.net/indexes/${this.config.indexName}/docs/autocomplete`,
method: 'post',
qs: {
'api-version': '2017-11-11-Preview'
},
headers: {
'api-key': this.config.adminKey,
'Content-Type': 'application/json'
},
json: true,
body: {
autocompleteMode: 'oneTermWithContext',
search: q,
suggesterName: 'suggestions'
}
})
suggestions = suggestResults.value.map(s => s.queryPlusText)
} catch (err) {
WIKI.logger.warn('Search Engine suggestion failure: ', err)
}
}
return {
results: results.result.value,
suggestions,
totalHits: results.result['@odata.count']
}
} catch (err) {
WIKI.logger.warn('Search Engine Error:')
WIKI.logger.warn(err)
}
},
/**
* CREATE
*
* @param {Object} page Page to create
*/
async created(page) {
await this.client.indexes.use(this.config.indexName).index([
{
id: page.hash,
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent
}
])
},
/**
* UPDATE
*
* @param {Object} page Page to update
*/
async updated(page) {
await this.client.indexes.use(this.config.indexName).index([
{
id: page.hash,
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent
}
])
},
/**
* DELETE
*
* @param {Object} page Page to delete
*/
async deleted(page) {
await this.client.indexes.use(this.config.indexName).index([
{
'@search.action': 'delete',
id: page.hash
}
])
},
/**
* RENAME
*
* @param {Object} page Page to rename
*/
async renamed(page) {
await this.client.indexes.use(this.config.indexName).index([
{
'@search.action': 'delete',
id: page.hash
}
])
await this.client.indexes.use(this.config.indexName).index([
{
id: page.destinationHash,
locale: page.destinationLocaleCode,
path: page.destinationPath,
title: page.title,
description: page.description,
content: page.safeContent
}
])
},
/**
* REBUILD INDEX
*/
async rebuild() {
WIKI.logger.info(`(SEARCH/AZURE) Rebuilding Index...`)
await pipeline(
WIKI.models.knex.column({ id: 'hash' }, 'path', { locale: 'localeCode' }, 'title', 'description', 'render').select().from('pages').where({
isPublished: true,
isPrivate: false
}).stream(),
new stream.Transform({
objectMode: true,
transform: (chunk, enc, cb) => {
cb(null, {
id: chunk.id,
path: chunk.path,
locale: chunk.locale,
title: chunk.title,
description: chunk.description,
content: WIKI.models.pages.cleanHTML(chunk.render)
})
}
}),
this.client.indexes.use(this.config.indexName).createIndexingStream()
)
WIKI.logger.info(`(SEARCH/AZURE) Index rebuilt successfully.`)
}
}

@ -1,8 +0,0 @@
key: db
title: Database - Basic
description: Default basic database-based search engine.
author: requarks.io
logo: https://static.requarks.io/logo/database.svg
website: https://www.requarks.io/
isAvailable: true
props: {}

@ -1,94 +0,0 @@
/* global WIKI */
module.exports = {
activate() {
// not used
},
deactivate() {
// not used
},
/**
* INIT
*/
init() {
// not used
},
/**
* QUERY
*
* @param {String} q Query
* @param {Object} opts Additional options
*/
async query(q, opts) {
const results = await WIKI.models.pages.query()
.column('pages.id', 'title', 'description', 'path', 'localeCode as locale')
.withGraphJoined('tags') // Adding page tags since they can be used to check resource access permissions
.modifyGraph('tags', builder => {
builder.select('tag')
})
.where(builder => {
builder.where('isPublished', true)
if (opts.locale) {
builder.andWhere('localeCode', opts.locale)
}
if (opts.path) {
builder.andWhere('path', 'like', `${opts.path}%`)
}
builder.andWhere(builderSub => {
if (WIKI.config.db.type === 'postgres') {
builderSub.where('title', 'ILIKE', `%${q}%`)
builderSub.orWhere('description', 'ILIKE', `%${q}%`)
builderSub.orWhere('path', 'ILIKE', `%${q.toLowerCase()}%`)
} else {
builderSub.where('title', 'LIKE', `%${q}%`)
builderSub.orWhere('description', 'LIKE', `%${q}%`)
builderSub.orWhere('path', 'LIKE', `%${q.toLowerCase()}%`)
}
})
})
.limit(WIKI.config.search.maxHits)
return {
results,
suggestions: [],
totalHits: results.length
}
},
/**
* CREATE
*
* @param {Object} page Page to create
*/
async created(page) {
// not used
},
/**
* UPDATE
*
* @param {Object} page Page to update
*/
async updated(page) {
// not used
},
/**
* DELETE
*
* @param {Object} page Page to delete
*/
async deleted(page) {
// not used
},
/**
* RENAME
*
* @param {Object} page Page to rename
*/
async renamed(page) {
// not used
},
/**
* REBUILD INDEX
*/
async rebuild() {
// not used
}
}

@ -1,47 +0,0 @@
key: elasticsearch
title: Elasticsearch
description: Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases.
author: requarks.io
logo: https://static.requarks.io/logo/elasticsearch.svg
website: https://www.elastic.co/products/elasticsearch
isAvailable: true
props:
apiVersion:
type: String
title: Elasticsearch Version
hint: Should match the version of the Elasticsearch nodes you are connecting to
order: 1
enum:
- '7.x'
- '6.x'
default: '6.x'
hosts:
type: String
title: Host(s)
hint: Comma-separated list of Elasticsearch hosts to connect to, including the port, username and password if necessary. (e.g. http://localhost:9200, https://user:pass@es1.example.com:9200)
order: 2
indexName:
type: String
title: Index Name
hint: The index name to use during creation
default: wiki
order: 3
analyzer:
type: String
title: Analyzer
hint: 'The token analyzer in elasticsearch'
default: simple
order: 4
sniffOnStart:
type: Boolean
title: Sniff on start
hint: 'Should Wiki.js attempt to detect the rest of the cluster on first connect? (Default: off)'
default: false
order: 5
sniffInterval:
type: Number
title: Sniff Interval
hint: '0 = disabled, Interval in seconds to check for updated list of nodes in cluster. (Default: 0)'
default: 0
order: 6

@ -1,353 +0,0 @@
const _ = require('lodash')
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)
/* global WIKI */
module.exports = {
async activate() {
// not used
},
async deactivate() {
// not used
},
/**
* INIT
*/
async init() {
WIKI.logger.info(`(SEARCH/ELASTICSEARCH) Initializing...`)
switch (this.config.apiVersion) {
case '7.x':
const { Client: Client7 } = require('elasticsearch7')
this.client = new Client7({
nodes: this.config.hosts.split(',').map(_.trim),
sniffOnStart: this.config.sniffOnStart,
sniffInterval: (this.config.sniffInterval > 0) ? this.config.sniffInterval : false,
name: 'wiki-js'
})
break
case '6.x':
const { Client: Client6 } = require('elasticsearch6')
this.client = new Client6({
nodes: this.config.hosts.split(',').map(_.trim),
sniffOnStart: this.config.sniffOnStart,
sniffInterval: (this.config.sniffInterval > 0) ? this.config.sniffInterval : false,
name: 'wiki-js'
})
break
default:
throw new Error('Unsupported version of elasticsearch! Update your settings in the Administration Area.')
}
// -> Create Search Index
await this.createIndex()
WIKI.logger.info(`(SEARCH/ELASTICSEARCH) Initialization completed.`)
},
/**
* Create Index
*/
async createIndex() {
try {
const indexExists = await this.client.indices.exists({ index: this.config.indexName })
if (!indexExists.body) {
WIKI.logger.info(`(SEARCH/ELASTICSEARCH) Creating index...`)
try {
const idxBody = {
properties: {
suggest: { type: 'completion' },
title: { type: 'text', boost: 10.0 },
description: { type: 'text', boost: 3.0 },
content: { type: 'text', boost: 1.0 },
locale: { type: 'keyword' },
path: { type: 'text' },
tags: { type: 'text', boost: 8.0 }
}
}
await this.client.indices.create({
index: this.config.indexName,
body: {
mappings: (this.config.apiVersion === '6.x') ? {
_doc: idxBody
} : idxBody,
settings: {
analysis: {
analyzer: {
default: {
type: this.config.analyzer
}
}
}
}
}
})
} catch (err) {
WIKI.logger.error(`(SEARCH/ELASTICSEARCH) Create Index Error: `, _.get(err, 'meta.body.error', err))
}
}
} catch (err) {
WIKI.logger.error(`(SEARCH/ELASTICSEARCH) Index Check Error: `, _.get(err, 'meta.body.error', err))
}
},
/**
* QUERY
*
* @param {String} q Query
* @param {Object} opts Additional options
*/
async query(q, opts) {
try {
const results = await this.client.search({
index: this.config.indexName,
body: {
query: {
simple_query_string: {
query: `*${q}*`,
fields: ['title^20', 'description^3', 'tags^8', 'content^1'],
default_operator: 'and',
analyze_wildcard: true
}
},
from: 0,
size: 50,
_source: ['title', 'description', 'path', 'locale'],
suggest: {
suggestions: {
text: q,
completion: {
field: 'suggest',
size: 5,
skip_duplicates: true,
fuzzy: true
}
}
}
}
})
return {
results: _.get(results, 'body.hits.hits', []).map(r => ({
id: r._id,
locale: r._source.locale,
path: r._source.path,
title: r._source.title,
description: r._source.description
})),
suggestions: _.reject(_.get(results, 'suggest.suggestions', []).map(s => _.get(s, 'options[0].text', false)), s => !s),
totalHits: _.get(results, 'body.hits.total.value', _.get(results, 'body.hits.total', 0))
}
} catch (err) {
WIKI.logger.warn('Search Engine Error: ', _.get(err, 'meta.body.error', err))
}
},
/**
* Build tags field
* @param id
* @returns {Promise<*|*[]>}
*/
async buildTags(id) {
const tags = await WIKI.models.pages.query().findById(id).select('*').withGraphJoined('tags')
return (tags.tags && tags.tags.length > 0) ? tags.tags.map(function (tag) {
return tag.title
}) : []
},
/**
* Build suggest field
*/
buildSuggest(page) {
return _.reject(_.uniq(_.concat(
page.title.split(' ').map(s => ({
input: s,
weight: 10
})),
page.description.split(' ').map(s => ({
input: s,
weight: 3
})),
page.safeContent.split(' ').map(s => ({
input: s,
weight: 1
}))
)), ['input', ''])
},
/**
* CREATE
*
* @param {Object} page Page to create
*/
async created(page) {
await this.client.index({
index: this.config.indexName,
type: '_doc',
id: page.hash,
body: {
suggest: this.buildSuggest(page),
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent,
tags: await this.buildTags(page.id)
},
refresh: true
})
},
/**
* UPDATE
*
* @param {Object} page Page to update
*/
async updated(page) {
await this.client.index({
index: this.config.indexName,
type: '_doc',
id: page.hash,
body: {
suggest: this.buildSuggest(page),
locale: page.localeCode,
path: page.path,
title: page.title,
description: page.description,
content: page.safeContent,
tags: await this.buildTags(page.id)
},
refresh: true
})
},
/**
* DELETE
*
* @param {Object} page Page to delete
*/
async deleted(page) {
await this.client.delete({
index: this.config.indexName,
type: '_doc',
id: page.hash,
refresh: true
})
},
/**
* RENAME
*
* @param {Object} page Page to rename
*/
async renamed(page) {
await this.client.delete({
index: this.config.indexName,
type: '_doc',
id: page.hash,
refresh: true
})
await this.client.index({
index: this.config.indexName,
type: '_doc',
id: page.destinationHash,
body: {
suggest: this.buildSuggest(page),
locale: page.destinationLocaleCode,
path: page.destinationPath,
title: page.title,
description: page.description,
content: page.safeContent,
tags: await this.buildTags(page.id)
},
refresh: true
})
},
/**
* REBUILD INDEX
*/
async rebuild() {
WIKI.logger.info(`(SEARCH/ELASTICSEARCH) Rebuilding Index...`)
await this.client.indices.delete({ index: this.config.indexName })
await this.createIndex()
const MAX_INDEXING_BYTES = 10 * Math.pow(2, 20) - Buffer.from('[').byteLength - Buffer.from(']').byteLength // 10 MB
const MAX_INDEXING_COUNT = 1000
const COMMA_BYTES = Buffer.from(',').byteLength
let chunks = []
let bytes = 0
const processDocument = async (cb, doc) => {
try {
if (doc) {
const docBytes = Buffer.from(JSON.stringify(doc)).byteLength
doc['tags'] = await this.buildTags(doc.realId)
// -> Current batch exceeds size limit, flush
if (docBytes + COMMA_BYTES + bytes >= MAX_INDEXING_BYTES) {
await flushBuffer()
}
if (chunks.length > 0) {
bytes += COMMA_BYTES
}
bytes += docBytes
chunks.push(doc)
// -> Current batch exceeds count limit, flush
if (chunks.length >= MAX_INDEXING_COUNT) {
await flushBuffer()
}
} else {
// -> End of stream, flush
await flushBuffer()
}
cb()
} catch (err) {
cb(err)
}
}
const flushBuffer = async () => {
WIKI.logger.info(`(SEARCH/ELASTICSEARCH) Sending batch of ${chunks.length}...`)
try {
await this.client.bulk({
index: this.config.indexName,
body: _.reduce(chunks, (result, doc) => {
result.push({
index: {
_index: this.config.indexName,
_type: '_doc',
_id: doc.id
}
})
doc.safeContent = WIKI.models.pages.cleanHTML(doc.render)
result.push({
suggest: this.buildSuggest(doc),
tags: doc.tags,
locale: doc.locale,
path: doc.path,
title: doc.title,
description: doc.description,
content: doc.safeContent
})
return result
}, []),
refresh: true
})
} catch (err) {
WIKI.logger.warn('(SEARCH/ELASTICSEARCH) Failed to send batch to elasticsearch: ', err)
}
chunks.length = 0
bytes = 0
}
// Added real id in order to fetch page tags from the query
await pipeline(
WIKI.models.knex.column({ id: 'hash' }, 'path', { locale: 'localeCode' }, 'title', 'description', 'render', { realId: 'id' }).select().from('pages').where({
isPublished: true,
isPrivate: false
}).stream(),
new stream.Transform({
objectMode: true,
transform: async (chunk, enc, cb) => processDocument(cb, chunk),
flush: async (cb) => processDocument(cb)
})
)
WIKI.logger.info(`(SEARCH/ELASTICSEARCH) Index rebuilt successfully.`)
}
}

@ -1,8 +0,0 @@
key: manticore
title: Manticore Search
description: High performance full-text search engine with SQL and JSON support.
author: requarks.io
logo: https://static.requarks.io/logo/manticore.svg
website: https://manticoresearch.com/
isAvailable: false
props: {}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save