Unverified Commit e9e93eff authored by NGPixel's avatar NGPixel

refactor: convert to esm (wip)

parent fc820eb1
'use strict' export default {
module.exports = {
arabic: '\u0600-\u06ff\u0750-\u077f\ufb50-\ufc3f\ufe70-\ufefc', arabic: '\u0600-\u06ff\u0750-\u077f\ufb50-\ufc3f\ufe70-\ufefc',
cjk: '\u4E00-\u9FBF\u3040-\u309F\u30A0-\u30FFㄱ-ㅎ가-힣ㅏ-ㅣ', cjk: '\u4E00-\u9FBF\u3040-\u309F\u30A0-\u30FFㄱ-ㅎ가-힣ㅏ-ㅣ',
youtube: /(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/, youtube: /(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/,
......
/* global WIKI */ /* global WIKI */
const express = require('express') import express from 'express'
const ExpressBrute = require('express-brute') import ExpressBrute from 'express-brute'
const BruteKnex = require('../helpers/brute-knex') import BruteKnex from '../helpers/brute-knex.mjs'
const router = express.Router() import { find, isEmpty, set } from 'lodash-es'
const moment = require('moment') import path from 'node:path'
const _ = require('lodash') import { DateTime } from 'luxon'
const path = require('path')
const bruteforce = new ExpressBrute(new BruteKnex({ export default function () {
const router = express.Router()
const bruteforce = new ExpressBrute(new BruteKnex({
createTable: true, createTable: true,
knex: WIKI.db.knex knex: WIKI.db.knex
}), { }), {
freeRetries: 5, freeRetries: 5,
minWait: 5 * 60 * 1000, // 5 minutes minWait: 5 * 60 * 1000, // 5 minutes
maxWait: 60 * 60 * 1000, // 1 hour maxWait: 60 * 60 * 1000, // 1 hour
failCallback: (req, res, next) => { failCallback: (req, res, next) => {
res.status(401).send('Too many failed attempts. Try again later.') res.status(401).send('Too many failed attempts. Try again later.')
} }
}) })
/** /**
* Login form * Login form
*/ */
router.get('/login', async (req, res, next) => { router.get('/login', async (req, res, next) => {
// -> Bypass Login // -> Bypass Login
if (WIKI.config.auth.autoLogin && !req.query.all) { if (WIKI.config.auth.autoLogin && !req.query.all) {
const stg = await WIKI.db.authentication.query().orderBy('order').first() const stg = await WIKI.db.authentication.query().orderBy('order').first()
const stgInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey]) const stgInfo = find(WIKI.data.authentication, ['key', stg.strategyKey])
if (!stgInfo.useForm) { if (!stgInfo.useForm) {
return res.redirect(`/login/${stg.key}`) return res.redirect(`/login/${stg.key}`)
} }
} }
// -> Show Login // -> Show Login
res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html')) res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
}) })
/** /**
* Social Strategies Login * Social Strategies Login
*/ */
router.get('/login/:strategy', async (req, res, next) => { router.get('/login/:strategy', async (req, res, next) => {
try { try {
await WIKI.db.users.login({ await WIKI.db.users.login({
strategy: req.params.strategy strategy: req.params.strategy
...@@ -47,19 +49,19 @@ router.get('/login/:strategy', async (req, res, next) => { ...@@ -47,19 +49,19 @@ router.get('/login/:strategy', async (req, res, next) => {
} catch (err) { } catch (err) {
next(err) next(err)
} }
}) })
/** /**
* Social Strategies Callback * Social Strategies Callback
*/ */
router.all('/login/:strategy/callback', async (req, res, next) => { router.all('/login/:strategy/callback', async (req, res, next) => {
if (req.method !== 'GET' && req.method !== 'POST') { return next() } if (req.method !== 'GET' && req.method !== 'POST') { return next() }
try { try {
const authResult = await WIKI.db.users.login({ const authResult = await WIKI.db.users.login({
strategy: req.params.strategy strategy: req.params.strategy
}, { req, res }) }, { req, res })
res.cookie('jwt', authResult.jwt, { expires: moment().add(1, 'y').toDate() }) res.cookie('jwt', authResult.jwt, { expires: DateTime.now().plus({ years: 1 }).toJSDate() })
const loginRedirect = req.cookies['loginRedirect'] const loginRedirect = req.cookies['loginRedirect']
if (loginRedirect === '/' && authResult.redirect) { if (loginRedirect === '/' && authResult.redirect) {
...@@ -76,37 +78,37 @@ router.all('/login/:strategy/callback', async (req, res, next) => { ...@@ -76,37 +78,37 @@ router.all('/login/:strategy/callback', async (req, res, next) => {
} catch (err) { } catch (err) {
next(err) next(err)
} }
}) })
/** /**
* Logout * Logout
*/ */
router.get('/logout', async (req, res, next) => { router.get('/logout', async (req, res, next) => {
const redirURL = await WIKI.db.users.logout({ req, res }) const redirURL = await WIKI.db.users.logout({ req, res })
req.logout((err) => { req.logout((err) => {
if (err) { return next(err) } if (err) { return next(err) }
res.clearCookie('jwt') res.clearCookie('jwt')
res.redirect(redirURL) res.redirect(redirURL)
}) })
}) })
/** /**
* Register form * Register form
*/ */
router.get('/register', async (req, res, next) => { router.get('/register', async (req, res, next) => {
_.set(res.locals, 'pageMeta.title', 'Register') set(res.locals, 'pageMeta.title', 'Register')
const localStrg = await WIKI.db.authentication.getStrategy('local') const localStrg = await WIKI.db.authentication.getStrategy('local')
if (localStrg.selfRegistration) { if (localStrg.selfRegistration) {
res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html')) res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
} else { } else {
next(new WIKI.Error.AuthRegistrationDisabled()) next(new WIKI.Error.AuthRegistrationDisabled())
} }
}) })
/** /**
* Verify * Verify
*/ */
router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => { router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
try { try {
const usr = await WIKI.db.userKeys.validateToken({ kind: 'verify', token: req.params.token }) 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) await WIKI.db.users.query().patch({ isVerified: true }).where('id', usr.id)
...@@ -115,18 +117,18 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => { ...@@ -115,18 +117,18 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
res.redirect('/login') res.redirect('/login')
} else { } else {
const result = await WIKI.db.users.refreshToken(usr) const result = await WIKI.db.users.refreshToken(usr)
res.cookie('jwt', result.token, { expires: moment().add(1, 'years').toDate() }) res.cookie('jwt', result.token, { expires: DateTime.now().plus({ years: 1 }).toJSDate() })
res.redirect('/') res.redirect('/')
} }
} catch (err) { } catch (err) {
next(err) next(err)
} }
}) })
/** /**
* Reset Password * Reset Password
*/ */
router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) => { router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) => {
try { try {
const usr = await WIKI.db.userKeys.validateToken({ kind: 'resetPwd', token: req.params.token }) const usr = await WIKI.db.userKeys.validateToken({ kind: 'resetPwd', token: req.params.token })
if (!usr) { if (!usr) {
...@@ -138,21 +140,22 @@ router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) => ...@@ -138,21 +140,22 @@ router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) =>
userId: usr.id, userId: usr.id,
kind: 'changePwd' kind: 'changePwd'
}) })
const bgUrl = !_.isEmpty(WIKI.config.auth.loginBgUrl) ? WIKI.config.auth.loginBgUrl : '/_assets/img/splash/1.jpg' const bgUrl = !isEmpty(WIKI.config.auth.loginBgUrl) ? WIKI.config.auth.loginBgUrl : '/_assets/img/splash/1.jpg'
res.render('login', { bgUrl, hideLocal: WIKI.config.auth.hideLocal, changePwdContinuationToken }) res.render('login', { bgUrl, hideLocal: WIKI.config.auth.hideLocal, changePwdContinuationToken })
} catch (err) { } catch (err) {
next(err) next(err)
} }
}) })
/** /**
* JWT Public Endpoints * JWT Public Endpoints
*/ */
router.get('/.well-known/jwk.json', function (req, res, next) { router.get('/.well-known/jwk.json', function (req, res, next) {
res.json(WIKI.config.certs.jwk) res.json(WIKI.config.certs.jwk)
}) })
router.get('/.well-known/jwk.pem', function (req, res, next) { router.get('/.well-known/jwk.pem', function (req, res, next) {
res.send(WIKI.config.certs.public) res.send(WIKI.config.certs.public)
}) })
module.exports = router return router
}
const express = require('express') import express from 'express'
const router = express.Router() import { get } from 'lodash-es'
const _ = require('lodash') import qs from 'querystring'
const qs = require('querystring')
/** export default function () {
const router = express.Router()
/**
* Let's Encrypt Challenge * Let's Encrypt Challenge
*/ */
router.get('/.well-known/acme-challenge/:token', (req, res, next) => { router.get('/.well-known/acme-challenge/:token', (req, res, next) => {
res.type('text/plain') res.type('text/plain')
if (_.get(WIKI.config, 'letsencrypt.challenge', false)) { if (get(WIKI.config, 'letsencrypt.challenge', false)) {
if (WIKI.config.letsencrypt.challenge.token === req.params.token) { if (WIKI.config.letsencrypt.challenge.token === req.params.token) {
res.send(WIKI.config.letsencrypt.challenge.keyAuthorization) res.send(WIKI.config.letsencrypt.challenge.keyAuthorization)
WIKI.logger.info(`(LETSENCRYPT) Received valid challenge request. [ ACCEPTED ]`) WIKI.logger.info(`(LETSENCRYPT) Received valid challenge request. [ ACCEPTED ]`)
...@@ -19,18 +21,19 @@ router.get('/.well-known/acme-challenge/:token', (req, res, next) => { ...@@ -19,18 +21,19 @@ router.get('/.well-known/acme-challenge/:token', (req, res, next) => {
} else { } else {
res.status(418).end() res.status(418).end()
} }
}) })
/** /**
* Redirect to HTTPS if HTTP Redirection is enabled * Redirect to HTTPS if HTTP Redirection is enabled
*/ */
// router.all('/*', (req, res, next) => { // router.all('/*', (req, res, next) => {
// if (WIKI.config.server.sslRedir && !req.secure && WIKI.servers.servers.https) { // if (WIKI.config.server.sslRedir && !req.secure && WIKI.servers.servers.https) {
// let query = (!_.isEmpty(req.query)) ? `?${qs.stringify(req.query)}` : `` // let query = (!_.isEmpty(req.query)) ? `?${qs.stringify(req.query)}` : ``
// return res.redirect(`https://${req.hostname}${req.originalUrl}${query}`) // return res.redirect(`https://${req.hostname}${req.originalUrl}${query}`)
// } else { // } else {
// next() // next()
// } // }
// }) // })
module.exports = router return router
}
const express = require('express')
const router = express.Router()
const _ = require('lodash')
const multer = require('multer')
const path = require('path')
const sanitize = require('sanitize-filename')
/**
* Upload files
*/
router.post('/u', (req, res, next) => {
multer({
dest: path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads'),
limits: {
fileSize: WIKI.config.uploads.maxFileSize,
files: WIKI.config.uploads.maxFiles
}
}).array('mediaUpload')(req, res, next)
}, async (req, res, next) => {
if (!_.some(req.user.permissions, pm => _.includes(['write:assets', 'manage:system'], pm))) {
return res.status(403).json({
succeeded: false,
message: 'You are not authorized to upload files.'
})
} else if (req.files.length < 1) {
return res.status(400).json({
succeeded: false,
message: 'Missing upload payload.'
})
} else if (req.files.length > 1) {
return res.status(400).json({
succeeded: false,
message: 'You cannot upload multiple files within the same request.'
})
}
const fileMeta = _.get(req, 'files[0]', false)
if (!fileMeta) {
return res.status(500).json({
succeeded: false,
message: 'Missing upload file metadata.'
})
}
// Get folder Id
let folderId = null
try {
const folderRaw = _.get(req, 'body.mediaUpload', false)
if (folderRaw) {
folderId = _.get(JSON.parse(folderRaw), 'folderId', null)
if (folderId === 0) {
folderId = null
}
} else {
throw new Error('Missing File Metadata')
}
} catch (err) {
return res.status(400).json({
succeeded: false,
message: 'Missing upload folder metadata.'
})
}
// Build folder hierarchy
let hierarchy = []
if (folderId) {
try {
hierarchy = await WIKI.db.assetFolders.getHierarchy(folderId)
} catch (err) {
return res.status(400).json({
succeeded: false,
message: 'Failed to fetch folder hierarchy.'
})
}
}
// Sanitize filename
fileMeta.originalname = sanitize(fileMeta.originalname.toLowerCase().replace(/[\s,;#]+/g, '_'))
// Check if user can upload at path
const assetPath = (folderId) ? hierarchy.map(h => h.slug).join('/') + `/${fileMeta.originalname}` : fileMeta.originalname
if (!WIKI.auth.checkAccess(req.user, ['write:assets'], { path: assetPath })) {
return res.status(403).json({
succeeded: false,
message: 'You are not authorized to upload files to this folder.'
})
}
// Process upload file
await WIKI.db.assets.upload({
...fileMeta,
mode: 'upload',
folderId: folderId,
assetPath,
user: req.user
})
res.send('ok')
})
router.get('/u', async (req, res, next) => {
res.json({
ok: true
})
})
module.exports = router
const chalk = require('chalk') import chalk from 'chalk'
const os = require('node:os') import os from 'node:os'
module.exports = () => { export default function () {
WIKI.servers.ws.on('connection', (socket) => { WIKI.servers.ws.on('connection', (socket) => {
// TODO: Validate token + permissions // TODO: Validate token + permissions
const token = socket.handshake.auth.token const token = socket.handshake.auth.token
console.info(token) // console.info(token)
const listeners = {} const listeners = {}
socket.on('server:logs', () => { socket.on('server:logs', () => {
socket.emit('server:log', chalk`{greenBright Streaming logs from {bold Wiki.js} instance {yellowBright.bold ${WIKI.INSTANCE_ID}} on host {yellowBright.bold ${os.hostname()}}...}`) socket.emit('server:log', chalk.greenBright(`Streaming logs from ${chalk.bold('Wiki.js')} instance ${chalk.yellowBright.bold(WIKI.INSTANCE_ID)} on host ${chalk.yellowBright.bold(os.hostname())}...`))
listeners.serverLogs = (msg) => { listeners.serverLogs = (msg) => {
socket.emit('server:log', msg) socket.emit('server:log', msg)
} }
......
const pickle = require('chromium-pickle-js') import pickle from 'chromium-pickle-js'
const path = require('path') import path from 'node:path'
const UINT64 = require('cuint').UINT64 import { UINT64 } from 'cuint'
const fs = require('fs') import fs from 'node:fs'
/** /**
* Based of express-serve-asar (https://github.com/toyobayashi/express-serve-asar) * Based of express-serve-asar (https://github.com/toyobayashi/express-serve-asar)
* by Fenglin Li (https://github.com/toyobayashi) * by Fenglin Li (https://github.com/toyobayashi)
*/ */
const packages = { export default {
'twemoji': path.join(WIKI.ROOTPATH, `assets-legacy/svg/twemoji.asar`)
}
module.exports = {
fdCache: {}, fdCache: {},
async serve (pkgName, req, res, next) { async serve (pkgName, req, res, next) {
const packages = {
'twemoji': path.join(WIKI.ROOTPATH, `assets/svg/twemoji.asar`)
}
const file = this.readFilesystemSync(packages[pkgName]) const file = this.readFilesystemSync(packages[pkgName])
const { filesystem, fd } = file const { filesystem, fd } = file
const info = filesystem.getFile(req.path.substring(1)) const info = filesystem.getFile(req.path.substring(1))
......
const passport = require('passport') import passport from 'passport'
const passportJWT = require('passport-jwt') import passportJWT from 'passport-jwt'
const _ = require('lodash') import _ from 'lodash'
const jwt = require('jsonwebtoken') import jwt from 'jsonwebtoken'
const ms = require('ms') import ms from 'ms'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const util = require('node:util') import util from 'node:util'
const crypto = require('node:crypto') import crypto from 'node:crypto'
const randomBytes = util.promisify(crypto.randomBytes) import { pem2jwk } from 'pem-jwk'
const pem2jwk = require('pem-jwk').pem2jwk import NodeCache from 'node-cache'
import { extractJWT } from '../helpers/security.mjs'
const securityHelper = require('../helpers/security') const randomBytes = util.promisify(crypto.randomBytes)
module.exports = { export default {
strategies: {}, strategies: {},
guest: { guest: {
cacheExpiration: DateTime.utc().minus({ days: 1 }) cacheExpiration: DateTime.utc().minus({ days: 1 })
}, },
groups: {}, groups: {},
validApiKeys: [], validApiKeys: [],
revocationList: require('./cache').init(), revocationList: new NodeCache(),
/** /**
* Initialize the authentication module * Initialize the authentication module
...@@ -64,7 +65,7 @@ module.exports = { ...@@ -64,7 +65,7 @@ module.exports = {
// Load JWT // Load JWT
passport.use('jwt', new passportJWT.Strategy({ passport.use('jwt', new passportJWT.Strategy({
jwtFromRequest: securityHelper.extractJWT, jwtFromRequest: extractJWT,
secretOrKey: WIKI.config.auth.certs.public, secretOrKey: WIKI.config.auth.certs.public,
audience: WIKI.config.auth.audience, audience: WIKI.config.auth.audience,
issuer: 'urn:wiki.js', issuer: 'urn:wiki.js',
...@@ -141,7 +142,7 @@ module.exports = { ...@@ -141,7 +142,7 @@ module.exports = {
// Revalidate and renew token // Revalidate and renew token
if (mustRevalidate) { if (mustRevalidate) {
const jwtPayload = jwt.decode(securityHelper.extractJWT(req)) const jwtPayload = jwt.decode(extractJWT(req))
try { try {
const newToken = await WIKI.db.users.refreshToken(jwtPayload.id, jwtPayload.pvd) const newToken = await WIKI.db.users.refreshToken(jwtPayload.id, jwtPayload.pvd)
user = newToken.user user = newToken.user
......
const NodeCache = require('node-cache')
module.exports = {
init() {
return new NodeCache()
}
}
const _ = require('lodash') import { defaultsDeep, get, isPlainObject } from 'lodash-es'
const chalk = require('chalk') import chalk from 'chalk'
const cfgHelper = require('../helpers/config') import cfgHelper from '../helpers/config.mjs'
const fs = require('fs') import regexData from '../app/regex.mjs'
const path = require('path') import fs from 'node:fs/promises'
const yaml = require('js-yaml') import path from 'node:path'
import yaml from 'js-yaml'
module.exports = {
export default {
/** /**
* Load root config from disk * Load root config from disk
*/ */
init(silent = false) { async init(silent = false) {
let confPaths = { const confPaths = {
config: path.join(WIKI.ROOTPATH, 'config.yml'), config: path.join(WIKI.ROOTPATH, 'config.yml'),
data: path.join(WIKI.SERVERPATH, 'app/data.yml'), data: path.join(WIKI.SERVERPATH, 'app/data.yml')
dataRegex: path.join(WIKI.SERVERPATH, 'app/regex.js')
} }
if (process.env.dockerdev) { if (process.env.dockerdev) {
...@@ -34,11 +34,11 @@ module.exports = { ...@@ -34,11 +34,11 @@ module.exports = {
try { try {
appconfig = yaml.load( appconfig = yaml.load(
cfgHelper.parseConfigValue( cfgHelper.parseConfigValue(
fs.readFileSync(confPaths.config, 'utf8') await fs.readFile(confPaths.config, 'utf8')
) )
) )
appdata = yaml.load(fs.readFileSync(confPaths.data, 'utf8')) appdata = yaml.load(await fs.readFile(confPaths.data, 'utf8'))
appdata.regex = require(confPaths.dataRegex) appdata.regex = regexData
if (!silent) { if (!silent) {
console.info(chalk.green.bold(`OK`)) console.info(chalk.green.bold(`OK`))
} }
...@@ -52,7 +52,7 @@ module.exports = { ...@@ -52,7 +52,7 @@ module.exports = {
// Merge with defaults // Merge with defaults
appconfig = _.defaultsDeep(appconfig, appdata.defaults.config) appconfig = defaultsDeep(appconfig, appdata.defaults.config)
// Override port // Override port
...@@ -66,7 +66,7 @@ module.exports = { ...@@ -66,7 +66,7 @@ module.exports = {
// Load package info // Load package info
const packageInfo = require(path.join(WIKI.SERVERPATH, 'package.json')) const packageInfo = JSON.parse(await fs.readFile(path.join(WIKI.SERVERPATH, 'package.json'), 'utf-8'))
// Load DB Password from Docker Secret File // Load DB Password from Docker Secret File
if (process.env.DB_PASS_FILE) { if (process.env.DB_PASS_FILE) {
...@@ -74,7 +74,7 @@ module.exports = { ...@@ -74,7 +74,7 @@ module.exports = {
console.info(chalk.blue(`DB_PASS_FILE is defined. Will use secret from file.`)) console.info(chalk.blue(`DB_PASS_FILE is defined. Will use secret from file.`))
} }
try { try {
appconfig.db.pass = fs.readFileSync(process.env.DB_PASS_FILE, 'utf8').trim() appconfig.db.pass = await fs.readFile(process.env.DB_PASS_FILE, 'utf8').trim()
} catch (err) { } catch (err) {
console.error(chalk.red.bold(`>>> Failed to read Docker Secret File using path defined in DB_PASS_FILE env variable!`)) console.error(chalk.red.bold(`>>> Failed to read Docker Secret File using path defined in DB_PASS_FILE env variable!`))
console.error(err.message) console.error(err.message)
...@@ -93,9 +93,9 @@ module.exports = { ...@@ -93,9 +93,9 @@ module.exports = {
* Load config from DB * Load config from DB
*/ */
async loadFromDb() { async loadFromDb() {
let conf = await WIKI.db.settings.getConfig() const conf = await WIKI.db.settings.getConfig()
if (conf) { if (conf) {
WIKI.config = _.defaultsDeep(conf, WIKI.config) WIKI.config = defaultsDeep(conf, WIKI.config)
} else { } else {
WIKI.logger.warn('Missing DB Configuration!') WIKI.logger.warn('Missing DB Configuration!')
process.exit(1) process.exit(1)
...@@ -110,8 +110,8 @@ module.exports = { ...@@ -110,8 +110,8 @@ module.exports = {
async saveToDb(keys, propagate = true) { async saveToDb(keys, propagate = true) {
try { try {
for (let key of keys) { for (let key of keys) {
let value = _.get(WIKI.config, key, null) let value = get(WIKI.config, key, null)
if (!_.isPlainObject(value)) { if (!isPlainObject(value)) {
value = { v: value } value = { v: value }
} }
let affectedRows = await WIKI.db.settings.query().patch({ value }).where('key', key) let affectedRows = await WIKI.db.settings.query().patch({ value }).where('key', key)
......
const _ = require('lodash') import { get, has, isEmpty, isPlainObject } from 'lodash-es'
const autoload = require('auto-load') import path from 'node:path'
const path = require('path') import knex from 'knex'
const Knex = require('knex') import fs from 'node:fs/promises'
const fs = require('fs') import Objection from 'objection'
const Objection = require('objection') import PGPubSub from 'pg-pubsub'
const PGPubSub = require('pg-pubsub')
import migrationSource from '../db/migrator-source.mjs'
const migrationSource = require('../db/migrator-source') // const migrateFromLegacy = require('../db/legacy')
const migrateFromLegacy = require('../db/legacy') import { setTimeout } from 'node:timers/promises'
const { setTimeout } = require('timers/promises')
/** /**
* ORM DB module * ORM DB module
*/ */
module.exports = { export default {
Objection, Objection,
knex: null, knex: null,
listener: null, listener: null,
...@@ -21,14 +20,14 @@ module.exports = { ...@@ -21,14 +20,14 @@ module.exports = {
/** /**
* Initialize DB * Initialize DB
*/ */
init(workerMode = false) { async init (workerMode = false) {
let self = this let self = this
WIKI.logger.info('Checking DB configuration...') WIKI.logger.info('Checking DB configuration...')
// Fetch DB Config // Fetch DB Config
this.config = (!_.isEmpty(process.env.DATABASE_URL)) ? process.env.DATABASE_URL : { this.config = (!isEmpty(process.env.DATABASE_URL)) ? process.env.DATABASE_URL : {
host: WIKI.config.db.host.toString(), host: WIKI.config.db.host.toString(),
user: WIKI.config.db.user.toString(), user: WIKI.config.db.user.toString(),
password: WIKI.config.db.pass.toString(), password: WIKI.config.db.pass.toString(),
...@@ -40,27 +39,27 @@ module.exports = { ...@@ -40,27 +39,27 @@ module.exports = {
let dbUseSSL = (WIKI.config.db.ssl === true || WIKI.config.db.ssl === 'true' || WIKI.config.db.ssl === 1 || WIKI.config.db.ssl === '1') let dbUseSSL = (WIKI.config.db.ssl === true || WIKI.config.db.ssl === 'true' || WIKI.config.db.ssl === 1 || WIKI.config.db.ssl === '1')
let sslOptions = null let sslOptions = null
if (dbUseSSL && _.isPlainObject(this.config) && _.get(WIKI.config.db, 'sslOptions.auto', null) === false) { if (dbUseSSL && isPlainObject(this.config) && get(WIKI.config.db, 'sslOptions.auto', null) === false) {
sslOptions = WIKI.config.db.sslOptions sslOptions = WIKI.config.db.sslOptions
sslOptions.rejectUnauthorized = sslOptions.rejectUnauthorized !== false sslOptions.rejectUnauthorized = sslOptions.rejectUnauthorized !== false
if (sslOptions.ca && sslOptions.ca.indexOf('-----') !== 0) { if (sslOptions.ca && sslOptions.ca.indexOf('-----') !== 0) {
sslOptions.ca = fs.readFileSync(path.resolve(WIKI.ROOTPATH, sslOptions.ca)) sslOptions.ca = await fs.readFile(path.resolve(WIKI.ROOTPATH, sslOptions.ca), 'utf-8')
} }
if (sslOptions.cert) { if (sslOptions.cert) {
sslOptions.cert = fs.readFileSync(path.resolve(WIKI.ROOTPATH, sslOptions.cert)) sslOptions.cert = await fs.readFile(path.resolve(WIKI.ROOTPATH, sslOptions.cert), 'utf-8')
} }
if (sslOptions.key) { if (sslOptions.key) {
sslOptions.key = fs.readFileSync(path.resolve(WIKI.ROOTPATH, sslOptions.key)) sslOptions.key = await fs.readFile(path.resolve(WIKI.ROOTPATH, sslOptions.key), 'utf-8')
} }
if (sslOptions.pfx) { if (sslOptions.pfx) {
sslOptions.pfx = fs.readFileSync(path.resolve(WIKI.ROOTPATH, sslOptions.pfx)) sslOptions.pfx = await fs.readFile(path.resolve(WIKI.ROOTPATH, sslOptions.pfx), 'utf-8')
} }
} else { } else {
sslOptions = true sslOptions = true
} }
// Handle inline SSL CA Certificate mode // Handle inline SSL CA Certificate mode
if (!_.isEmpty(process.env.DB_SSL_CA)) { if (!isEmpty(process.env.DB_SSL_CA)) {
const chunks = [] const chunks = []
for (let i = 0, charsLength = process.env.DB_SSL_CA.length; i < charsLength; i += 64) { for (let i = 0, charsLength = process.env.DB_SSL_CA.length; i < charsLength; i += 64) {
chunks.push(process.env.DB_SSL_CA.substring(i, i + 64)) chunks.push(process.env.DB_SSL_CA.substring(i, i + 64))
...@@ -73,12 +72,12 @@ module.exports = { ...@@ -73,12 +72,12 @@ module.exports = {
} }
} }
if (dbUseSSL && _.isPlainObject(this.config)) { if (dbUseSSL && isPlainObject(this.config)) {
this.config.ssl = (sslOptions === true) ? { rejectUnauthorized: true } : sslOptions this.config.ssl = (sslOptions === true) ? { rejectUnauthorized: true } : sslOptions
} }
// Initialize Knex // Initialize Knex
this.knex = Knex({ this.knex = knex({
client: 'pg', client: 'pg',
useNullAsDefault: true, useNullAsDefault: true,
asyncStackTraces: WIKI.IS_DEBUG, asyncStackTraces: WIKI.IS_DEBUG,
...@@ -104,11 +103,11 @@ module.exports = { ...@@ -104,11 +103,11 @@ module.exports = {
// Load DB Models // Load DB Models
WIKI.logger.info('Loading DB models...') WIKI.logger.info('Loading DB models...')
const models = autoload(path.join(WIKI.SERVERPATH, 'models')) const models = (await import(path.join(WIKI.SERVERPATH, 'models/index.mjs'))).default
// Set init tasks // Set init tasks
let conAttempts = 0 let conAttempts = 0
let initTasks = { const initTasks = {
// -> Attempt initial connection // -> Attempt initial connection
async connect () { async connect () {
try { try {
...@@ -143,7 +142,7 @@ module.exports = { ...@@ -143,7 +142,7 @@ module.exports = {
}, },
// -> Migrate DB Schemas from 2.x // -> Migrate DB Schemas from 2.x
async migrateFromLegacy () { async migrateFromLegacy () {
return migrateFromLegacy.migrate(self.knex) // return migrateFromLegacy.migrate(self.knex)
} }
} }
...@@ -184,7 +183,7 @@ module.exports = { ...@@ -184,7 +183,7 @@ module.exports = {
// -> Outbound events handling // -> Outbound events handling
this.listener.addChannel('wiki', payload => { this.listener.addChannel('wiki', payload => {
if (_.has(payload, 'event') && payload.source !== WIKI.INSTANCE_ID) { if (has(payload, 'event') && payload.source !== WIKI.INSTANCE_ID) {
WIKI.logger.info(`Received event ${payload.event} from instance ${payload.source}: [ OK ]`) WIKI.logger.info(`Received event ${payload.event} from instance ${payload.source}: [ OK ]`)
WIKI.events.inbound.emit(payload.event, payload.value) WIKI.events.inbound.emit(payload.event, payload.value)
} }
......
const fs = require('fs-extra') import fs from 'node:fs/promises'
const path = require('path') import path from 'path'
module.exports = { export default {
ext: {}, ext: {},
async init () { async init () {
const extDirs = await fs.readdir(path.join(WIKI.SERVERPATH, 'modules/extensions')) const extDirs = await fs.readdir(path.join(WIKI.SERVERPATH, 'modules/extensions'))
WIKI.logger.info(`Checking for installed optional extensions...`) WIKI.logger.info(`Checking for installed optional extensions...`)
for (let dir of extDirs) { for (const dir of extDirs) {
WIKI.extensions.ext[dir] = require(path.join(WIKI.SERVERPATH, 'modules/extensions', dir, 'ext.js')) WIKI.extensions.ext[dir] = (await import(path.join(WIKI.SERVERPATH, 'modules/extensions', dir, 'ext.mjs'))).default
const isInstalled = await WIKI.extensions.ext[dir].check() const isInstalled = await WIKI.extensions.ext[dir].check()
if (isInstalled) { if (isInstalled) {
WIKI.logger.info(`Optional extension ${dir} is installed. [ OK ]`) WIKI.logger.info(`Optional extension ${dir} is installed. [ OK ]`)
......
const _ = require('lodash') import { padEnd } from 'lodash-es'
const EventEmitter = require('eventemitter2').EventEmitter2 import eventemitter2 from 'eventemitter2'
import NodeCache from 'node-cache'
import asar from './asar.mjs'
import db from './db.mjs'
import extensions from './extensions.mjs'
import scheduler from './scheduler.mjs'
import servers from './servers.mjs'
let isShuttingDown = false let isShuttingDown = false
module.exports = { export default {
async init() { async init() {
WIKI.logger.info('=======================================') WIKI.logger.info('=======================================')
WIKI.logger.info(`= Wiki.js ${_.padEnd(WIKI.version + ' ', 29, '=')}`) WIKI.logger.info(`= Wiki.js ${padEnd(WIKI.version + ' ', 29, '=')}`)
WIKI.logger.info('=======================================') WIKI.logger.info('=======================================')
WIKI.logger.info('Initializing...') WIKI.logger.info('Initializing...')
WIKI.logger.info(`Running node.js ${process.version}`)
WIKI.db = require('./db').init() WIKI.db = await db.init()
try { try {
await WIKI.db.onReady await WIKI.db.onReady
...@@ -31,15 +39,15 @@ module.exports = { ...@@ -31,15 +39,15 @@ module.exports = {
*/ */
async preBootWeb() { async preBootWeb() {
try { try {
WIKI.cache = require('./cache').init() WIKI.cache = new NodeCache()
WIKI.scheduler = await require('./scheduler').init() WIKI.scheduler = await scheduler.init()
WIKI.servers = require('./servers') WIKI.servers = servers
WIKI.events = { WIKI.events = {
inbound: new EventEmitter(), inbound: new eventemitter2.EventEmitter2(),
outbound: new EventEmitter() outbound: new eventemitter2.EventEmitter2()
} }
WIKI.extensions = require('./extensions') WIKI.extensions = extensions
WIKI.asar = require('./asar') WIKI.asar = asar
} catch (err) { } catch (err) {
WIKI.logger.error(err) WIKI.logger.error(err)
process.exit(1) process.exit(1)
...@@ -51,7 +59,7 @@ module.exports = { ...@@ -51,7 +59,7 @@ module.exports = {
async bootWeb() { async bootWeb() {
try { try {
await this.preBootWeb() await this.preBootWeb()
await require('../web')() await (await import('../web.mjs')).init()
this.postBootWeb() this.postBootWeb()
} catch (err) { } catch (err) {
WIKI.logger.error(err) WIKI.logger.error(err)
......
const chalk = require('chalk') import chalk from 'chalk'
const EventEmitter = require('events') import EventEmitter from 'node:events'
const LEVELS = ['error', 'warn', 'info', 'debug'] const LEVELS = ['error', 'warn', 'info', 'debug']
const LEVELSIGNORED = ['verbose', 'silly'] const LEVELSIGNORED = ['verbose', 'silly']
...@@ -11,13 +11,17 @@ const LEVELCOLORS = { ...@@ -11,13 +11,17 @@ const LEVELCOLORS = {
} }
class Logger extends EventEmitter {} class Logger extends EventEmitter {}
const primaryLogger = new Logger()
let ignoreNextLevels = false export default {
loggers: {},
init () {
const primaryLogger = new Logger()
primaryLogger.ws = new EventEmitter() let ignoreNextLevels = false
LEVELS.forEach(lvl => { primaryLogger.ws = new EventEmitter()
LEVELS.forEach(lvl => {
primaryLogger[lvl] = (...args) => { primaryLogger[lvl] = (...args) => {
primaryLogger.emit(lvl, ...args) primaryLogger.emit(lvl, ...args)
} }
...@@ -36,7 +40,7 @@ LEVELS.forEach(lvl => { ...@@ -36,7 +40,7 @@ LEVELS.forEach(lvl => {
if (msg instanceof Error) { if (msg instanceof Error) {
msg = msg.stack msg = msg.stack
} }
formatted = chalk`${new Date().toISOString()} {dim [${WIKI.INSTANCE_ID}]} {${LEVELCOLORS[lvl]}.bold ${lvl}}: ${msg}` formatted = `${new Date().toISOString()} ${chalk.dim('[' + WIKI.INSTANCE_ID + ']')} ${chalk[LEVELCOLORS[lvl]].bold(lvl)}: ${msg}`
} }
console.log(formatted) console.log(formatted)
...@@ -46,15 +50,12 @@ LEVELS.forEach(lvl => { ...@@ -46,15 +50,12 @@ LEVELS.forEach(lvl => {
if (lvl === WIKI.config.logLevel) { if (lvl === WIKI.config.logLevel) {
ignoreNextLevels = true ignoreNextLevels = true
} }
}) })
LEVELSIGNORED.forEach(lvl => { LEVELSIGNORED.forEach(lvl => {
primaryLogger[lvl] = () => {} primaryLogger[lvl] = () => {}
}) })
module.exports = {
loggers: {},
init () {
return primaryLogger return primaryLogger
} }
} }
const nodemailer = require('nodemailer') import nodemailer from 'nodemailer'
const _ = require('lodash') import { get, has, kebabCase, set, template } from 'lodash-es'
const fs = require('fs-extra') import fs from 'node:fs/promises'
const path = require('path') import path from 'node:path'
module.exports = { export default {
transport: null, transport: null,
templates: {}, templates: {},
init() { init() {
if (_.get(WIKI.config, 'mail.host', '').length > 2) { if (get(WIKI.config, 'mail.host', '').length > 2) {
let conf = { let conf = {
host: WIKI.config.mail.host, host: WIKI.config.mail.host,
port: WIKI.config.mail.port, port: WIKI.config.mail.port,
...@@ -17,7 +17,7 @@ module.exports = { ...@@ -17,7 +17,7 @@ module.exports = {
rejectUnauthorized: !(WIKI.config.mail.verifySSL === false) rejectUnauthorized: !(WIKI.config.mail.verifySSL === false)
} }
} }
if (_.get(WIKI.config, 'mail.user', '').length > 1) { if (get(WIKI.config, 'mail.user', '').length > 1) {
conf = { conf = {
...conf, ...conf,
auth: { auth: {
...@@ -26,7 +26,7 @@ module.exports = { ...@@ -26,7 +26,7 @@ module.exports = {
} }
} }
} }
if (_.get(WIKI.config, 'mail.useDKIM', false)) { if (get(WIKI.config, 'mail.useDKIM', false)) {
conf = { conf = {
...conf, ...conf,
dkim: { dkim: {
...@@ -57,7 +57,7 @@ module.exports = { ...@@ -57,7 +57,7 @@ module.exports = {
to: opts.to, to: opts.to,
subject: `${opts.subject} - ${WIKI.config.title}`, subject: `${opts.subject} - ${WIKI.config.title}`,
text: opts.text, text: opts.text,
html: _.get(this.templates, opts.template)({ html: get(this.templates, opts.template)({
logo: (WIKI.config.logoUrl.startsWith('http') ? '' : WIKI.config.host) + WIKI.config.logoUrl, logo: (WIKI.config.logoUrl.startsWith('http') ? '' : WIKI.config.host) + WIKI.config.logoUrl,
siteTitle: WIKI.config.title, siteTitle: WIKI.config.title,
copyright: WIKI.config.company.length > 0 ? WIKI.config.company : 'Powered by Wiki.js', copyright: WIKI.config.company.length > 0 ? WIKI.config.company : 'Powered by Wiki.js',
...@@ -66,11 +66,11 @@ module.exports = { ...@@ -66,11 +66,11 @@ module.exports = {
}) })
}, },
async loadTemplate(key) { async loadTemplate(key) {
if (_.has(this.templates, key)) { return } if (has(this.templates, key)) { return }
const keyKebab = _.kebabCase(key) const keyKebab = kebabCase(key)
try { try {
const rawTmpl = await fs.readFile(path.join(WIKI.SERVERPATH, `templates/${keyKebab}.html`), 'utf8') const rawTmpl = await fs.readFile(path.join(WIKI.SERVERPATH, `templates/${keyKebab}.html`), 'utf8')
_.set(this.templates, key, _.template(rawTmpl)) set(this.templates, key, template(rawTmpl))
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
throw new WIKI.Error.MailTemplateFailed() throw new WIKI.Error.MailTemplateFailed()
......
const { DynamicThreadPool } = require('poolifier') import { DynamicThreadPool } from 'poolifier'
const os = require('node:os') import os from 'node:os'
const autoload = require('auto-load') import fs from 'node:fs/promises'
const path = require('node:path') import path from 'node:path'
const cronparser = require('cron-parser') import cronparser from 'cron-parser'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const { v4: uuid } = require('uuid') import { v4 as uuid } from 'uuid'
const { createDeferred } = require('../helpers/common') import { createDeferred } from '../helpers/common.mjs'
const _ = require('lodash') import { find, remove } from 'lodash-es'
module.exports = { export default {
workerPool: null, workerPool: null,
maxWorkers: 1, maxWorkers: 1,
activeWorkers: 0, activeWorkers: 0,
...@@ -25,7 +25,11 @@ module.exports = { ...@@ -25,7 +25,11 @@ module.exports = {
exitHandler: () => WIKI.logger.debug('A worker has gone offline.'), exitHandler: () => WIKI.logger.debug('A worker has gone offline.'),
onlineHandler: () => WIKI.logger.debug('New worker is online.') onlineHandler: () => WIKI.logger.debug('New worker is online.')
}) })
this.tasks = autoload(path.join(WIKI.SERVERPATH, 'tasks/simple')) this.tasks = {}
for (const f of (await fs.readdir(path.join(WIKI.SERVERPATH, 'tasks/simple')))) {
const taskName = f.replace('.mjs', '')
this.tasks[taskName] = (await import(path.join(WIKI.SERVERPATH, 'tasks/simple', f))).task
}
return this return this
}, },
async start () { async start () {
...@@ -43,7 +47,7 @@ module.exports = { ...@@ -43,7 +47,7 @@ module.exports = {
break break
} }
case 'jobCompleted': { case 'jobCompleted': {
const jobPromise = _.find(this.completionPromises, ['id', payload.id]) const jobPromise = find(this.completionPromises, ['id', payload.id])
if (jobPromise) { if (jobPromise) {
if (payload.state === 'success') { if (payload.state === 'success') {
jobPromise.resolve() jobPromise.resolve()
...@@ -51,7 +55,7 @@ module.exports = { ...@@ -51,7 +55,7 @@ module.exports = {
jobPromise.reject(new Error(payload.errorMessage)) jobPromise.reject(new Error(payload.errorMessage))
} }
setTimeout(() => { setTimeout(() => {
_.remove(this.completionPromises, ['id', payload.id]) remove(this.completionPromises, ['id', payload.id])
}) })
} }
break break
......
const fs = require('fs-extra') import fs from 'node:fs/promises'
const http = require('http') import http from 'node:http'
const https = require('https') import https from 'node:https'
const { ApolloServer } = require('apollo-server-express') import { ApolloServer } from 'apollo-server-express'
const _ = require('lodash') import { isEmpty } from 'lodash-es'
const io = require('socket.io') import { Server as IoServer } from 'socket.io'
const { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } = require('apollo-server-core') import { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } from 'apollo-server-core'
const { graphqlUploadExpress } = require('graphql-upload') import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs'
module.exports = { import { initSchema } from '../graph/index.mjs'
export default {
graph: null, graph: null,
http: null, http: null,
https: null, https: null,
...@@ -69,15 +71,15 @@ module.exports = { ...@@ -69,15 +71,15 @@ module.exports = {
const tlsOpts = {} const tlsOpts = {}
try { try {
if (WIKI.config.ssl.format === 'pem') { if (WIKI.config.ssl.format === 'pem') {
tlsOpts.key = WIKI.config.ssl.inline ? WIKI.config.ssl.key : fs.readFileSync(WIKI.config.ssl.key) tlsOpts.key = WIKI.config.ssl.inline ? WIKI.config.ssl.key : await fs.readFile(WIKI.config.ssl.key, 'utf-8')
tlsOpts.cert = WIKI.config.ssl.inline ? WIKI.config.ssl.cert : fs.readFileSync(WIKI.config.ssl.cert) tlsOpts.cert = WIKI.config.ssl.inline ? WIKI.config.ssl.cert : await fs.readFile(WIKI.config.ssl.cert, 'utf-8')
} else { } else {
tlsOpts.pfx = WIKI.config.ssl.inline ? WIKI.config.ssl.pfx : fs.readFileSync(WIKI.config.ssl.pfx) tlsOpts.pfx = WIKI.config.ssl.inline ? WIKI.config.ssl.pfx : await fs.readFile(WIKI.config.ssl.pfx, 'utf-8')
} }
if (!_.isEmpty(WIKI.config.ssl.passphrase)) { if (!isEmpty(WIKI.config.ssl.passphrase)) {
tlsOpts.passphrase = WIKI.config.ssl.passphrase tlsOpts.passphrase = WIKI.config.ssl.passphrase
} }
if (!_.isEmpty(WIKI.config.ssl.dhparam)) { if (!isEmpty(WIKI.config.ssl.dhparam)) {
tlsOpts.dhparam = WIKI.config.ssl.dhparam tlsOpts.dhparam = WIKI.config.ssl.dhparam
} }
} catch (err) { } catch (err) {
...@@ -127,7 +129,7 @@ module.exports = { ...@@ -127,7 +129,7 @@ module.exports = {
* Start GraphQL Server * Start GraphQL Server
*/ */
async startGraphQL () { async startGraphQL () {
const graphqlSchema = require('../graph') const graphqlSchema = await initSchema()
this.graph = new ApolloServer({ this.graph = new ApolloServer({
schema: graphqlSchema, schema: graphqlSchema,
csrfPrevention: true, csrfPrevention: true,
...@@ -155,13 +157,13 @@ module.exports = { ...@@ -155,13 +157,13 @@ module.exports = {
*/ */
async initWebSocket() { async initWebSocket() {
if (this.https) { if (this.https) {
this.ws = new io.Server(this.https, { this.ws = new IoServer(this.https, {
path: '/_ws/', path: '/_ws/',
serveClient: false serveClient: false
}) })
WIKI.logger.info(`WebSocket Server attached to HTTPS Server [ OK ]`) WIKI.logger.info(`WebSocket Server attached to HTTPS Server [ OK ]`)
} else { } else {
this.ws = new io.Server(this.http, { this.ws = new IoServer(this.http, {
path: '/_ws/', path: '/_ws/',
serveClient: false, serveClient: false,
cors: true // TODO: dev only, replace with app settings once stable cors: true // TODO: dev only, replace with app settings once stable
......
const fs = require('fs-extra') import fse from 'fs-extra'
const path = require('path') import path from 'node:path'
module.exports = { export default {
updates: { updates: {
channel: 'BETA', channel: 'BETA',
version: WIKI.version, version: WIKI.version,
...@@ -10,11 +10,11 @@ module.exports = { ...@@ -10,11 +10,11 @@ module.exports = {
minimumNodeRequired: '18.0.0' minimumNodeRequired: '18.0.0'
}, },
init () { init () {
fs.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets')) fse.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets'))
fs.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads')) fse.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads'))
// Clear content cache // Clear content cache
fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'cache')) fse.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'cache'))
return this return this
} }
......
const { v4: uuid } = require('uuid') import { v4 as uuid } from 'uuid'
const bcrypt = require('bcryptjs-then') import bcrypt from 'bcryptjs-then'
const crypto = require('crypto') import crypto from 'node:crypto'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const pem2jwk = require('pem-jwk').pem2jwk import { pem2jwk } from 'pem-jwk'
exports.up = async knex => { export async function up (knex) {
WIKI.logger.info('Running 3.0.0 database migration...') WIKI.logger.info('Running 3.0.0 database migration...')
// ===================================== // =====================================
...@@ -778,4 +778,4 @@ exports.up = async knex => { ...@@ -778,4 +778,4 @@ exports.up = async knex => {
WIKI.logger.info('Completed 3.0.0 database migration.') WIKI.logger.info('Completed 3.0.0 database migration.')
} }
exports.down = knex => { } export function down (knex) { }
const path = require('path') import path from 'node:path'
const fs = require('fs-extra') import fse from 'fs-extra'
const semver = require('semver') import semver from 'semver'
const baseMigrationPath = path.join(WIKI.SERVERPATH, 'db/migrations') export default {
module.exports = {
/** /**
* Gets the migration names * Gets the migration names
* @returns Promise<string[]> * @returns Promise<string[]>
*/ */
async getMigrations() { async getMigrations() {
const migrationFiles = await fs.readdir(baseMigrationPath) const baseMigrationPath = path.join(WIKI.SERVERPATH, 'db/migrations')
return migrationFiles.map(m => m.replace('.js', '')).sort(semver.compare).map(m => ({ const migrationFiles = await fse.readdir(baseMigrationPath)
return migrationFiles.map(m => m.replace('.mjs', '')).sort(semver.compare).map(m => ({
file: m, file: m,
directory: baseMigrationPath directory: baseMigrationPath
})) }))
}, },
getMigrationName(migration) { getMigrationName(migration) {
return migration.file.indexOf('.js') >= 0 ? migration.file : `${migration.file}.js` return migration.file.indexOf('.mjs') >= 0 ? migration.file : `${migration.file}.mjs`
}, },
getMigration(migration) { async getMigration(migration) {
return require(path.join(baseMigrationPath, migration.file)) return import(path.join(WIKI.SERVERPATH, 'db/migrations', `${migration.file}.mjs`))
} }
} }
const _ = require('lodash') import { merge } from 'lodash-es'
const fs = require('fs') import fs from 'node:fs/promises'
const path = require('path') import path from 'node:path'
const autoload = require('auto-load') import { makeExecutableSchema } from '@graphql-tools/schema'
const { makeExecutableSchema } = require('@graphql-tools/schema') import { defaultKeyGenerator, rateLimitDirective } from 'graphql-rate-limit-directive'
const { defaultKeyGenerator, rateLimitDirective } = require('graphql-rate-limit-directive') import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'
const { GraphQLUpload } = require('graphql-upload')
// Rate Limiter import DateScalar from './scalars/date.mjs'
import JSONScalar from './scalars/json.mjs'
import UUIDScalar from './scalars/uuid.mjs'
const { rateLimitDirectiveTypeDefs, rateLimitDirectiveTransformer } = rateLimitDirective({ export async function initSchema () {
// Rate Limiter
const { rateLimitDirectiveTypeDefs, rateLimitDirectiveTransformer } = rateLimitDirective({
keyGenerator: (directiveArgs, source, args, context, info) => `${context.req.ip}:${defaultKeyGenerator(directiveArgs, source, args, context, info)}` keyGenerator: (directiveArgs, source, args, context, info) => `${context.req.ip}:${defaultKeyGenerator(directiveArgs, source, args, context, info)}`
}) })
// Schemas // Schemas
WIKI.logger.info(`Loading GraphQL Schema...`) WIKI.logger.info(`Loading GraphQL Schema...`)
const typeDefs = [ const typeDefs = [
rateLimitDirectiveTypeDefs rateLimitDirectiveTypeDefs
] ]
const schemas = fs.readdirSync(path.join(WIKI.SERVERPATH, 'graph/schemas')) const schemaList = await fs.readdir(path.join(WIKI.SERVERPATH, 'graph/schemas'))
schemas.forEach(schema => { for (const schemaFile of schemaList) {
typeDefs.push(fs.readFileSync(path.join(WIKI.SERVERPATH, `graph/schemas/${schema}`), 'utf8')) typeDefs.push(await fs.readFile(path.join(WIKI.SERVERPATH, `graph/schemas/${schemaFile}`), 'utf8'))
}) }
// Resolvers // Resolvers
WIKI.logger.info(`Loading GraphQL Resolvers...`) WIKI.logger.info(`Loading GraphQL Resolvers...`)
let resolvers = { let resolvers = {
Date: require('./scalars/date'), Date: DateScalar,
JSON: require('./scalars/json'), JSON: JSONScalar,
UUID: require('./scalars/uuid'), UUID: UUIDScalar,
Upload: GraphQLUpload Upload: GraphQLUpload
} }
const resolversObj = _.values(autoload(path.join(WIKI.SERVERPATH, 'graph/resolvers')))
resolversObj.forEach(resolver => {
_.merge(resolvers, resolver)
})
// Make executable schema const resolverList = await fs.readdir(path.join(WIKI.SERVERPATH, 'graph/resolvers'))
for (const resolverFile of resolverList) {
const resolver = (await import(path.join(WIKI.SERVERPATH, 'graph/resolvers', resolverFile))).default
merge(resolvers, resolver)
}
WIKI.logger.info(`Compiling GraphQL Schema...`) // Make executable schema
let schema = makeExecutableSchema({
WIKI.logger.info(`Compiling GraphQL Schema...`)
let schema = makeExecutableSchema({
typeDefs, typeDefs,
resolvers resolvers
}) })
// Apply schema transforms // Apply schema transforms
schema = rateLimitDirectiveTransformer(schema) schema = rateLimitDirectiveTransformer(schema)
WIKI.logger.info(`GraphQL Schema: [ OK ]`) WIKI.logger.info(`GraphQL Schema: [ OK ]`)
module.exports = schema return schema
}
const _ = require('lodash') import { find, get, reduce, set, sortBy, transform } from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Query: { Query: {
async analyticsProviders(obj, args, context, info) { async analyticsProviders(obj, args, context, info) {
let providers = await WIKI.db.analytics.getProviders(args.isEnabled) let providers = await WIKI.db.analytics.getProviders(args.isEnabled)
providers = providers.map(stg => { providers = providers.map(stg => {
const providerInfo = _.find(WIKI.data.analytics, ['key', stg.key]) || {} const providerInfo = find(WIKI.data.analytics, ['key', stg.key]) || {}
return { return {
...providerInfo, ...providerInfo,
...stg, ...stg,
config: _.sortBy(_.transform(stg.config, (res, value, key) => { config: sortBy(transform(stg.config, (res, value, key) => {
const configData = _.get(providerInfo.props, key, {}) const configData = get(providerInfo.props, key, {})
res.push({ res.push({
key, key,
value: JSON.stringify({ value: JSON.stringify({
...@@ -31,18 +31,18 @@ module.exports = { ...@@ -31,18 +31,18 @@ module.exports = {
for (let str of args.providers) { for (let str of args.providers) {
await WIKI.db.analytics.query().patch({ await WIKI.db.analytics.query().patch({
isEnabled: str.isEnabled, isEnabled: str.isEnabled,
config: _.reduce(str.config, (result, value, key) => { config: reduce(str.config, (result, value, key) => {
_.set(result, `${value.key}`, _.get(JSON.parse(value.value), 'v', null)) set(result, `${value.key}`, get(JSON.parse(value.value), 'v', null))
return result return result
}, {}) }, {})
}).where('key', str.key) }).where('key', str.key)
await WIKI.cache.del('analytics') await WIKI.cache.del('analytics')
} }
return { return {
responseResult: graphHelper.generateSuccess('Providers updated successfully') responseResult: generateSuccess('Providers updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const _ = require('lodash') import _ from 'lodash-es'
const sanitize = require('sanitize-filename') import sanitize from 'sanitize-filename'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const path = require('node:path') import path from 'node:path'
const fs = require('fs-extra') import fs from 'fs-extra'
const { v4: uuid } = require('uuid') import { v4 as uuid } from 'uuid'
const { pipeline } = require('node:stream/promises') import { pipeline } from 'node:stream/promises'
module.exports = { export default {
Query: { Query: {
async assetById(obj, args, context) { async assetById(obj, args, context) {
return null return null
...@@ -83,13 +83,13 @@ module.exports = { ...@@ -83,13 +83,13 @@ module.exports = {
}) })
return { return {
responseResult: graphHelper.generateSuccess('Asset has been renamed successfully.') responseResult: generateSuccess('Asset has been renamed successfully.')
} }
} else { } else {
throw new WIKI.Error.AssetInvalid() throw new WIKI.Error.AssetInvalid()
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -122,13 +122,13 @@ module.exports = { ...@@ -122,13 +122,13 @@ module.exports = {
}) })
return { return {
responseResult: graphHelper.generateSuccess('Asset has been deleted successfully.') responseResult: generateSuccess('Asset has been deleted successfully.')
} }
} else { } else {
throw new WIKI.Error.AssetInvalid() throw new WIKI.Error.AssetInvalid()
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -357,13 +357,13 @@ module.exports = { ...@@ -357,13 +357,13 @@ module.exports = {
} else { } else {
WIKI.logger.debug('Asset(s) uploaded successfully.') WIKI.logger.debug('Asset(s) uploaded successfully.')
return { return {
operation: graphHelper.generateSuccess('Asset(s) uploaded successfully') operation: generateSuccess('Asset(s) uploaded successfully')
} }
} }
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -373,10 +373,10 @@ module.exports = { ...@@ -373,10 +373,10 @@ module.exports = {
try { try {
await WIKI.db.assets.flushTempUploads() await WIKI.db.assets.flushTempUploads()
return { return {
responseResult: graphHelper.generateSuccess('Temporary Uploads have been flushed successfully.') responseResult: generateSuccess('Temporary Uploads have been flushed successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Query: { Query: {
/** /**
* List of API Keys * List of API Keys
...@@ -67,11 +67,11 @@ module.exports = { ...@@ -67,11 +67,11 @@ module.exports = {
WIKI.events.outbound.emit('reloadApiKeys') WIKI.events.outbound.emit('reloadApiKeys')
return { return {
key, key,
operation: graphHelper.generateSuccess('API Key created successfully') operation: generateSuccess('API Key created successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -82,7 +82,7 @@ module.exports = { ...@@ -82,7 +82,7 @@ module.exports = {
const authResult = await WIKI.db.users.login(args, context) const authResult = await WIKI.db.users.login(args, context)
return { return {
...authResult, ...authResult,
operation: graphHelper.generateSuccess('Login success') operation: generateSuccess('Login success')
} }
} catch (err) { } catch (err) {
// LDAP Debug Flag // LDAP Debug Flag
...@@ -91,7 +91,7 @@ module.exports = { ...@@ -91,7 +91,7 @@ module.exports = {
} }
console.error(err) console.error(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -102,10 +102,10 @@ module.exports = { ...@@ -102,10 +102,10 @@ module.exports = {
const authResult = await WIKI.db.users.loginTFA(args, context) const authResult = await WIKI.db.users.loginTFA(args, context)
return { return {
...authResult, ...authResult,
responseResult: graphHelper.generateSuccess('TFA success') responseResult: generateSuccess('TFA success')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -116,10 +116,10 @@ module.exports = { ...@@ -116,10 +116,10 @@ module.exports = {
const authResult = await WIKI.db.users.loginChangePassword(args, context) const authResult = await WIKI.db.users.loginChangePassword(args, context)
return { return {
...authResult, ...authResult,
responseResult: graphHelper.generateSuccess('Password changed successfully') responseResult: generateSuccess('Password changed successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -129,10 +129,10 @@ module.exports = { ...@@ -129,10 +129,10 @@ module.exports = {
try { try {
await WIKI.db.users.loginForgotPassword(args, context) await WIKI.db.users.loginForgotPassword(args, context)
return { return {
responseResult: graphHelper.generateSuccess('Password reset request processed.') responseResult: generateSuccess('Password reset request processed.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -142,10 +142,10 @@ module.exports = { ...@@ -142,10 +142,10 @@ module.exports = {
try { try {
await WIKI.db.users.register({ ...args, verify: true }, context) await WIKI.db.users.register({ ...args, verify: true }, context)
return { return {
responseResult: graphHelper.generateSuccess('Registration success') responseResult: generateSuccess('Registration success')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -156,10 +156,10 @@ module.exports = { ...@@ -156,10 +156,10 @@ module.exports = {
WIKI.config.api.isEnabled = args.enabled WIKI.config.api.isEnabled = args.enabled
await WIKI.configSvc.saveToDb(['api']) await WIKI.configSvc.saveToDb(['api'])
return { return {
operation: graphHelper.generateSuccess('API State changed successfully') operation: generateSuccess('API State changed successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -173,10 +173,10 @@ module.exports = { ...@@ -173,10 +173,10 @@ module.exports = {
await WIKI.auth.reloadApiKeys() await WIKI.auth.reloadApiKeys()
WIKI.events.outbound.emit('reloadApiKeys') WIKI.events.outbound.emit('reloadApiKeys')
return { return {
operation: graphHelper.generateSuccess('API Key revoked successfully') operation: generateSuccess('API Key revoked successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -226,10 +226,10 @@ module.exports = { ...@@ -226,10 +226,10 @@ module.exports = {
await WIKI.auth.activateStrategies() await WIKI.auth.activateStrategies()
WIKI.events.outbound.emit('reloadAuthStrategies') WIKI.events.outbound.emit('reloadAuthStrategies')
return { return {
responseResult: graphHelper.generateSuccess('Strategies updated successfully') responseResult: generateSuccess('Strategies updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -239,10 +239,10 @@ module.exports = { ...@@ -239,10 +239,10 @@ module.exports = {
try { try {
await WIKI.auth.regenerateCertificates() await WIKI.auth.regenerateCertificates()
return { return {
responseResult: graphHelper.generateSuccess('Certificates have been regenerated successfully.') responseResult: generateSuccess('Certificates have been regenerated successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -252,10 +252,10 @@ module.exports = { ...@@ -252,10 +252,10 @@ module.exports = {
try { try {
await WIKI.auth.resetGuestUser() await WIKI.auth.resetGuestUser()
return { return {
responseResult: graphHelper.generateSuccess('Guest user has been reset successfully.') responseResult: generateSuccess('Guest user has been reset successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
}, },
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Query: { Query: {
/** /**
* Fetch list of Comments Providers * Fetch list of Comments Providers
...@@ -90,11 +90,11 @@ module.exports = { ...@@ -90,11 +90,11 @@ module.exports = {
ip: context.req.ip ip: context.req.ip
}) })
return { return {
responseResult: graphHelper.generateSuccess('New comment posted successfully'), responseResult: generateSuccess('New comment posted successfully'),
id: cmId id: cmId
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -108,11 +108,11 @@ module.exports = { ...@@ -108,11 +108,11 @@ module.exports = {
ip: context.req.ip ip: context.req.ip
}) })
return { return {
responseResult: graphHelper.generateSuccess('Comment updated successfully'), responseResult: generateSuccess('Comment updated successfully'),
render: cmRender render: cmRender
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -126,10 +126,10 @@ module.exports = { ...@@ -126,10 +126,10 @@ module.exports = {
ip: context.req.ip ip: context.req.ip
}) })
return { return {
responseResult: graphHelper.generateSuccess('Comment deleted successfully') responseResult: generateSuccess('Comment deleted successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -148,10 +148,10 @@ module.exports = { ...@@ -148,10 +148,10 @@ module.exports = {
} }
await WIKI.db.commentProviders.initProvider() await WIKI.db.commentProviders.initProvider()
return { return {
responseResult: graphHelper.generateSuccess('Comment Providers updated successfully') responseResult: generateSuccess('Comment Providers updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const safeRegex = require('safe-regex') import safeRegex from 'safe-regex'
const _ = require('lodash') import _ from 'lodash-es'
const { v4: uuid } = require('uuid') import { v4 as uuid } from 'uuid'
module.exports = { export default {
Query: { Query: {
/** /**
* FETCH ALL GROUPS * FETCH ALL GROUPS
...@@ -71,7 +71,7 @@ module.exports = { ...@@ -71,7 +71,7 @@ module.exports = {
WIKI.events.outbound.emit('addAuthRevoke', { id: usr.id, kind: 'u' }) WIKI.events.outbound.emit('addAuthRevoke', { id: usr.id, kind: 'u' })
return { return {
operation: graphHelper.generateSuccess('User has been assigned to group.') operation: generateSuccess('User has been assigned to group.')
} }
}, },
/** /**
...@@ -90,7 +90,7 @@ module.exports = { ...@@ -90,7 +90,7 @@ module.exports = {
await WIKI.auth.reloadGroups() await WIKI.auth.reloadGroups()
WIKI.events.outbound.emit('reloadGroups') WIKI.events.outbound.emit('reloadGroups')
return { return {
operation: graphHelper.generateSuccess('Group created successfully.'), operation: generateSuccess('Group created successfully.'),
group group
} }
}, },
...@@ -111,7 +111,7 @@ module.exports = { ...@@ -111,7 +111,7 @@ module.exports = {
WIKI.events.outbound.emit('reloadGroups') WIKI.events.outbound.emit('reloadGroups')
return { return {
operation: graphHelper.generateSuccess('Group has been deleted.') operation: generateSuccess('Group has been deleted.')
} }
}, },
/** /**
...@@ -138,7 +138,7 @@ module.exports = { ...@@ -138,7 +138,7 @@ module.exports = {
WIKI.events.outbound.emit('addAuthRevoke', { id: usr.id, kind: 'u' }) WIKI.events.outbound.emit('addAuthRevoke', { id: usr.id, kind: 'u' })
return { return {
operation: graphHelper.generateSuccess('User has been unassigned from group.') operation: generateSuccess('User has been unassigned from group.')
} }
}, },
/** /**
...@@ -193,7 +193,7 @@ module.exports = { ...@@ -193,7 +193,7 @@ module.exports = {
WIKI.events.outbound.emit('reloadGroups') WIKI.events.outbound.emit('reloadGroups')
return { return {
operation: graphHelper.generateSuccess('Group has been updated.') operation: generateSuccess('Group has been updated.')
} }
} }
}, },
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const _ = require('lodash') import _ from 'lodash-es'
module.exports = { export default {
Query: { Query: {
async hooks () { async hooks () {
return WIKI.db.hooks.query().orderBy('name') return WIKI.db.hooks.query().orderBy('name')
...@@ -31,11 +31,11 @@ module.exports = { ...@@ -31,11 +31,11 @@ module.exports = {
WIKI.logger.debug(`New Hook ${newHook.id} created successfully.`) WIKI.logger.debug(`New Hook ${newHook.id} created successfully.`)
return { return {
operation: graphHelper.generateSuccess('Hook created successfully'), operation: generateSuccess('Hook created successfully'),
hook: newHook hook: newHook
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -63,10 +63,10 @@ module.exports = { ...@@ -63,10 +63,10 @@ module.exports = {
WIKI.logger.debug(`Hook ${args.id} updated successfully.`) WIKI.logger.debug(`Hook ${args.id} updated successfully.`)
return { return {
operation: graphHelper.generateSuccess('Hook updated successfully') operation: generateSuccess('Hook updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -77,10 +77,10 @@ module.exports = { ...@@ -77,10 +77,10 @@ module.exports = {
await WIKI.db.hooks.deleteHook(args.id) await WIKI.db.hooks.deleteHook(args.id)
WIKI.logger.debug(`Hook ${args.id} deleted successfully.`) WIKI.logger.debug(`Hook ${args.id} deleted successfully.`)
return { return {
operation: graphHelper.generateSuccess('Hook deleted successfully') operation: generateSuccess('Hook deleted successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const _ = require('lodash') import _ from 'lodash-es'
module.exports = { export default {
Query: { Query: {
async locales(obj, args, context, info) { async locales(obj, args, context, info) {
let remoteLocales = await WIKI.cache.get('locales') let remoteLocales = await WIKI.cache.get('locales')
...@@ -29,10 +29,10 @@ module.exports = { ...@@ -29,10 +29,10 @@ module.exports = {
}, args.locale) }, args.locale)
await job.finished await job.finished
return { return {
responseResult: graphHelper.generateSuccess('Locale downloaded successfully') responseResult: generateSuccess('Locale downloaded successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async updateLocale(obj, args, context) { async updateLocale(obj, args, context) {
...@@ -53,10 +53,10 @@ module.exports = { ...@@ -53,10 +53,10 @@ module.exports = {
await WIKI.cache.del('nav:locales') await WIKI.cache.del('nav:locales')
return { return {
responseResult: graphHelper.generateSuccess('Locale config updated') responseResult: generateSuccess('Locale config updated')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Query: { Query: {
async mailConfig(obj, args, context, info) { async mailConfig(obj, args, context, info) {
return { return {
...@@ -28,10 +28,10 @@ module.exports = { ...@@ -28,10 +28,10 @@ module.exports = {
}) })
return { return {
operation: graphHelper.generateSuccess('Test email sent successfully.') operation: generateSuccess('Test email sent successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async updateMailConfig(obj, args, context) { async updateMailConfig(obj, args, context) {
...@@ -56,10 +56,10 @@ module.exports = { ...@@ -56,10 +56,10 @@ module.exports = {
WIKI.mail.init() WIKI.mail.init()
return { return {
operation: graphHelper.generateSuccess('Mail configuration updated successfully.') operation: generateSuccess('Mail configuration updated successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Query: { Query: {
async navigationTree (obj, args, context, info) { async navigationTree (obj, args, context, info) {
return WIKI.db.navigation.getTree({ cache: false, locale: 'all', bypassAuth: true }) return WIKI.db.navigation.getTree({ cache: false, locale: 'all', bypassAuth: true })
...@@ -20,10 +20,10 @@ module.exports = { ...@@ -20,10 +20,10 @@ module.exports = {
} }
return { return {
responseResult: graphHelper.generateSuccess('Navigation updated successfully') responseResult: generateSuccess('Navigation updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async updateNavigationConfig (obj, args, context) { async updateNavigationConfig (obj, args, context) {
...@@ -34,10 +34,10 @@ module.exports = { ...@@ -34,10 +34,10 @@ module.exports = {
await WIKI.configSvc.saveToDb(['nav']) await WIKI.configSvc.saveToDb(['nav'])
return { return {
responseResult: graphHelper.generateSuccess('Navigation config updated successfully') responseResult: generateSuccess('Navigation config updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const pageHelper = require('../../helpers/page') import { parsePath }from '../../helpers/page.mjs'
module.exports = { export default {
Query: { Query: {
/** /**
* PAGE HISTORY * PAGE HISTORY
...@@ -169,7 +169,7 @@ module.exports = { ...@@ -169,7 +169,7 @@ module.exports = {
*/ */
async pageByPath (obj, args, context, info) { async pageByPath (obj, args, context, info) {
// console.info(info) // console.info(info)
const pageArgs = pageHelper.parsePath(args.path) const pageArgs = parsePath(args.path)
const page = await WIKI.db.pages.getPageFromDb({ const page = await WIKI.db.pages.getPageFromDb({
...pageArgs, ...pageArgs,
siteId: args.siteId siteId: args.siteId
...@@ -393,11 +393,11 @@ module.exports = { ...@@ -393,11 +393,11 @@ module.exports = {
user: context.req.user user: context.req.user
}) })
return { return {
operation: graphHelper.generateSuccess('Page created successfully.'), operation: generateSuccess('Page created successfully.'),
page page
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -410,11 +410,11 @@ module.exports = { ...@@ -410,11 +410,11 @@ module.exports = {
user: context.req.user user: context.req.user
}) })
return { return {
operation: graphHelper.generateSuccess('Page has been updated.'), operation: generateSuccess('Page has been updated.'),
page page
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -427,10 +427,10 @@ module.exports = { ...@@ -427,10 +427,10 @@ module.exports = {
user: context.req.user user: context.req.user
}) })
return { return {
responseResult: graphHelper.generateSuccess('Page has been converted.') responseResult: generateSuccess('Page has been converted.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -443,10 +443,10 @@ module.exports = { ...@@ -443,10 +443,10 @@ module.exports = {
user: context.req.user user: context.req.user
}) })
return { return {
responseResult: graphHelper.generateSuccess('Page has been moved.') responseResult: generateSuccess('Page has been moved.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -459,10 +459,10 @@ module.exports = { ...@@ -459,10 +459,10 @@ module.exports = {
user: context.req.user user: context.req.user
}) })
return { return {
responseResult: graphHelper.generateSuccess('Page has been deleted.') responseResult: generateSuccess('Page has been deleted.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -478,10 +478,10 @@ module.exports = { ...@@ -478,10 +478,10 @@ module.exports = {
throw new Error('This tag does not exist.') throw new Error('This tag does not exist.')
} }
return { return {
responseResult: graphHelper.generateSuccess('Tag has been deleted.') responseResult: generateSuccess('Tag has been deleted.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -499,10 +499,10 @@ module.exports = { ...@@ -499,10 +499,10 @@ module.exports = {
throw new Error('This tag does not exist.') throw new Error('This tag does not exist.')
} }
return { return {
responseResult: graphHelper.generateSuccess('Tag has been updated successfully.') responseResult: generateSuccess('Tag has been updated successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -513,10 +513,10 @@ module.exports = { ...@@ -513,10 +513,10 @@ module.exports = {
await WIKI.db.pages.flushCache() await WIKI.db.pages.flushCache()
WIKI.events.outbound.emit('flushCache') WIKI.events.outbound.emit('flushCache')
return { return {
responseResult: graphHelper.generateSuccess('Pages Cache has been flushed successfully.') responseResult: generateSuccess('Pages Cache has been flushed successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -526,11 +526,11 @@ module.exports = { ...@@ -526,11 +526,11 @@ module.exports = {
try { try {
const count = await WIKI.db.pages.migrateToLocale(args) const count = await WIKI.db.pages.migrateToLocale(args)
return { return {
responseResult: graphHelper.generateSuccess('Migrated content to target locale successfully.'), responseResult: generateSuccess('Migrated content to target locale successfully.'),
count count
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -540,10 +540,10 @@ module.exports = { ...@@ -540,10 +540,10 @@ module.exports = {
try { try {
await WIKI.db.pages.rebuildTree() await WIKI.db.pages.rebuildTree()
return { return {
responseResult: graphHelper.generateSuccess('Page tree rebuilt successfully.') responseResult: generateSuccess('Page tree rebuilt successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -557,10 +557,10 @@ module.exports = { ...@@ -557,10 +557,10 @@ module.exports = {
} }
await WIKI.db.pages.renderPage(page) await WIKI.db.pages.renderPage(page)
return { return {
responseResult: graphHelper.generateSuccess('Page rendered successfully.') responseResult: generateSuccess('Page rendered successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -593,10 +593,10 @@ module.exports = { ...@@ -593,10 +593,10 @@ module.exports = {
}) })
return { return {
responseResult: graphHelper.generateSuccess('Page version restored successfully.') responseResult: generateSuccess('Page version restored successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -606,10 +606,10 @@ module.exports = { ...@@ -606,10 +606,10 @@ module.exports = {
try { try {
await WIKI.db.pageHistory.purge(args.olderThan) await WIKI.db.pageHistory.purge(args.olderThan)
return { return {
responseResult: graphHelper.generateSuccess('Page history purged successfully.') responseResult: generateSuccess('Page history purged successfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
}, },
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Query: { Query: {
async renderers(obj, args, context, info) { async renderers(obj, args, context, info) {
let renderers = await WIKI.db.renderers.getRenderers() let renderers = await WIKI.db.renderers.getRenderers()
...@@ -42,10 +42,10 @@ module.exports = { ...@@ -42,10 +42,10 @@ module.exports = {
}).where('key', rdr.key) }).where('key', rdr.key)
} }
return { return {
responseResult: graphHelper.generateSuccess('Renderers updated successfully') responseResult: generateSuccess('Renderers updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { export default {
Mutation: { Mutation: {
async rebuildSearchIndex (obj, args, context) { async rebuildSearchIndex (obj, args, context) {
try { try {
await WIKI.data.searchEngine.rebuild() await WIKI.data.searchEngine.rebuild()
return { return {
responseResult: graphHelper.generateSuccess('Index rebuilt successfully') responseResult: generateSuccess('Index rebuilt successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const _ = require('lodash') import _ from 'lodash-es'
const CleanCSS = require('clean-css') import CleanCSS from 'clean-css'
const path = require('path') import path from 'node:path'
const fs = require('fs-extra') import fs from 'fs-extra'
const { v4: uuid } = require('uuid') import { v4 as uuid } from 'uuid'
module.exports = { export default {
Query: { Query: {
async sites () { async sites () {
const sites = await WIKI.db.sites.query().orderBy('hostname') const sites = await WIKI.db.sites.query().orderBy('hostname')
...@@ -72,12 +72,12 @@ module.exports = { ...@@ -72,12 +72,12 @@ module.exports = {
title: args.title title: args.title
}) })
return { return {
operation: graphHelper.generateSuccess('Site created successfully'), operation: generateSuccess('Site created successfully'),
site: newSite site: newSite
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -117,11 +117,11 @@ module.exports = { ...@@ -117,11 +117,11 @@ module.exports = {
}) })
return { return {
operation: graphHelper.generateSuccess('Site updated successfully') operation: generateSuccess('Site updated successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -137,11 +137,11 @@ module.exports = { ...@@ -137,11 +137,11 @@ module.exports = {
// -> Delete site // -> Delete site
await WIKI.db.sites.deleteSite(args.id) await WIKI.db.sites.deleteSite(args.id)
return { return {
operation: graphHelper.generateSuccess('Site deleted successfully') operation: generateSuccess('Site deleted successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -197,11 +197,11 @@ module.exports = { ...@@ -197,11 +197,11 @@ module.exports = {
}).onConflict('id').merge() }).onConflict('id').merge()
WIKI.logger.info('New site logo processed successfully.') WIKI.logger.info('New site logo processed successfully.')
return { return {
operation: graphHelper.generateSuccess('Site logo uploaded successfully') operation: generateSuccess('Site logo uploaded successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -258,11 +258,11 @@ module.exports = { ...@@ -258,11 +258,11 @@ module.exports = {
}).onConflict('id').merge() }).onConflict('id').merge()
WIKI.logger.info('New site favicon processed successfully.') WIKI.logger.info('New site favicon processed successfully.')
return { return {
operation: graphHelper.generateSuccess('Site favicon uploaded successfully') operation: generateSuccess('Site favicon uploaded successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -316,11 +316,11 @@ module.exports = { ...@@ -316,11 +316,11 @@ module.exports = {
}).onConflict('id').merge() }).onConflict('id').merge()
WIKI.logger.info('New site login bg processed successfully.') WIKI.logger.info('New site login bg processed successfully.')
return { return {
operation: graphHelper.generateSuccess('Site login bg uploaded successfully') operation: generateSuccess('Site login bg uploaded successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const { v4: uuid } = require('uuid') import { v4 as uuid } from 'uuid'
module.exports = { export default {
Query: { Query: {
async storageTargets (obj, args, context, info) { async storageTargets (obj, args, context, info) {
const dbTargets = await WIKI.db.storage.getTargets({ siteId: args.siteId }) const dbTargets = await WIKI.db.storage.getTargets({ siteId: args.siteId })
...@@ -164,10 +164,10 @@ module.exports = { ...@@ -164,10 +164,10 @@ module.exports = {
} }
// await WIKI.db.storage.initTargets() // await WIKI.db.storage.initTargets()
return { return {
status: graphHelper.generateSuccess('Storage targets updated successfully') status: generateSuccess('Storage targets updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async setupStorageTarget (obj, args, context) { async setupStorageTarget (obj, args, context) {
...@@ -186,11 +186,11 @@ module.exports = { ...@@ -186,11 +186,11 @@ module.exports = {
const result = await WIKI.storage.modules[md.key].setup(args.targetId, args.state) const result = await WIKI.storage.modules[md.key].setup(args.targetId, args.state)
return { return {
status: graphHelper.generateSuccess('Storage target setup step succeeded'), status: generateSuccess('Storage target setup step succeeded'),
state: result state: result
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async destroyStorageTargetSetup (obj, args, context) { async destroyStorageTargetSetup (obj, args, context) {
...@@ -209,20 +209,20 @@ module.exports = { ...@@ -209,20 +209,20 @@ module.exports = {
await WIKI.storage.modules[md.key].setupDestroy(args.targetId) await WIKI.storage.modules[md.key].setupDestroy(args.targetId)
return { return {
status: graphHelper.generateSuccess('Storage target setup configuration destroyed succesfully.') status: generateSuccess('Storage target setup configuration destroyed succesfully.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async executeStorageAction (obj, args, context) { async executeStorageAction (obj, args, context) {
try { try {
await WIKI.db.storage.executeAction(args.targetKey, args.handler) await WIKI.db.storage.executeAction(args.targetKey, args.handler)
return { return {
status: graphHelper.generateSuccess('Action completed.') status: generateSuccess('Action completed.')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
} }
} }
......
const _ = require('lodash') import _ from 'lodash-es'
const util = require('node:util') import util from 'node:util'
const getos = util.promisify(require('getos')) import getosSync from 'getos'
const os = require('node:os') import os from 'node:os'
const filesize = require('filesize') import filesize from 'filesize'
const path = require('path') import path from 'node:path'
const fs = require('fs-extra') import fs from 'fs-extra'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
module.exports = { const getos = util.promisify(getosSync)
export default {
Query: { Query: {
systemFlags () { systemFlags () {
return WIKI.config.flags return WIKI.config.flags
...@@ -89,18 +91,18 @@ module.exports = { ...@@ -89,18 +91,18 @@ module.exports = {
throw new Error('Job has already entered active state or does not exist.') throw new Error('Job has already entered active state or does not exist.')
} }
return { return {
operation: graphHelper.generateSuccess('Cancelled job successfully.') operation: generateSuccess('Cancelled job successfully.')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async disconnectWS (obj, args, context) { async disconnectWS (obj, args, context) {
WIKI.servers.ws.disconnectSockets(true) WIKI.servers.ws.disconnectSockets(true)
WIKI.logger.info('All active websocket connections have been terminated.') WIKI.logger.info('All active websocket connections have been terminated.')
return { return {
operation: graphHelper.generateSuccess('All websocket connections closed successfully.') operation: generateSuccess('All websocket connections closed successfully.')
} }
}, },
async installExtension (obj, args, context) { async installExtension (obj, args, context) {
...@@ -108,10 +110,10 @@ module.exports = { ...@@ -108,10 +110,10 @@ module.exports = {
await WIKI.extensions.ext[args.key].install() await WIKI.extensions.ext[args.key].install()
// TODO: broadcast ext install // TODO: broadcast ext install
return { return {
operation: graphHelper.generateSuccess('Extension installed successfully') operation: generateSuccess('Extension installed successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async retryJob (obj, args, context) { async retryJob (obj, args, context) {
...@@ -140,11 +142,11 @@ module.exports = { ...@@ -140,11 +142,11 @@ module.exports = {
}) })
WIKI.logger.info(`Job ${args.id} has been rescheduled [ OK ]`) WIKI.logger.info(`Job ${args.id} has been rescheduled [ OK ]`)
return { return {
operation: graphHelper.generateSuccess('Job rescheduled successfully.') operation: generateSuccess('Job rescheduled successfully.')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async updateSystemFlags (obj, args, context) { async updateSystemFlags (obj, args, context) {
...@@ -155,7 +157,7 @@ module.exports = { ...@@ -155,7 +157,7 @@ module.exports = {
await WIKI.configSvc.applyFlags() await WIKI.configSvc.applyFlags()
await WIKI.configSvc.saveToDb(['flags']) await WIKI.configSvc.saveToDb(['flags'])
return { return {
operation: graphHelper.generateSuccess('System Flags applied successfully') operation: generateSuccess('System Flags applied successfully')
} }
}, },
async updateSystemSecurity (obj, args, context) { async updateSystemSecurity (obj, args, context) {
...@@ -163,7 +165,7 @@ module.exports = { ...@@ -163,7 +165,7 @@ module.exports = {
// TODO: broadcast config update // TODO: broadcast config update
await WIKI.configSvc.saveToDb(['security']) await WIKI.configSvc.saveToDb(['security'])
return { return {
operation: graphHelper.generateSuccess('System Security configuration applied successfully') operation: generateSuccess('System Security configuration applied successfully')
} }
} }
}, },
......
const _ = require('lodash') import _ from 'lodash-es'
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const typeResolvers = { const typeResolvers = {
folder: 'TreeItemFolder', folder: 'TreeItemFolder',
...@@ -10,7 +10,7 @@ const typeResolvers = { ...@@ -10,7 +10,7 @@ const typeResolvers = {
const rePathName = /^[a-z0-9-]+$/ const rePathName = /^[a-z0-9-]+$/
const reTitle = /^[^<>"]+$/ const reTitle = /^[^<>"]+$/
module.exports = { export default {
Query: { Query: {
/** /**
* FETCH TREE * FETCH TREE
...@@ -162,11 +162,11 @@ module.exports = { ...@@ -162,11 +162,11 @@ module.exports = {
await WIKI.db.tree.createFolder(args) await WIKI.db.tree.createFolder(args)
return { return {
operation: graphHelper.generateSuccess('Folder created successfully') operation: generateSuccess('Folder created successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.debug(`Failed to create folder: ${err.message}`) WIKI.logger.debug(`Failed to create folder: ${err.message}`)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -177,11 +177,11 @@ module.exports = { ...@@ -177,11 +177,11 @@ module.exports = {
await WIKI.db.tree.renameFolder(args) await WIKI.db.tree.renameFolder(args)
return { return {
operation: graphHelper.generateSuccess('Folder renamed successfully') operation: generateSuccess('Folder renamed successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.debug(`Failed to rename folder ${args.folderId}: ${err.message}`) WIKI.logger.debug(`Failed to rename folder ${args.folderId}: ${err.message}`)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -192,11 +192,11 @@ module.exports = { ...@@ -192,11 +192,11 @@ module.exports = {
await WIKI.db.tree.deleteFolder(args.folderId) await WIKI.db.tree.deleteFolder(args.folderId)
return { return {
operation: graphHelper.generateSuccess('Folder deleted successfully') operation: generateSuccess('Folder deleted successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.debug(`Failed to delete folder ${args.folderId}: ${err.message}`) WIKI.logger.debug(`Failed to delete folder ${args.folderId}: ${err.message}`)
return graphHelper.generateError(err) return generateError(err)
} }
} }
}, },
......
const graphHelper = require('../../helpers/graph') import { generateError, generateSuccess } from '../../helpers/graph.mjs'
const _ = require('lodash') import _ from 'lodash-es'
const path = require('node:path') import path from 'node:path'
const fs = require('fs-extra') import fs from 'fs-extra'
module.exports = { export default {
Query: { Query: {
/** /**
* FETCH ALL USERS * FETCH ALL USERS
...@@ -93,10 +93,10 @@ module.exports = { ...@@ -93,10 +93,10 @@ module.exports = {
await WIKI.db.users.createNewUser({ ...args, passwordRaw: args.password, isVerified: true }) await WIKI.db.users.createNewUser({ ...args, passwordRaw: args.password, isVerified: true })
return { return {
operation: graphHelper.generateSuccess('User created successfully') operation: generateSuccess('User created successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async deleteUser (obj, args) { async deleteUser (obj, args) {
...@@ -110,13 +110,13 @@ module.exports = { ...@@ -110,13 +110,13 @@ module.exports = {
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' }) WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' })
return { return {
operation: graphHelper.generateSuccess('User deleted successfully') operation: generateSuccess('User deleted successfully')
} }
} catch (err) { } catch (err) {
if (err.message.indexOf('foreign') >= 0) { if (err.message.indexOf('foreign') >= 0) {
return graphHelper.generateError(new WIKI.Error.UserDeleteForeignConstraint()) return generateError(new WIKI.Error.UserDeleteForeignConstraint())
} else { } else {
return graphHelper.generateError(err) return generateError(err)
} }
} }
}, },
...@@ -125,10 +125,10 @@ module.exports = { ...@@ -125,10 +125,10 @@ module.exports = {
await WIKI.db.users.updateUser(args.id, args.patch) await WIKI.db.users.updateUser(args.id, args.patch)
return { return {
operation: graphHelper.generateSuccess('User updated successfully') operation: generateSuccess('User updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async verifyUser (obj, args) { async verifyUser (obj, args) {
...@@ -136,10 +136,10 @@ module.exports = { ...@@ -136,10 +136,10 @@ module.exports = {
await WIKI.db.users.query().patch({ isVerified: true }).findById(args.id) await WIKI.db.users.query().patch({ isVerified: true }).findById(args.id)
return { return {
operation: graphHelper.generateSuccess('User verified successfully') operation: generateSuccess('User verified successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async activateUser (obj, args) { async activateUser (obj, args) {
...@@ -147,10 +147,10 @@ module.exports = { ...@@ -147,10 +147,10 @@ module.exports = {
await WIKI.db.users.query().patch({ isActive: true }).findById(args.id) await WIKI.db.users.query().patch({ isActive: true }).findById(args.id)
return { return {
operation: graphHelper.generateSuccess('User activated successfully') operation: generateSuccess('User activated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async deactivateUser (obj, args) { async deactivateUser (obj, args) {
...@@ -164,10 +164,10 @@ module.exports = { ...@@ -164,10 +164,10 @@ module.exports = {
WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' }) WIKI.events.outbound.emit('addAuthRevoke', { id: args.id, kind: 'u' })
return { return {
operation: graphHelper.generateSuccess('User deactivated successfully') operation: generateSuccess('User deactivated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async enableUserTFA (obj, args) { async enableUserTFA (obj, args) {
...@@ -175,10 +175,10 @@ module.exports = { ...@@ -175,10 +175,10 @@ module.exports = {
await WIKI.db.users.query().patch({ tfaIsActive: true, tfaSecret: null }).findById(args.id) await WIKI.db.users.query().patch({ tfaIsActive: true, tfaSecret: null }).findById(args.id)
return { return {
operation: graphHelper.generateSuccess('User 2FA enabled successfully') operation: generateSuccess('User 2FA enabled successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async disableUserTFA (obj, args) { async disableUserTFA (obj, args) {
...@@ -186,10 +186,10 @@ module.exports = { ...@@ -186,10 +186,10 @@ module.exports = {
await WIKI.db.users.query().patch({ tfaIsActive: false, tfaSecret: null }).findById(args.id) await WIKI.db.users.query().patch({ tfaIsActive: false, tfaSecret: null }).findById(args.id)
return { return {
operation: graphHelper.generateSuccess('User 2FA disabled successfully') operation: generateSuccess('User 2FA disabled successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
resetUserPassword (obj, args) { resetUserPassword (obj, args) {
...@@ -235,10 +235,10 @@ module.exports = { ...@@ -235,10 +235,10 @@ module.exports = {
}) })
return { return {
operation: graphHelper.generateSuccess('User profile updated successfully') operation: generateSuccess('User profile updated successfully')
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
async changePassword (obj, args, context) { async changePassword (obj, args, context) {
...@@ -270,11 +270,11 @@ module.exports = { ...@@ -270,11 +270,11 @@ module.exports = {
const newToken = await WIKI.db.users.refreshToken(usr) const newToken = await WIKI.db.users.refreshToken(usr)
return { return {
responseResult: graphHelper.generateSuccess('Password changed successfully'), responseResult: generateSuccess('Password changed successfully'),
jwt: newToken.token jwt: newToken.token
} }
} catch (err) { } catch (err) {
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -316,11 +316,11 @@ module.exports = { ...@@ -316,11 +316,11 @@ module.exports = {
}).onConflict('id').merge() }).onConflict('id').merge()
WIKI.logger.debug(`Processed user ${args.id} avatar successfully.`) WIKI.logger.debug(`Processed user ${args.id} avatar successfully.`)
return { return {
operation: graphHelper.generateSuccess('User avatar uploaded successfully') operation: generateSuccess('User avatar uploaded successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
}, },
/** /**
...@@ -333,11 +333,11 @@ module.exports = { ...@@ -333,11 +333,11 @@ module.exports = {
await WIKI.db.knex('userAvatars').where({ id: args.id }).del() await WIKI.db.knex('userAvatars').where({ id: args.id }).del()
WIKI.logger.debug(`Cleared user ${args.id} avatar successfully.`) WIKI.logger.debug(`Cleared user ${args.id} avatar successfully.`)
return { return {
operation: graphHelper.generateSuccess('User avatar cleared successfully') operation: generateSuccess('User avatar cleared successfully')
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
return graphHelper.generateError(err) return generateError(err)
} }
} }
}, },
......
const gql = require('graphql') import gql from 'graphql'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
function parseDateTime (value) { function parseDateTime (value) {
const nDate = DateTime.fromISO(value) const nDate = DateTime.fromISO(value)
return nDate.isValid ? nDate : null return nDate.isValid ? nDate : null
} }
module.exports = new gql.GraphQLScalarType({ export default new gql.GraphQLScalarType({
name: 'Date', name: 'Date',
description: 'ISO date-time string at UTC', description: 'ISO date-time string at UTC',
parseValue(value) { parseValue(value) {
......
const { Kind, GraphQLScalarType } = require('graphql') import { Kind, GraphQLScalarType } from 'graphql'
function ensureObject (value) { function ensureObject (value) {
if (typeof value !== 'object' || value === null || Array.isArray(value)) { if (typeof value !== 'object' || value === null || Array.isArray(value)) {
...@@ -39,7 +39,7 @@ function parseObject (typeName, ast, variables) { ...@@ -39,7 +39,7 @@ function parseObject (typeName, ast, variables) {
return value return value
} }
module.exports = new GraphQLScalarType({ export default new GraphQLScalarType({
name: 'JSON', name: 'JSON',
description: description:
'The `JSON` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).', 'The `JSON` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).',
......
const { Kind, GraphQLScalarType } = require('graphql') import { Kind, GraphQLScalarType } from 'graphql'
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
const nilUUID = '00000000-0000-0000-0000-000000000000' const nilUUID = '00000000-0000-0000-0000-000000000000'
...@@ -7,7 +7,7 @@ function isUUID (value) { ...@@ -7,7 +7,7 @@ function isUUID (value) {
return uuidRegex.test(value) || nilUUID === value return uuidRegex.test(value) || nilUUID === value
} }
module.exports = new GraphQLScalarType({ export default new GraphQLScalarType({
name: 'UUID', name: 'UUID',
description: 'The `UUID` scalar type represents UUID values as specified by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122).', description: 'The `UUID` scalar type represents UUID values as specified by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122).',
serialize: (value) => { serialize: (value) => {
......
const AbstractClientStore = require('express-brute/lib/AbstractClientStore') import AbstractClientStore from 'express-brute/lib/AbstractClientStore.js'
const KnexStore = module.exports = function (options) { const KnexStore = function (options) {
options = options || Object.create(null) options = options || Object.create(null)
AbstractClientStore.apply(this, arguments) AbstractClientStore.apply(this, arguments)
...@@ -146,3 +146,5 @@ KnexStore.defaultsKnex = { ...@@ -146,3 +146,5 @@ KnexStore.defaultsKnex = {
filename: './brute-knex.sqlite' filename: './brute-knex.sqlite'
} }
} }
export default KnexStore
const _ = require('lodash') import { isNil, isPlainObject, set, startCase, transform } from 'lodash-es'
const crypto = require('node:crypto') import crypto from 'node:crypto'
module.exports = { /* eslint-disable promise/param-names */
/* eslint-disable promise/param-names */ export function createDeferred () {
createDeferred () {
let result, resolve, reject let result, resolve, reject
return { return {
resolve: function (value) { resolve: function (value) {
...@@ -29,41 +28,45 @@ module.exports = { ...@@ -29,41 +28,45 @@ module.exports = {
} }
}) })
} }
}, }
/**
/**
* Decode a tree path * Decode a tree path
* *
* @param {string} str String to decode * @param {string} str String to decode
* @returns Decoded tree path * @returns Decoded tree path
*/ */
decodeTreePath (str) { export function decodeTreePath (str) {
return str.replaceAll('_', '-').replaceAll('.', '/') return str.replaceAll('_', '-').replaceAll('.', '/')
}, }
/**
/**
* Encode a tree path * Encode a tree path
* *
* @param {string} str String to encode * @param {string} str String to encode
* @returns Encoded tree path * @returns Encoded tree path
*/ */
encodeTreePath (str) { export function encodeTreePath (str) {
return str?.toLowerCase()?.replaceAll('-', '_')?.replaceAll('/', '.') || '' return str?.toLowerCase()?.replaceAll('-', '_')?.replaceAll('/', '.') || ''
}, }
/**
/**
* Generate SHA-1 Hash of a string * Generate SHA-1 Hash of a string
* *
* @param {string} str String to hash * @param {string} str String to hash
* @returns Hashed string * @returns Hashed string
*/ */
generateHash (str) { export function generateHash (str) {
return crypto.createHash('sha1').update(str).digest('hex') return crypto.createHash('sha1').update(str).digest('hex')
}, }
/**
/**
* Get default value of type * Get default value of type
* *
* @param {any} type primitive type name * @param {any} type primitive type name
* @returns Default value * @returns Default value
*/ */
getTypeDefaultValue (type) { export function getTypeDefaultValue (type) {
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case 'string': case 'string':
return '' return ''
...@@ -72,19 +75,20 @@ module.exports = { ...@@ -72,19 +75,20 @@ module.exports = {
case 'boolean': case 'boolean':
return false return false
} }
}, }
parseModuleProps (props) {
return _.transform(props, (result, value, key) => { export function parseModuleProps (props) {
return transform(props, (result, value, key) => {
let defaultValue = '' let defaultValue = ''
if (_.isPlainObject(value)) { if (isPlainObject(value)) {
defaultValue = !_.isNil(value.default) ? value.default : this.getTypeDefaultValue(value.type) defaultValue = !isNil(value.default) ? value.default : this.getTypeDefaultValue(value.type)
} else { } else {
defaultValue = this.getTypeDefaultValue(value) defaultValue = this.getTypeDefaultValue(value)
} }
_.set(result, key, { set(result, key, {
default: defaultValue, default: defaultValue,
type: (value.type || value).toLowerCase(), type: (value.type || value).toLowerCase(),
title: value.title || _.startCase(key), title: value.title || startCase(key),
hint: value.hint || '', hint: value.hint || '',
enum: value.enum || false, enum: value.enum || false,
enumDisplay: value.enumDisplay || 'select', enumDisplay: value.enumDisplay || 'select',
...@@ -95,5 +99,4 @@ module.exports = { ...@@ -95,5 +99,4 @@ module.exports = {
}) })
return result return result
}, {}) }, {})
}
} }
'use strict' import { replace } from 'lodash-es'
const _ = require('lodash')
const isoDurationReg = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/ const isoDurationReg = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/
module.exports = { export default {
/** /**
* Parse configuration value for environment vars * Parse configuration value for environment vars
* *
...@@ -16,7 +14,7 @@ module.exports = { ...@@ -16,7 +14,7 @@ module.exports = {
* @returns Parse configuration value * @returns Parse configuration value
*/ */
parseConfigValue (cfg) { parseConfigValue (cfg) {
return _.replace( return replace(
cfg, cfg,
/\$\(([A-Z0-9_]+)(?::(.+))?\)/g, /\$\(([A-Z0-9_]+)(?::(.+))?\)/g,
(fm, m, d) => { return process.env[m] || d } (fm, m, d) => { return process.env[m] || d }
......
const CustomError = require('custom-error-instance') import CustomError from 'custom-error-instance'
module.exports = { export default {
Custom (slug, message) { Custom (slug, message) {
return CustomError(slug, { message }) return CustomError(slug, { message })
}, },
......
const _ = require('lodash') import { defaultTo, isFinite } from 'lodash-es'
module.exports = { export function generateSuccess (msg) {
generateSuccess (msg) {
return { return {
succeeded: true, succeeded: true,
errorCode: 0, errorCode: 0,
slug: 'ok', slug: 'ok',
message: _.defaultTo(msg, 'Operation succeeded.') message: defaultTo(msg, 'Operation succeeded.')
} }
}, }
generateError (err, complete = true) {
export function generateError (err, complete = true) {
const error = { const error = {
succeeded: false, succeeded: false,
errorCode: _.isFinite(err.code) ? err.code : 1, errorCode: isFinite(err.code) ? err.code : 1,
slug: err.name, slug: err.name,
message: err.message || 'An unexpected error occured.' message: err.message || 'An unexpected error occured.'
} }
return (complete) ? { operation: error } : error return (complete) ? { operation: error } : error
}
} }
const qs = require('querystring') import qs from 'querystring'
const _ = require('lodash') import { fromPairs, get, initial, invert, isEmpty, last } from 'lodash-es'
const crypto = require('crypto') import crypto from 'node:crypto'
const path = require('path') import path from 'node:path'
const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
const localeFolderRegex = /^([a-z]{2}(?:-[a-z]{2})?\/)?(.*)/i const localeFolderRegex = /^([a-z]{2}(?:-[a-z]{2})?\/)?(.*)/i
...@@ -12,14 +12,13 @@ const contentToExt = { ...@@ -12,14 +12,13 @@ const contentToExt = {
markdown: 'md', markdown: 'md',
html: 'html' html: 'html'
} }
const extToContent = _.invert(contentToExt) const extToContent = invert(contentToExt)
module.exports = { /**
/**
* Parse raw url path and make it safe * Parse raw url path and make it safe
*/ */
parsePath (rawPath, opts = {}) { export function parsePath (rawPath, opts = {}) {
let pathObj = { const pathObj = {
// TODO: use site base lang // TODO: use site base lang
locale: 'en', // WIKI.config.lang.code, locale: 'en', // WIKI.config.lang.code,
path: 'home', path: 'home',
...@@ -29,17 +28,17 @@ module.exports = { ...@@ -29,17 +28,17 @@ module.exports = {
} }
// Clean Path // Clean Path
rawPath = _.trim(qs.unescape(rawPath)) rawPath = qs.unescape(rawPath).trim()
if (_.startsWith(rawPath, '/')) { rawPath = rawPath.substring(1) } if (rawPath.startsWith('/')) { rawPath = rawPath.substring(1) }
rawPath = rawPath.replace(unsafeCharsRegex, '') rawPath = rawPath.replace(unsafeCharsRegex, '')
if (rawPath === '') { rawPath = 'home' } if (rawPath === '') { rawPath = 'home' }
rawPath = rawPath.replace(/\\/g, '').replace(/\/\//g, '').replace(/\.\.+/ig, '') rawPath = rawPath.replace(/\\/g, '').replace(/\/\//g, '').replace(/\.\.+/ig, '')
// Extract Info // Extract Info
let pathParts = _.filter(_.split(rawPath, '/'), p => { let pathParts = rawPath.split('/').filter(p => {
p = _.trim(p) p = p.trim()
return !_.isEmpty(p) && p !== '..' && p !== '.' return !isEmpty(p) && p !== '..' && p !== '.'
}) })
if (pathParts[0].startsWith('_')) { if (pathParts[0].startsWith('_')) {
pathParts.shift() pathParts.shift()
...@@ -52,7 +51,7 @@ module.exports = { ...@@ -52,7 +51,7 @@ module.exports = {
// Strip extension // Strip extension
if (opts.stripExt && pathParts.length > 0) { if (opts.stripExt && pathParts.length > 0) {
const lastPart = _.last(pathParts) const lastPart = last(pathParts)
if (lastPart.indexOf('.') > 0) { if (lastPart.indexOf('.') > 0) {
pathParts.pop() pathParts.pop()
const lastPartMeta = path.parse(lastPart) const lastPartMeta = path.parse(lastPart)
...@@ -62,18 +61,20 @@ module.exports = { ...@@ -62,18 +61,20 @@ module.exports = {
pathObj.path = _.join(pathParts, '/') pathObj.path = _.join(pathParts, '/')
return pathObj return pathObj
}, }
/**
/**
* Generate unique hash from page * Generate unique hash from page
*/ */
generateHash(opts) { export function generateHash(opts) {
return crypto.createHash('sha1').update(`${opts.locale}|${opts.path}|${opts.privateNS}`).digest('hex') return crypto.createHash('sha1').update(`${opts.locale}|${opts.path}|${opts.privateNS}`).digest('hex')
}, }
/**
/**
* Inject Page Metadata * Inject Page Metadata
*/ */
injectPageMetadata(page) { export function injectPageMetadata(page) {
let meta = [ const meta = [
['title', page.title], ['title', page.title],
['description', page.description], ['description', page.description],
['published', page.isPublished.toString()], ['published', page.isPublished.toString()],
...@@ -90,54 +91,58 @@ module.exports = { ...@@ -90,54 +91,58 @@ module.exports = {
case 'json': case 'json':
return { return {
...page.content, ...page.content,
_meta: _.fromPairs(meta) _meta: fromPairs(meta)
} }
default: default:
return page.content return page.content
} }
}, }
/**
/**
* Check if path is a reserved path * Check if path is a reserved path
*/ */
isReservedPath(rawPath) { export function isReservedPath(rawPath) {
const firstSection = _.head(rawPath.split('/')) const firstSection = _.head(rawPath.split('/'))
if (firstSection.length < 1) { if (firstSection.length < 1) {
return true return true
} else if (localeSegmentRegex.test(firstSection)) { } else if (localeSegmentRegex.test(firstSection)) {
return true return true
} else if ( } else if (
_.some(WIKI.data.reservedPaths, p => { WIKI.data.reservedPaths.some(p => {
return p === firstSection return p === firstSection
})) { })) {
return true return true
} else { } else {
return false return false
} }
}, }
/**
/**
* Get file extension from content type * Get file extension from content type
*/ */
getFileExtension(contentType) { export function getFileExtension(contentType) {
return _.get(contentToExt, contentType, 'txt') return get(contentToExt, contentType, 'txt')
}, }
/**
/**
* Get content type from file extension * Get content type from file extension
*/ */
getContentType (filePath) { export function getContentType (filePath) {
const ext = _.last(filePath.split('.')) const ext = last(filePath.split('.'))
return _.get(extToContent, ext, false) return get(extToContent, ext, false)
}, }
/**
/**
* Get Page Meta object from disk path * Get Page Meta object from disk path
*/ */
getPagePath (filePath) { export function getPagePath (filePath) {
let fpath = filePath let fpath = filePath
if (process.platform === 'win32') { if (process.platform === 'win32') {
fpath = filePath.replace(/\\/g, '/') fpath = filePath.replace(/\\/g, '/')
} }
let meta = { let meta = {
locale: WIKI.config.lang.code, locale: WIKI.config.lang.code,
path: _.initial(fpath.split('.')).join('') path: initial(fpath.split('.')).join('')
} }
const result = localeFolderRegex.exec(meta.path) const result = localeFolderRegex.exec(meta.path)
if (result[1]) { if (result[1]) {
...@@ -147,5 +152,4 @@ module.exports = { ...@@ -147,5 +152,4 @@ module.exports = {
} }
} }
return meta return meta
}
} }
const util = require('node:util') import util from 'node:util'
const crypto = require('node:crypto') import crypto from 'node:crypto'
import passportJWT from 'passport-jwt'
const randomBytes = util.promisify(crypto.randomBytes) const randomBytes = util.promisify(crypto.randomBytes)
const passportJWT = require('passport-jwt')
module.exports = { export function sanitizeCommitUser (user) {
sanitizeCommitUser (user) {
// let wlist = new RegExp('[^a-zA-Z0-9-_.\',& ' + appdata.regex.cjk + appdata.regex.arabic + ']', 'g') // let wlist = new RegExp('[^a-zA-Z0-9-_.\',& ' + appdata.regex.cjk + appdata.regex.arabic + ']', 'g')
// return { // return {
// name: _.chain(user.name).replace(wlist, '').trim().value(), // name: _.chain(user.name).replace(wlist, '').trim().value(),
// email: appconfig.git.showUserEmail ? user.email : appconfig.git.serverEmail // email: appconfig.git.showUserEmail ? user.email : appconfig.git.serverEmail
// } // }
}, }
/**
/**
* Generate a random token * Generate a random token
* *
* @param {any} length * @param {any} length
* @returns * @returns
*/ */
async generateToken (length) { export async function generateToken (length) {
return (await randomBytes(length)).toString('hex') return (await randomBytes(length)).toString('hex')
}, }
extractJWT: passportJWT.ExtractJwt.fromExtractors([ export const extractJWT = passportJWT.ExtractJwt.fromExtractors([
passportJWT.ExtractJwt.fromAuthHeaderAsBearerToken(), passportJWT.ExtractJwt.fromAuthHeaderAsBearerToken(),
(req) => { (req) => {
let token = null let token = null
...@@ -34,5 +35,4 @@ module.exports = { ...@@ -34,5 +35,4 @@ module.exports = {
} }
return token return token
} }
]) ])
}
...@@ -3,30 +3,34 @@ ...@@ -3,30 +3,34 @@
// Licensed under AGPLv3 // Licensed under AGPLv3
// =========================================== // ===========================================
const path = require('path') import path from 'node:path'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const semver = require('semver') import semver from 'semver'
const nanoid = require('nanoid').customAlphabet('1234567890abcdef', 10) import { customAlphabet } from 'nanoid'
const fs = require('fs-extra') import fse from 'fs-extra'
import configSvc from './core/config.mjs'
import kernel from './core/kernel.mjs'
import logger from './core/logger.mjs'
const nanoid = customAlphabet('1234567890abcdef', 10)
if (!semver.satisfies(process.version, '>=18')) { if (!semver.satisfies(process.version, '>=18')) {
console.error('ERROR: Node.js 18.x or later required!') console.error('ERROR: Node.js 18.x or later required!')
process.exit(1) process.exit(1)
} }
if (fs.pathExistsSync('./package.json')) { if (fse.pathExistsSync('./package.json')) {
console.error('ERROR: Must run server from the parent directory!') console.error('ERROR: Must run server from the parent directory!')
process.exit(1) process.exit(1)
} }
let WIKI = { const WIKI = {
IS_DEBUG: process.env.NODE_ENV === 'development', IS_DEBUG: process.env.NODE_ENV === 'development',
ROOTPATH: process.cwd(), ROOTPATH: process.cwd(),
INSTANCE_ID: nanoid(10), INSTANCE_ID: nanoid(10),
SERVERPATH: path.join(process.cwd(), 'server'), SERVERPATH: path.join(process.cwd(), 'server'),
Error: require('./helpers/error'), configSvc,
configSvc: require('./core/config'), kernel,
kernel: require('./core/kernel'),
sites: {}, sites: {},
sitesMappings: {}, sitesMappings: {},
startedAt: DateTime.utc(), startedAt: DateTime.utc(),
...@@ -37,13 +41,13 @@ let WIKI = { ...@@ -37,13 +41,13 @@ let WIKI = {
} }
global.WIKI = WIKI global.WIKI = WIKI
WIKI.configSvc.init() await WIKI.configSvc.init()
// ---------------------------------------- // ----------------------------------------
// Init Logger // Init Logger
// ---------------------------------------- // ----------------------------------------
WIKI.logger = require('./core/logger').init() WIKI.logger = logger.init()
// ---------------------------------------- // ----------------------------------------
// Start Kernel // Start Kernel
......
## IMPORTANT
Localization files are not stored into files!
Contact us on Gitter to request access to the translation web service: https://gitter.im/Requarks/wiki
## Development Mode
If you need to add new keys and test them live, simply create a {LANG}.yml file in this folder containing the values you want to test. e.g.:
### en.yml
```yml
admin:
api.title: 'API Access'
auth.title: 'Authentication'
```
The official localization keys will still be loaded first, but your local files will overwrite any existing keys (and add new ones).
Note that you must restart Wiki.js to load any changes made to the files, which happens automatically on save when in dev mode.
/* global WIKI */
/**
* Security Middleware
*
* @param {Express Request} req Express request object
* @param {Express Response} res Express response object
* @param {Function} next next callback function
* @return {any} void
*/
module.exports = function (req, res, next) {
// -> Disable X-Powered-By
req.app.disable('x-powered-by')
// -> Disable Frame Embedding
if (WIKI.config.security.securityIframe) {
res.set('X-Frame-Options', 'deny')
}
// -> Re-enable XSS Fitler if disabled
res.set('X-XSS-Protection', '1; mode=block')
// -> Disable MIME-sniffing
res.set('X-Content-Type-Options', 'nosniff')
// -> Disable IE Compatibility Mode
res.set('X-UA-Compatible', 'IE=edge')
// -> Disables referrer header when navigating to a different origin
if (WIKI.config.security.securityReferrerPolicy) {
res.set('Referrer-Policy', 'same-origin')
}
// -> Enforce HSTS
if (WIKI.config.security.securityHSTS) {
res.set('Strict-Transport-Security', `max-age=${WIKI.config.security.securityHSTSDuration}; includeSubDomains`)
}
// -> Prevent Open Redirect from user provided URL
if (WIKI.config.security.securityOpenRedirect) {
// Strips out all repeating / character in the provided URL
req.url = req.url.replace(/(\/)(?=\/*\1)/g, '')
}
return next()
}
const _ = require('lodash')
/**
* SEO Middleware
*
* @param {Express Request} req Express request object
* @param {Express Response} res Express response object
* @param {Function} next next callback function
* @return {any} void
*/
module.exports = function (req, res, next) {
if (req.path.length > 1 && _.endsWith(req.path, '/')) {
let query = req.url.slice(req.path.length) || ''
res.redirect(301, req.path.slice(0, -1) + query)
} else {
_.set(res.locals, 'pageMeta.url', `${WIKI.config.host}${req.path}`)
return next()
}
}
const Model = require('objection').Model import { Model } from 'objection'
const fs = require('fs-extra') import fs from 'node:fs/promises'
const path = require('path') import path from 'node:path'
const _ = require('lodash') import { defaultTo, forOwn, isBoolean, replace, sortBy } from 'lodash-es'
const yaml = require('js-yaml') import yaml from 'js-yaml'
const commonHelper = require('../helpers/common') import { parseModuleProps } from '../helpers/common.mjs'
/** /**
* Analytics model * Analytics model
*/ */
module.exports = class Analytics extends Model { export class Analytics extends Model {
static get tableName() { return 'analytics' } static get tableName() { return 'analytics' }
static get jsonSchema () { static get jsonSchema () {
...@@ -29,8 +29,8 @@ module.exports = class Analytics extends Model { ...@@ -29,8 +29,8 @@ module.exports = class Analytics extends Model {
} }
static async getProviders(isEnabled) { static async getProviders(isEnabled) {
const providers = await WIKI.db.analytics.query().where(_.isBoolean(isEnabled) ? { isEnabled } : {}) const providers = await WIKI.db.analytics.query().where(isBoolean(isEnabled) ? { isEnabled } : {})
return _.sortBy(providers, ['module']) return sortBy(providers, ['module'])
} }
static async refreshProvidersFromDisk() { static async refreshProvidersFromDisk() {
...@@ -42,7 +42,7 @@ module.exports = class Analytics extends Model { ...@@ -42,7 +42,7 @@ module.exports = class Analytics extends Model {
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/analytics', dir, 'definition.yml'), 'utf8') const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/analytics', dir, 'definition.yml'), 'utf8')
const defParsed = yaml.load(def) const defParsed = yaml.load(def)
defParsed.key = dir defParsed.key = dir
defParsed.props = commonHelper.parseModuleProps(defParsed.props) defParsed.props = parseModuleProps(defParsed.props)
WIKI.data.analytics.push(defParsed) WIKI.data.analytics.push(defParsed)
WIKI.logger.debug(`Loaded analytics module definition ${dir}: [ OK ]`) WIKI.logger.debug(`Loaded analytics module definition ${dir}: [ OK ]`)
} }
...@@ -72,14 +72,14 @@ module.exports = class Analytics extends Model { ...@@ -72,14 +72,14 @@ module.exports = class Analytics extends Model {
for (let provider of providers) { for (let provider of providers) {
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/analytics', provider.key, 'code.yml'), 'utf8') const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/analytics', provider.key, 'code.yml'), 'utf8')
let code = yaml.safeLoad(def) let code = yaml.safeLoad(def)
code.head = _.defaultTo(code.head, '') code.head = defaultTo(code.head, '')
code.bodyStart = _.defaultTo(code.bodyStart, '') code.bodyStart = defaultTo(code.bodyStart, '')
code.bodyEnd = _.defaultTo(code.bodyEnd, '') code.bodyEnd = defaultTo(code.bodyEnd, '')
_.forOwn(provider.config, (value, key) => { forOwn(provider.config, (value, key) => {
code.head = _.replace(code.head, new RegExp(`{{${key}}}`, 'g'), value) code.head = replace(code.head, new RegExp(`{{${key}}}`, 'g'), value)
code.bodyStart = _.replace(code.bodyStart, `{{${key}}}`, value) code.bodyStart = replace(code.bodyStart, `{{${key}}}`, value)
code.bodyEnd = _.replace(code.bodyEnd, `{{${key}}}`, value) code.bodyEnd = replace(code.bodyEnd, `{{${key}}}`, value)
}) })
analyticsCode.head += code.head analyticsCode.head += code.head
......
/* global WIKI */ /* global WIKI */
const Model = require('objection').Model import { Model } from 'objection'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const ms = require('ms') import ms from 'ms'
const jwt = require('jsonwebtoken') import jwt from 'jsonwebtoken'
/** /**
* Users model * Users model
*/ */
module.exports = class ApiKey extends Model { export class ApiKey extends Model {
static get tableName() { return 'apiKeys' } static get tableName() { return 'apiKeys' }
static get jsonSchema () { static get jsonSchema () {
......
const Model = require('objection').Model import { Model } from 'objection'
const moment = require('moment') import path from 'path'
const path = require('path') import fse from 'fs-extra'
const fs = require('fs-extra') import { startsWith } from 'lodash-es'
const _ = require('lodash') import { generateHash } from '../helpers/common.mjs'
const commonHelper = require('../helpers/common')
import { User } from './users.mjs'
/** /**
* Users model * Users model
*/ */
module.exports = class Asset extends Model { export class Asset extends Model {
static get tableName() { return 'assets' } static get tableName() { return 'assets' }
static get jsonSchema () { static get jsonSchema () {
...@@ -34,19 +35,11 @@ module.exports = class Asset extends Model { ...@@ -34,19 +35,11 @@ module.exports = class Asset extends Model {
return { return {
author: { author: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'assets.authorId', from: 'assets.authorId',
to: 'users.id' to: 'users.id'
} }
},
folder: {
relation: Model.BelongsToOneRelation,
modelClass: require('./assetFolders'),
join: {
from: 'assets.folderId',
to: 'assetFolders.id'
}
} }
} }
} }
...@@ -72,7 +65,7 @@ module.exports = class Asset extends Model { ...@@ -72,7 +65,7 @@ module.exports = class Asset extends Model {
} }
async deleteAssetCache() { async deleteAssetCache() {
await fs.remove(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${this.hash}.dat`)) await fse.remove(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${this.hash}.dat`))
} }
static async upload(opts) { static async upload(opts) {
...@@ -88,7 +81,7 @@ module.exports = class Asset extends Model { ...@@ -88,7 +81,7 @@ module.exports = class Asset extends Model {
let assetRow = { let assetRow = {
filename: opts.originalname, filename: opts.originalname,
ext: fileInfo.ext, ext: fileInfo.ext,
kind: _.startsWith(opts.mimetype, 'image/') ? 'image' : 'binary', kind: startsWith(opts.mimetype, 'image/') ? 'image' : 'binary',
mime: opts.mimetype, mime: opts.mimetype,
fileSize: opts.size, fileSize: opts.size,
folderId: opts.folderId folderId: opts.folderId
...@@ -112,7 +105,7 @@ module.exports = class Asset extends Model { ...@@ -112,7 +105,7 @@ module.exports = class Asset extends Model {
// Save asset data // Save asset data
try { try {
const fileBuffer = await fs.readFile(opts.path) const fileBuffer = await fse.readFile(opts.path)
if (asset) { if (asset) {
// Patch existing asset // Patch existing asset
...@@ -166,7 +159,7 @@ module.exports = class Asset extends Model { ...@@ -166,7 +159,7 @@ module.exports = class Asset extends Model {
.select('tree.*', 'assets.preview', 'assets.previewState') .select('tree.*', 'assets.preview', 'assets.previewState')
.innerJoin('assets', 'tree.id', 'assets.id') .innerJoin('assets', 'tree.id', 'assets.id')
.where(id ? { 'tree.id': id } : { .where(id ? { 'tree.id': id } : {
'tree.hash': commonHelper.generateHash(path), 'tree.hash': generateHash(path),
'tree.localeCode': locale, 'tree.localeCode': locale,
'tree.siteId': siteId 'tree.siteId': siteId
}) })
...@@ -202,7 +195,7 @@ module.exports = class Asset extends Model { ...@@ -202,7 +195,7 @@ module.exports = class Asset extends Model {
static async getAssetFromCache(assetPath, cachePath, res) { static async getAssetFromCache(assetPath, cachePath, res) {
try { try {
await fs.access(cachePath, fs.constants.R_OK) await fse.access(cachePath, fse.constants.R_OK)
} catch (err) { } catch (err) {
return false return false
} }
...@@ -217,7 +210,7 @@ module.exports = class Asset extends Model { ...@@ -217,7 +210,7 @@ module.exports = class Asset extends Model {
path: assetPath path: assetPath
} }
}) })
for (let location of _.filter(localLocations, location => Boolean(location.path))) { for (let location of localLocations.filter(location => Boolean(location.path))) {
const assetExists = await WIKI.db.assets.getAssetFromCache(assetPath, location.path, res) const assetExists = await WIKI.db.assets.getAssetFromCache(assetPath, location.path, res)
if (assetExists) { if (assetExists) {
return true return true
...@@ -232,13 +225,13 @@ module.exports = class Asset extends Model { ...@@ -232,13 +225,13 @@ module.exports = class Asset extends Model {
const assetData = await WIKI.db.knex('assetData').where('id', asset.id).first() const assetData = await WIKI.db.knex('assetData').where('id', asset.id).first()
res.type(asset.ext) res.type(asset.ext)
res.send(assetData.data) res.send(assetData.data)
await fs.outputFile(cachePath, assetData.data) await fse.outputFile(cachePath, assetData.data)
} else { } else {
res.sendStatus(404) res.sendStatus(404)
} }
} }
static async flushTempUploads() { static async flushTempUploads() {
return fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `uploads`)) return fse.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `uploads`))
} }
} }
const Model = require('objection').Model import { Model } from 'objection'
const fs = require('fs-extra') import fs from 'node:fs/promises'
const path = require('path') import path from 'node:path'
const _ = require('lodash') import { get } from 'lodash-es'
const yaml = require('js-yaml') import yaml from 'js-yaml'
const commonHelper = require('../helpers/common') import { parseModuleProps } from '../helpers/common.mjs'
/** /**
* Authentication model * Authentication model
*/ */
module.exports = class Authentication extends Model { export class Authentication extends Model {
static get tableName() { return 'authentication' } static get tableName() { return 'authentication' }
static get jsonSchema () { static get jsonSchema () {
...@@ -37,8 +37,8 @@ module.exports = class Authentication extends Model { ...@@ -37,8 +37,8 @@ module.exports = class Authentication extends Model {
const strategies = await WIKI.db.authentication.query().where(enabledOnly ? { isEnabled: true } : {}) const strategies = await WIKI.db.authentication.query().where(enabledOnly ? { isEnabled: true } : {})
return strategies.map(str => ({ return strategies.map(str => ({
...str, ...str,
domainWhitelist: _.get(str.domainWhitelist, 'v', []), domainWhitelist: get(str.domainWhitelist, 'v', []),
autoEnrollGroups: _.get(str.autoEnrollGroups, 'v', []) autoEnrollGroups: get(str.autoEnrollGroups, 'v', [])
})) }))
} }
...@@ -52,7 +52,7 @@ module.exports = class Authentication extends Model { ...@@ -52,7 +52,7 @@ module.exports = class Authentication extends Model {
const defParsed = yaml.load(def) const defParsed = yaml.load(def)
if (!defParsed.isAvailable) { continue } if (!defParsed.isAvailable) { continue }
defParsed.key = dir defParsed.key = dir
defParsed.props = commonHelper.parseModuleProps(defParsed.props) defParsed.props = parseModuleProps(defParsed.props)
WIKI.data.authentication.push(defParsed) WIKI.data.authentication.push(defParsed)
WIKI.logger.debug(`Loaded authentication module definition ${dir}: [ OK ]`) WIKI.logger.debug(`Loaded authentication module definition ${dir}: [ OK ]`)
} }
......
const Model = require('objection').Model import { Model } from 'objection'
const fs = require('fs-extra') import fs from 'node:fs/promises'
const path = require('path') import path from 'node:path'
const _ = require('lodash') import { defaultTo, find, forOwn, isBoolean, replace, sortBy } from 'lodash-es'
const yaml = require('js-yaml') import yaml from 'js-yaml'
const commonHelper = require('../helpers/common') import { parseModuleProps } from '../helpers/common.mjs'
/** /**
* CommentProvider model * CommentProvider model
*/ */
module.exports = class CommentProvider extends Model { export class CommentProvider extends Model {
static get tableName() { return 'commentProviders' } static get tableName() { return 'commentProviders' }
static get idColumn() { return 'key' } static get idColumn() { return 'key' }
...@@ -33,8 +33,8 @@ module.exports = class CommentProvider extends Model { ...@@ -33,8 +33,8 @@ module.exports = class CommentProvider extends Model {
} }
static async getProviders(isEnabled) { static async getProviders(isEnabled) {
const providers = await WIKI.db.commentProviders.query().where(_.isBoolean(isEnabled) ? { isEnabled } : {}) const providers = await WIKI.db.commentProviders.query().where(isBoolean(isEnabled) ? { isEnabled } : {})
return _.sortBy(providers, ['module']) return sortBy(providers, ['module'])
} }
static async refreshProvidersFromDisk() { static async refreshProvidersFromDisk() {
...@@ -46,7 +46,7 @@ module.exports = class CommentProvider extends Model { ...@@ -46,7 +46,7 @@ module.exports = class CommentProvider extends Model {
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/comments', dir, 'definition.yml'), 'utf8') const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/comments', dir, 'definition.yml'), 'utf8')
const defParsed = yaml.load(def) const defParsed = yaml.load(def)
defParsed.key = dir defParsed.key = dir
defParsed.props = commonHelper.parseModuleProps(defParsed.props) defParsed.props = parseModuleProps(defParsed.props)
WIKI.data.commentProviders.push(defParsed) WIKI.data.commentProviders.push(defParsed)
WIKI.logger.debug(`Loaded comments provider module definition ${dir}: [ OK ]`) WIKI.logger.debug(`Loaded comments provider module definition ${dir}: [ OK ]`)
} }
...@@ -62,7 +62,7 @@ module.exports = class CommentProvider extends Model { ...@@ -62,7 +62,7 @@ module.exports = class CommentProvider extends Model {
const commentProvider = await WIKI.db.commentProviders.query().findOne('isEnabled', true) const commentProvider = await WIKI.db.commentProviders.query().findOne('isEnabled', true)
if (commentProvider) { if (commentProvider) {
WIKI.data.commentProvider = { WIKI.data.commentProvider = {
..._.find(WIKI.data.commentProviders, ['key', commentProvider.module]), ...find(WIKI.data.commentProviders, ['key', commentProvider.module]),
head: '', head: '',
bodyStart: '', bodyStart: '',
bodyEnd: '', bodyEnd: '',
...@@ -72,14 +72,14 @@ module.exports = class CommentProvider extends Model { ...@@ -72,14 +72,14 @@ module.exports = class CommentProvider extends Model {
if (WIKI.data.commentProvider.codeTemplate) { if (WIKI.data.commentProvider.codeTemplate) {
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/comments', commentProvider.key, 'code.yml'), 'utf8') const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/comments', commentProvider.key, 'code.yml'), 'utf8')
let code = yaml.safeLoad(def) let code = yaml.safeLoad(def)
code.head = _.defaultTo(code.head, '') code.head = defaultTo(code.head, '')
code.body = _.defaultTo(code.body, '') code.body = defaultTo(code.body, '')
code.main = _.defaultTo(code.main, '') code.main = defaultTo(code.main, '')
_.forOwn(commentProvider.config, (value, key) => { forOwn(commentProvider.config, (value, key) => {
code.head = _.replace(code.head, new RegExp(`{{${key}}}`, 'g'), value) code.head = replace(code.head, new RegExp(`{{${key}}}`, 'g'), value)
code.body = _.replace(code.body, new RegExp(`{{${key}}}`, 'g'), value) code.body = replace(code.body, new RegExp(`{{${key}}}`, 'g'), value)
code.main = _.replace(code.main, new RegExp(`{{${key}}}`, 'g'), value) code.main = replace(code.main, new RegExp(`{{${key}}}`, 'g'), value)
}) })
WIKI.data.commentProvider.head = code.head WIKI.data.commentProvider.head = code.head
...@@ -88,7 +88,7 @@ module.exports = class CommentProvider extends Model { ...@@ -88,7 +88,7 @@ module.exports = class CommentProvider extends Model {
} else { } else {
WIKI.data.commentProvider = { WIKI.data.commentProvider = {
...WIKI.data.commentProvider, ...WIKI.data.commentProvider,
...require(`../modules/comments/${commentProvider.key}/comment`), ...(await import(`../modules/comments/${commentProvider.key}/comment.mjs`)),
config: commentProvider.config config: commentProvider.config
} }
await WIKI.data.commentProvider.init() await WIKI.data.commentProvider.init()
......
const Model = require('objection').Model import { Model } from 'objection'
const validate = require('validate.js') import validate from 'validate.js'
const _ = require('lodash')
import { Page } from './pages.mjs'
import { User } from './users.mjs'
/** /**
* Comments model * Comments model
*/ */
module.exports = class Comment extends Model { export class Comment extends Model {
static get tableName() { return 'comments' } static get tableName() { return 'comments' }
static get jsonSchema () { static get jsonSchema () {
...@@ -30,7 +32,7 @@ module.exports = class Comment extends Model { ...@@ -30,7 +32,7 @@ module.exports = class Comment extends Model {
return { return {
author: { author: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'comments.authorId', from: 'comments.authorId',
to: 'users.id' to: 'users.id'
...@@ -38,7 +40,7 @@ module.exports = class Comment extends Model { ...@@ -38,7 +40,7 @@ module.exports = class Comment extends Model {
}, },
page: { page: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./pages'), modelClass: Page,
join: { join: {
from: 'comments.pageId', from: 'comments.pageId',
to: 'pages.id' to: 'pages.id'
...@@ -62,7 +64,7 @@ module.exports = class Comment extends Model { ...@@ -62,7 +64,7 @@ module.exports = class Comment extends Model {
// -> Input validation // -> Input validation
if (user.id === 2) { if (user.id === 2) {
const validation = validate({ const validation = validate({
email: _.toLower(guestEmail), email: guestEmail.toLowerCase(),
name: guestName name: guestName
}, { }, {
email: { email: {
...@@ -87,7 +89,7 @@ module.exports = class Comment extends Model { ...@@ -87,7 +89,7 @@ module.exports = class Comment extends Model {
} }
} }
content = _.trim(content) content = content.trim()
if (content.length < 2) { if (content.length < 2) {
throw new WIKI.Error.CommentContentMissing() throw new WIKI.Error.CommentContentMissing()
} }
......
const Model = require('objection').Model import { Model } from 'objection'
import { User } from './users.mjs'
/** /**
* Groups model * Groups model
*/ */
module.exports = class Group extends Model { export class Group extends Model {
static get tableName() { return 'groups' } static get tableName() { return 'groups' }
static get jsonSchema () { static get jsonSchema () {
...@@ -30,7 +32,7 @@ module.exports = class Group extends Model { ...@@ -30,7 +32,7 @@ module.exports = class Group extends Model {
return { return {
users: { users: {
relation: Model.ManyToManyRelation, relation: Model.ManyToManyRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'groups.id', from: 'groups.id',
through: { through: {
......
const Model = require('objection').Model import { Model } from 'objection'
/** /**
* Hook model * Hook model
*/ */
module.exports = class Hook extends Model { export class Hook extends Model {
static get tableName () { return 'hooks' } static get tableName () { return 'hooks' }
static get jsonAttributes () { static get jsonAttributes () {
......
import { Analytics } from './analytics.mjs'
import { ApiKey } from './apiKeys.mjs'
import { Asset } from './assets.mjs'
import { Authentication } from './authentication.mjs'
import { CommentProvider } from './commentProviders.mjs'
import { Comment } from './comments.mjs'
import { Group } from './groups.mjs'
import { Hook } from './hooks.mjs'
import { Locale } from './locales.mjs'
import { Navigation } from './navigation.mjs'
import { PageHistory } from './pageHistory.mjs'
import { PageLink } from './pageLinks.mjs'
import { Page } from './pages.mjs'
import { Renderer } from './renderers.mjs'
import { Setting } from './settings.mjs'
import { Site } from './sites.mjs'
import { Storage } from './storage.mjs'
import { Tag } from './tags.mjs'
import { Tree } from './tree.mjs'
import { UserKey } from './userKeys.mjs'
import { User } from './users.mjs'
export default {
analytics: Analytics,
apiKeys: ApiKey,
assets: Asset,
authentication: Authentication,
commentProviders: CommentProvider,
comments: Comment,
groups: Group,
hooks: Hook,
locales: Locale,
navigation: Navigation,
pageHistory: PageHistory,
pageLinks: PageLink,
pages: Page,
renderers: Renderer,
settings: Setting,
sites: Site,
storage: Storage,
tags: Tag,
tree: Tree,
userKeys: UserKey,
users: User
}
const Model = require('objection').Model import { Model } from 'objection'
/** /**
* Locales model * Locales model
*/ */
module.exports = class Locale extends Model { export class Locale extends Model {
static get tableName() { return 'locales' } static get tableName() { return 'locales' }
static get idColumn() { return 'code' } static get idColumn() { return 'code' }
......
const Model = require('objection').Model import { Model } from 'objection'
const _ = require('lodash') import { has } from 'lodash-es'
/** /**
* Navigation model * Navigation model
*/ */
module.exports = class Navigation extends Model { export class Navigation extends Model {
static get tableName() { return 'navigation' } static get tableName() { return 'navigation' }
static get idColumn() { return 'key' } static get idColumn() { return 'key' }
...@@ -30,7 +30,7 @@ module.exports = class Navigation extends Model { ...@@ -30,7 +30,7 @@ module.exports = class Navigation extends Model {
const navTree = await WIKI.db.navigation.query().findOne('key', `site`) const navTree = await WIKI.db.navigation.query().findOne('key', `site`)
if (navTree) { if (navTree) {
// Check for pre-2.3 format // Check for pre-2.3 format
if (_.has(navTree.config[0], 'kind')) { if (has(navTree.config[0], 'kind')) {
navTree.config = [{ navTree.config = [{
locale: 'en', locale: 'en',
items: navTree.config.map(item => ({ items: navTree.config.map(item => ({
...@@ -58,7 +58,7 @@ module.exports = class Navigation extends Model { ...@@ -58,7 +58,7 @@ module.exports = class Navigation extends Model {
} }
static getAuthorizedItems(tree = [], groups = []) { static getAuthorizedItems(tree = [], groups = []) {
return _.filter(tree, leaf => { return tree.filter(leaf => {
return leaf.visibilityMode === 'all' || _.intersection(leaf.visibilityGroups, groups).length > 0 return leaf.visibilityMode === 'all' || _.intersection(leaf.visibilityGroups, groups).length > 0
}) })
} }
......
const Model = require('objection').Model import { Model } from 'objection'
const _ = require('lodash') import { get, reduce, reverse } from 'lodash-es'
const { DateTime, Duration } = require('luxon') import { DateTime, Duration } from 'luxon'
import { Locale } from './locales.mjs'
import { Page } from './pages.mjs'
import { User } from './users.mjs'
import { Tag } from './tags.mjs'
/** /**
* Page History model * Page History model
*/ */
module.exports = class PageHistory extends Model { export class PageHistory extends Model {
static get tableName() { return 'pageHistory' } static get tableName() { return 'pageHistory' }
static get jsonSchema () { static get jsonSchema () {
...@@ -34,7 +39,7 @@ module.exports = class PageHistory extends Model { ...@@ -34,7 +39,7 @@ module.exports = class PageHistory extends Model {
return { return {
tags: { tags: {
relation: Model.ManyToManyRelation, relation: Model.ManyToManyRelation,
modelClass: require('./tags'), modelClass: Tag,
join: { join: {
from: 'pageHistory.id', from: 'pageHistory.id',
through: { through: {
...@@ -46,7 +51,7 @@ module.exports = class PageHistory extends Model { ...@@ -46,7 +51,7 @@ module.exports = class PageHistory extends Model {
}, },
page: { page: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./pages'), modelClass: Page,
join: { join: {
from: 'pageHistory.pageId', from: 'pageHistory.pageId',
to: 'pages.id' to: 'pages.id'
...@@ -54,7 +59,7 @@ module.exports = class PageHistory extends Model { ...@@ -54,7 +59,7 @@ module.exports = class PageHistory extends Model {
}, },
author: { author: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'pageHistory.authorId', from: 'pageHistory.authorId',
to: 'users.id' to: 'users.id'
...@@ -62,7 +67,7 @@ module.exports = class PageHistory extends Model { ...@@ -62,7 +67,7 @@ module.exports = class PageHistory extends Model {
}, },
locale: { locale: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./locales'), modelClass: Locale,
join: { join: {
from: 'pageHistory.localeCode', from: 'pageHistory.localeCode',
to: 'locales.code' to: 'locales.code'
...@@ -189,16 +194,16 @@ module.exports = class PageHistory extends Model { ...@@ -189,16 +194,16 @@ module.exports = class PageHistory extends Model {
} }
return { return {
trail: _.reduce(_.reverse(history.results), (res, ph) => { trail: reduce(reverse(history.results), (res, ph) => {
let actionType = 'edit' let actionType = 'edit'
let valueBefore = null let valueBefore = null
let valueAfter = null let valueAfter = null
if (!prevPh && history.total < upperLimit) { if (!prevPh && history.total < upperLimit) {
actionType = 'initial' actionType = 'initial'
} else if (_.get(prevPh, 'path', '') !== ph.path) { } else if (get(prevPh, 'path', '') !== ph.path) {
actionType = 'move' actionType = 'move'
valueBefore = _.get(prevPh, 'path', '') valueBefore = get(prevPh, 'path', '')
valueAfter = ph.path valueAfter = ph.path
} }
......
const Model = require('objection').Model import { Model } from 'objection'
import { Page } from './pages.mjs'
/** /**
* Users model * Users model
*/ */
module.exports = class PageLink extends Model { export class PageLink extends Model {
static get tableName() { return 'pageLinks' } static get tableName() { return 'pageLinks' }
static get jsonSchema () { static get jsonSchema () {
...@@ -23,7 +25,7 @@ module.exports = class PageLink extends Model { ...@@ -23,7 +25,7 @@ module.exports = class PageLink extends Model {
return { return {
page: { page: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./pages'), modelClass: Page,
join: { join: {
from: 'pageLinks.pageId', from: 'pageLinks.pageId',
to: 'pages.id' to: 'pages.id'
......
const Model = require('objection').Model import { Model } from 'objection'
const _ = require('lodash') import { find, get, has, isEmpty, isString, pick } from 'lodash-es'
const JSBinType = require('js-binary').Type import { Type as JSBinType } from 'js-binary'
const pageHelper = require('../helpers/page') import { generateHash, getFileExtension, injectPageMetadata } from '../helpers/page.mjs'
const path = require('path') import path from 'node:path'
const fs = require('fs-extra') import fse from 'fs-extra'
const yaml = require('js-yaml') import yaml from 'js-yaml'
const striptags = require('striptags') import striptags from 'striptags'
const emojiRegex = require('emoji-regex') import emojiRegex from 'emoji-regex'
const he = require('he') import he from 'he'
const CleanCSS = require('clean-css') import CleanCSS from 'clean-css'
const TurndownService = require('turndown') import TurndownService from 'turndown'
const turndownPluginGfm = require('@joplin/turndown-plugin-gfm').gfm import { gfm as turndownPluginGfm } from '@joplin/turndown-plugin-gfm'
const cheerio = require('cheerio') import cheerio from 'cheerio'
import { Locale } from './locales.mjs'
import { PageLink } from './pageLinks.mjs'
import { Tag } from './tags.mjs'
import { User } from './users.mjs'
const pageRegex = /^[a-zA0-90-9-_/]*$/ const pageRegex = /^[a-zA0-90-9-_/]*$/
...@@ -27,7 +32,7 @@ const punctuationRegex = /[!,:;/\\_+\-=()&#@<>$~%^*[\]{}"'|]+|(\.\s)|(\s\.)/ig ...@@ -27,7 +32,7 @@ const punctuationRegex = /[!,:;/\\_+\-=()&#@<>$~%^*[\]{}"'|]+|(\.\s)|(\s\.)/ig
/** /**
* Pages model * Pages model
*/ */
module.exports = class Page extends Model { export class Page extends Model {
static get tableName() { return 'pages' } static get tableName() { return 'pages' }
static get jsonSchema () { static get jsonSchema () {
...@@ -62,7 +67,7 @@ module.exports = class Page extends Model { ...@@ -62,7 +67,7 @@ module.exports = class Page extends Model {
return { return {
tags: { tags: {
relation: Model.ManyToManyRelation, relation: Model.ManyToManyRelation,
modelClass: require('./tags'), modelClass: Tag,
join: { join: {
from: 'pages.id', from: 'pages.id',
through: { through: {
...@@ -74,7 +79,7 @@ module.exports = class Page extends Model { ...@@ -74,7 +79,7 @@ module.exports = class Page extends Model {
}, },
links: { links: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
modelClass: require('./pageLinks'), modelClass: PageLink,
join: { join: {
from: 'pages.id', from: 'pages.id',
to: 'pageLinks.pageId' to: 'pageLinks.pageId'
...@@ -82,7 +87,7 @@ module.exports = class Page extends Model { ...@@ -82,7 +87,7 @@ module.exports = class Page extends Model {
}, },
author: { author: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'pages.authorId', from: 'pages.authorId',
to: 'users.id' to: 'users.id'
...@@ -90,7 +95,7 @@ module.exports = class Page extends Model { ...@@ -90,7 +95,7 @@ module.exports = class Page extends Model {
}, },
creator: { creator: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'pages.creatorId', from: 'pages.creatorId',
to: 'users.id' to: 'users.id'
...@@ -98,7 +103,7 @@ module.exports = class Page extends Model { ...@@ -98,7 +103,7 @@ module.exports = class Page extends Model {
}, },
locale: { locale: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./locales'), modelClass: Locale,
join: { join: {
from: 'pages.localeCode', from: 'pages.localeCode',
to: 'locales.code' to: 'locales.code'
...@@ -162,7 +167,7 @@ module.exports = class Page extends Model { ...@@ -162,7 +167,7 @@ module.exports = class Page extends Model {
* @returns {string} Page Contents with Injected Metadata * @returns {string} Page Contents with Injected Metadata
*/ */
injectMetadata () { injectMetadata () {
return pageHelper.injectPageMetadata(this) return injectPageMetadata(this)
} }
/** /**
...@@ -171,7 +176,7 @@ module.exports = class Page extends Model { ...@@ -171,7 +176,7 @@ module.exports = class Page extends Model {
* @returns {string} File Extension * @returns {string} File Extension
*/ */
getFileExtension() { getFileExtension() {
return pageHelper.getFileExtension(this.contentType) return getFileExtension(this.contentType)
} }
/** /**
...@@ -312,7 +317,7 @@ module.exports = class Page extends Model { ...@@ -312,7 +317,7 @@ module.exports = class Page extends Model {
contentType: WIKI.data.editors[opts.editor]?.contentType ?? 'text', contentType: WIKI.data.editors[opts.editor]?.contentType ?? 'text',
description: opts.description, description: opts.description,
editor: opts.editor, editor: opts.editor,
hash: pageHelper.generateHash({ path: opts.path, locale: opts.locale }), hash: generateHash({ path: opts.path, locale: opts.locale }),
icon: opts.icon, icon: opts.icon,
isBrowsable: opts.isBrowsable ?? true, isBrowsable: opts.isBrowsable ?? true,
localeCode: opts.locale, localeCode: opts.locale,
...@@ -561,7 +566,7 @@ module.exports = class Page extends Model { ...@@ -561,7 +566,7 @@ module.exports = class Page extends Model {
})) { })) {
patch.scripts = { patch.scripts = {
...patch.scripts ?? ogPage.scripts ?? {}, ...patch.scripts ?? ogPage.scripts ?? {},
css: !_.isEmpty(opts.patch.scriptCss) ? new CleanCSS({ inline: false }).minify(opts.patch.scriptCss).styles : '' css: !isEmpty(opts.patch.scriptCss) ? new CleanCSS({ inline: false }).minify(opts.patch.scriptCss).styles : ''
} }
historyData.affectedFields.push('scripts.css') historyData.affectedFields.push('scripts.css')
} }
...@@ -698,7 +703,7 @@ module.exports = class Page extends Model { ...@@ -698,7 +703,7 @@ module.exports = class Page extends Model {
// -> Check content type // -> Check content type
const sourceContentType = ogPage.contentType const sourceContentType = ogPage.contentType
const targetContentType = _.get(_.find(WIKI.data.editors, ['key', opts.editor]), `contentType`, 'text') const targetContentType = get(find(WIKI.data.editors, ['key', opts.editor]), `contentType`, 'text')
const shouldConvert = sourceContentType !== targetContentType const shouldConvert = sourceContentType !== targetContentType
let convertedContent = null let convertedContent = null
...@@ -846,7 +851,7 @@ module.exports = class Page extends Model { ...@@ -846,7 +851,7 @@ module.exports = class Page extends Model {
*/ */
static async movePage(opts) { static async movePage(opts) {
let page let page
if (_.has(opts, 'id')) { if (has(opts, 'id')) {
page = await WIKI.db.pages.query().findById(opts.id) page = await WIKI.db.pages.query().findById(opts.id)
} else { } else {
page = await WIKI.db.pages.query().findOne({ page = await WIKI.db.pages.query().findOne({
...@@ -904,7 +909,7 @@ module.exports = class Page extends Model { ...@@ -904,7 +909,7 @@ module.exports = class Page extends Model {
versionDate: page.updatedAt versionDate: page.updatedAt
}) })
const destinationHash = pageHelper.generateHash({ path: opts.destinationPath, locale: opts.destinationLocale }) const destinationHash = generateHash({ path: opts.destinationPath, locale: opts.destinationLocale })
// -> Move page // -> Move page
const destinationTitle = (page.title === page.path ? opts.destinationPath : page.title) const destinationTitle = (page.title === page.path ? opts.destinationPath : page.title)
...@@ -970,7 +975,7 @@ module.exports = class Page extends Model { ...@@ -970,7 +975,7 @@ module.exports = class Page extends Model {
* @returns {Promise} Promise with no value * @returns {Promise} Promise with no value
*/ */
static async deletePage(opts) { static async deletePage(opts) {
const page = await WIKI.db.pages.getPageFromDb(_.has(opts, 'id') ? opts.id : opts) const page = await WIKI.db.pages.getPageFromDb(has(opts, 'id') ? opts.id : opts)
if (!page) { if (!page) {
throw new WIKI.Error.PageNotFound() throw new WIKI.Error.PageNotFound()
} }
...@@ -1209,7 +1214,7 @@ module.exports = class Page extends Model { ...@@ -1209,7 +1214,7 @@ module.exports = class Page extends Model {
*/ */
static async savePageToCache(page) { static async savePageToCache(page) {
const cachePath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${page.hash}.bin`) const cachePath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${page.hash}.bin`)
await fs.outputFile(cachePath, WIKI.db.pages.cacheSchema.encode({ await fse.outputFile(cachePath, WIKI.db.pages.cacheSchema.encode({
id: page.id, id: page.id,
authorId: page.authorId, authorId: page.authorId,
authorName: page.authorName, authorName: page.authorName,
...@@ -1219,17 +1224,17 @@ module.exports = class Page extends Model { ...@@ -1219,17 +1224,17 @@ module.exports = class Page extends Model {
description: page.description, description: page.description,
editor: page.editor, editor: page.editor,
extra: { extra: {
css: _.get(page, 'extra.css', ''), css: get(page, 'extra.css', ''),
js: _.get(page, 'extra.js', '') js: get(page, 'extra.js', '')
}, },
publishState: page.publishState ?? '', publishState: page.publishState ?? '',
publishEndDate: page.publishEndDate ?? '', publishEndDate: page.publishEndDate ?? '',
publishStartDate: page.publishStartDate ?? '', publishStartDate: page.publishStartDate ?? '',
render: page.render, render: page.render,
siteId: page.siteId, siteId: page.siteId,
tags: page.tags.map(t => _.pick(t, ['tag'])), tags: page.tags.map(t => pick(t, ['tag'])),
title: page.title, title: page.title,
toc: _.isString(page.toc) ? page.toc : JSON.stringify(page.toc), toc: isString(page.toc) ? page.toc : JSON.stringify(page.toc),
updatedAt: page.updatedAt.toISOString() updatedAt: page.updatedAt.toISOString()
})) }))
} }
...@@ -1241,11 +1246,11 @@ module.exports = class Page extends Model { ...@@ -1241,11 +1246,11 @@ module.exports = class Page extends Model {
* @returns {Promise} Promise of the Page Model Instance * @returns {Promise} Promise of the Page Model Instance
*/ */
static async getPageFromCache(opts) { static async getPageFromCache(opts) {
const pageHash = pageHelper.generateHash({ path: opts.path, locale: opts.locale }) const pageHash = generateHash({ path: opts.path, locale: opts.locale })
const cachePath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${pageHash}.bin`) const cachePath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${pageHash}.bin`)
try { try {
const pageBuffer = await fs.readFile(cachePath) const pageBuffer = await fse.readFile(cachePath)
let page = WIKI.db.pages.cacheSchema.decode(pageBuffer) let page = WIKI.db.pages.cacheSchema.decode(pageBuffer)
return { return {
...page, ...page,
...@@ -1268,14 +1273,14 @@ module.exports = class Page extends Model { ...@@ -1268,14 +1273,14 @@ module.exports = class Page extends Model {
* @returns {Promise} Promise with no value * @returns {Promise} Promise with no value
*/ */
static async deletePageFromCache(hash) { static async deletePageFromCache(hash) {
return fs.remove(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${hash}.bin`)) return fse.remove(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache/${hash}.bin`))
} }
/** /**
* Flush the contents of the Cache * Flush the contents of the Cache
*/ */
static async flushCache() { static async flushCache() {
return fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache`)) return fse.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `cache`))
} }
/** /**
......
const Model = require('objection').Model import { Model } from 'objection'
const path = require('path') import path from 'node:path'
const fs = require('fs-extra') import fs from 'fs/promises'
const _ = require('lodash') import { clone, filter, find, get, has, reverse, some, transform, union } from 'lodash-es'
const yaml = require('js-yaml') import yaml from 'js-yaml'
const DepGraph = require('dependency-graph').DepGraph import { DepGraph } from 'dependency-graph'
const commonHelper = require('../helpers/common') import { parseModuleProps } from '../helpers/common.mjs'
/** /**
* Renderer model * Renderer model
*/ */
module.exports = class Renderer extends Model { export class Renderer extends Model {
static get tableName() { return 'renderers' } static get tableName() { return 'renderers' }
static get jsonSchema () { static get jsonSchema () {
...@@ -42,7 +42,7 @@ module.exports = class Renderer extends Model { ...@@ -42,7 +42,7 @@ module.exports = class Renderer extends Model {
const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/rendering', dir, 'definition.yml'), 'utf8') const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/rendering', dir, 'definition.yml'), 'utf8')
const defParsed = yaml.load(def) const defParsed = yaml.load(def)
defParsed.key = dir defParsed.key = dir
defParsed.props = commonHelper.parseModuleProps(defParsed.props) defParsed.props = parseModuleProps(defParsed.props)
WIKI.data.renderers.push(defParsed) WIKI.data.renderers.push(defParsed)
WIKI.logger.debug(`Loaded renderers module definition ${dir}: [ OK ]`) WIKI.logger.debug(`Loaded renderers module definition ${dir}: [ OK ]`)
} }
...@@ -65,20 +65,20 @@ module.exports = class Renderer extends Model { ...@@ -65,20 +65,20 @@ module.exports = class Renderer extends Model {
const newRenderers = [] const newRenderers = []
let updatedRenderers = 0 let updatedRenderers = 0
for (const renderer of WIKI.data.renderers) { for (const renderer of WIKI.data.renderers) {
if (!_.some(dbRenderers, ['module', renderer.key])) { if (!some(dbRenderers, ['module', renderer.key])) {
newRenderers.push({ newRenderers.push({
module: renderer.key, module: renderer.key,
isEnabled: renderer.enabledDefault ?? true, isEnabled: renderer.enabledDefault ?? true,
config: _.transform(renderer.props, (result, value, key) => { config: transform(renderer.props, (result, value, key) => {
result[key] = value.default result[key] = value.default
return result return result
}, {}) }, {})
}) })
} else { } else {
const rendererConfig = _.get(_.find(dbRenderers, ['module', renderer.key]), 'config', {}) const rendererConfig = get(find(dbRenderers, ['module', renderer.key]), 'config', {})
await WIKI.db.renderers.query().patch({ await WIKI.db.renderers.query().patch({
config: _.transform(renderer.props, (result, value, key) => { config: transform(renderer.props, (result, value, key) => {
if (!_.has(result, key)) { if (!has(result, key)) {
result[key] = value.default result[key] = value.default
} }
return result return result
...@@ -98,7 +98,7 @@ module.exports = class Renderer extends Model { ...@@ -98,7 +98,7 @@ module.exports = class Renderer extends Model {
// -> Delete removed Renderers // -> Delete removed Renderers
for (const renderer of dbRenderers) { for (const renderer of dbRenderers) {
if (!_.some(WIKI.data.renderers, ['key', renderer.module])) { if (!some(WIKI.data.renderers, ['key', renderer.module])) {
await WIKI.db.renderers.query().where('module', renderer.key).del() await WIKI.db.renderers.query().where('module', renderer.key).del()
WIKI.logger.info(`Removed renderer ${renderer.key} because it is no longer present in the modules folder: [ OK ]`) WIKI.logger.info(`Removed renderer ${renderer.key} because it is no longer present in the modules folder: [ OK ]`)
} }
...@@ -113,7 +113,7 @@ module.exports = class Renderer extends Model { ...@@ -113,7 +113,7 @@ module.exports = class Renderer extends Model {
const renderersDb = await WIKI.db.renderers.query().where('isEnabled', true) const renderersDb = await WIKI.db.renderers.query().where('isEnabled', true)
if (renderersDb && renderersDb.length > 0) { if (renderersDb && renderersDb.length > 0) {
const renderers = renderersDb.map(rdr => { const renderers = renderersDb.map(rdr => {
const renderer = _.find(WIKI.data.renderers, ['key', rdr.module]) const renderer = find(WIKI.data.renderers, ['key', rdr.module])
return { return {
...renderer, ...renderer,
config: rdr.config config: rdr.config
...@@ -121,8 +121,8 @@ module.exports = class Renderer extends Model { ...@@ -121,8 +121,8 @@ module.exports = class Renderer extends Model {
}) })
// Build tree // Build tree
const rawCores = _.filter(renderers, renderer => !_.has(renderer, 'dependsOn')).map(core => { const rawCores = filter(renderers, renderer => !has(renderer, 'dependsOn')).map(core => {
core.children = _.filter(renderers, ['dependsOn', core.key]) core.children = filter(renderers, ['dependsOn', core.key])
return core return core
}) })
...@@ -140,11 +140,11 @@ module.exports = class Renderer extends Model { ...@@ -140,11 +140,11 @@ module.exports = class Renderer extends Model {
}) })
// Filter unused cores // Filter unused cores
let activeCoreKeys = _.filter(rawCores, ['input', contentType]).map(core => core.key) let activeCoreKeys = filter(rawCores, ['input', contentType]).map(core => core.key)
_.clone(activeCoreKeys).map(coreKey => { clone(activeCoreKeys).map(coreKey => {
activeCoreKeys = _.union(activeCoreKeys, graph.dependenciesOf(coreKey)) activeCoreKeys = union(activeCoreKeys, graph.dependenciesOf(coreKey))
}) })
const activeCores = _.filter(rawCores, core => _.includes(activeCoreKeys, core.key)) const activeCores = filter(rawCores, core => activeCoreKeys.includes(core.key))
// Rebuild dependency graph with active cores // Rebuild dependency graph with active cores
const graphActive = new DepGraph({ circular: true }) const graphActive = new DepGraph({ circular: true })
...@@ -161,8 +161,8 @@ module.exports = class Renderer extends Model { ...@@ -161,8 +161,8 @@ module.exports = class Renderer extends Model {
// Reorder cores in reverse dependency order // Reorder cores in reverse dependency order
let orderedCores = [] let orderedCores = []
_.reverse(graphActive.overallOrder()).map(coreKey => { reverse(graphActive.overallOrder()).map(coreKey => {
orderedCores.push(_.find(rawCores, ['key', coreKey])) orderedCores.push(find(rawCores, ['key', coreKey]))
}) })
return orderedCores return orderedCores
......
const Model = require('objection').Model import { Model } from 'objection'
const _ = require('lodash') import { has, reduce, set } from 'lodash-es'
/** /**
* Settings model * Settings model
*/ */
module.exports = class Setting extends Model { export class Setting extends Model {
static get tableName() { return 'settings' } static get tableName() { return 'settings' }
static get idColumn() { return 'key' } static get idColumn() { return 'key' }
...@@ -26,8 +26,8 @@ module.exports = class Setting extends Model { ...@@ -26,8 +26,8 @@ module.exports = class Setting extends Model {
static async getConfig() { static async getConfig() {
const settings = await WIKI.db.settings.query() const settings = await WIKI.db.settings.query()
if (settings.length > 0) { if (settings.length > 0) {
return _.reduce(settings, (res, val, key) => { return reduce(settings, (res, val, key) => {
_.set(res, val.key, (_.has(val.value, 'v')) ? val.value.v : val.value) set(res, val.key, (has(val.value, 'v')) ? val.value.v : val.value)
return res return res
}, {}) }, {})
} else { } else {
......
const Model = require('objection').Model import { Model } from 'objection'
const crypto = require('crypto') import { defaultsDeep, keyBy } from 'lodash-es'
const pem2jwk = require('pem-jwk').pem2jwk
const _ = require('lodash')
/** /**
* Site model * Site model
*/ */
module.exports = class Site extends Model { export class Site extends Model {
static get tableName () { return 'sites' } static get tableName () { return 'sites' }
static get jsonSchema () { static get jsonSchema () {
...@@ -40,7 +38,7 @@ module.exports = class Site extends Model { ...@@ -40,7 +38,7 @@ module.exports = class Site extends Model {
static async reloadCache () { static async reloadCache () {
WIKI.logger.info('Reloading site configurations...') WIKI.logger.info('Reloading site configurations...')
const sites = await WIKI.db.sites.query().orderBy('id') const sites = await WIKI.db.sites.query().orderBy('id')
WIKI.sites = _.keyBy(sites, 'id') WIKI.sites = keyBy(sites, 'id')
WIKI.sitesMappings = {} WIKI.sitesMappings = {}
for (const site of sites) { for (const site of sites) {
WIKI.sitesMappings[site.hostname] = site.id WIKI.sitesMappings[site.hostname] = site.id
...@@ -52,7 +50,7 @@ module.exports = class Site extends Model { ...@@ -52,7 +50,7 @@ module.exports = class Site extends Model {
const newSite = await WIKI.db.sites.query().insertAndFetch({ const newSite = await WIKI.db.sites.query().insertAndFetch({
hostname, hostname,
isEnabled: true, isEnabled: true,
config: _.defaultsDeep(config, { config: defaultsDeep(config, {
title: 'My Wiki Site', title: 'My Wiki Site',
description: '', description: '',
company: '', company: '',
......
const Model = require('objection').Model import { Model } from 'objection'
const path = require('path') import path from 'node:path'
const fs = require('fs-extra') import fs from 'node:fs/promises'
const _ = require('lodash') import { capitalize, find, has, hasIn, uniq } from 'lodash-es'
const yaml = require('js-yaml') import yaml from 'js-yaml'
const commonHelper = require('../helpers/common')
/** /**
* Storage model * Storage model
*/ */
module.exports = class Storage extends Model { export class Storage extends Model {
static get tableName() { return 'storage' } static get tableName() { return 'storage' }
static get idColumn() { return 'id' } static get idColumn() { return 'id' }
...@@ -67,7 +66,7 @@ module.exports = class Storage extends Model { ...@@ -67,7 +66,7 @@ module.exports = class Storage extends Model {
* Ensure a storage module is loaded * Ensure a storage module is loaded
*/ */
static async ensureModule (moduleName) { static async ensureModule (moduleName) {
if (!_.has(WIKI.storage.modules, moduleName)) { if (!has(WIKI.storage.modules, moduleName)) {
try { try {
WIKI.storage.modules[moduleName] = require(`../modules/storage/${moduleName}/storage`) WIKI.storage.modules[moduleName] = require(`../modules/storage/${moduleName}/storage`)
WIKI.logger.debug(`Activated storage module ${moduleName}: [ OK ]`) WIKI.logger.debug(`Activated storage module ${moduleName}: [ OK ]`)
...@@ -87,7 +86,7 @@ module.exports = class Storage extends Model { ...@@ -87,7 +86,7 @@ module.exports = class Storage extends Model {
*/ */
static async initTargets () { static async initTargets () {
const dbTargets = await WIKI.db.storage.query().where('isEnabled', true) const dbTargets = await WIKI.db.storage.query().where('isEnabled', true)
const activeModules = _.uniq(dbTargets.map(t => t.module)) const activeModules = uniq(dbTargets.map(t => t.module))
try { try {
// -> Stop and delete existing jobs // -> Stop and delete existing jobs
// const prevjobs = _.remove(WIKI.scheduler.jobs, job => job.name === 'sync-storage') // const prevjobs = _.remove(WIKI.scheduler.jobs, job => job.name === 'sync-storage')
...@@ -168,7 +167,7 @@ module.exports = class Storage extends Model { ...@@ -168,7 +167,7 @@ module.exports = class Storage extends Model {
static async assetEvent({ event, asset }) { static async assetEvent({ event, asset }) {
try { try {
for (let target of this.targets) { for (let target of this.targets) {
await target.fn[`asset${_.capitalize(event)}`](asset) await target.fn[`asset${capitalize(event)}`](asset)
} }
} catch (err) { } catch (err) {
WIKI.logger.warn(err) WIKI.logger.warn(err)
...@@ -195,9 +194,9 @@ module.exports = class Storage extends Model { ...@@ -195,9 +194,9 @@ module.exports = class Storage extends Model {
static async executeAction(targetKey, handler) { static async executeAction(targetKey, handler) {
try { try {
const target = _.find(this.targets, ['key', targetKey]) const target = find(this.targets, ['key', targetKey])
if (target) { if (target) {
if (_.hasIn(target.fn, handler)) { if (hasIn(target.fn, handler)) {
await target.fn[handler]() await target.fn[handler]()
} else { } else {
throw new Error('Invalid Handler for Storage Target') throw new Error('Invalid Handler for Storage Target')
......
const Model = require('objection').Model import { Model } from 'objection'
const _ = require('lodash') import { concat, differenceBy, some, uniq } from 'lodash-es'
import { Page } from './pages.mjs'
/** /**
* Tags model * Tags model
*/ */
module.exports = class Tag extends Model { export class Tag extends Model {
static get tableName() { return 'tags' } static get tableName() { return 'tags' }
static get jsonSchema () { static get jsonSchema () {
...@@ -26,7 +28,7 @@ module.exports = class Tag extends Model { ...@@ -26,7 +28,7 @@ module.exports = class Tag extends Model {
return { return {
pages: { pages: {
relation: Model.ManyToManyRelation, relation: Model.ManyToManyRelation,
modelClass: require('./pages'), modelClass: Page,
join: { join: {
from: 'tags.id', from: 'tags.id',
through: { through: {
...@@ -52,15 +54,15 @@ module.exports = class Tag extends Model { ...@@ -52,15 +54,15 @@ module.exports = class Tag extends Model {
// Format tags // Format tags
tags = _.uniq(tags.map(t => _.trim(t).toLowerCase())) tags = uniq(tags.map(t => t.trim().toLowerCase()))
// Create missing tags // Create missing tags
const newTags = _.filter(tags, t => !_.some(existingTags, ['tag', t])).map(t => ({ tag: t })) const newTags = tags.filter(t => !some(existingTags, ['tag', t])).map(t => ({ tag: t }))
if (newTags.length > 0) { if (newTags.length > 0) {
if (WIKI.config.db.type === 'postgres') { if (WIKI.config.db.type === 'postgres') {
const createdTags = await WIKI.db.tags.query().insert(newTags) const createdTags = await WIKI.db.tags.query().insert(newTags)
existingTags = _.concat(existingTags, createdTags) existingTags = concat(existingTags, createdTags)
} else { } else {
for (const newTag of newTags) { for (const newTag of newTags) {
const createdTag = await WIKI.db.tags.query().insert(newTag) const createdTag = await WIKI.db.tags.query().insert(newTag)
...@@ -71,12 +73,12 @@ module.exports = class Tag extends Model { ...@@ -71,12 +73,12 @@ module.exports = class Tag extends Model {
// Fetch current page tags // Fetch current page tags
const targetTags = _.filter(existingTags, t => _.includes(tags, t.tag)) const targetTags = existingTags.filter(t => _.includes(tags, t.tag))
const currentTags = await page.$relatedQuery('tags') const currentTags = await page.$relatedQuery('tags')
// Tags to relate // Tags to relate
const tagsToRelate = _.differenceBy(targetTags, currentTags, 'id') const tagsToRelate = differenceBy(targetTags, currentTags, 'id')
if (tagsToRelate.length > 0) { if (tagsToRelate.length > 0) {
if (WIKI.config.db.type === 'postgres') { if (WIKI.config.db.type === 'postgres') {
await page.$relatedQuery('tags').relate(tagsToRelate) await page.$relatedQuery('tags').relate(tagsToRelate)
...@@ -89,9 +91,9 @@ module.exports = class Tag extends Model { ...@@ -89,9 +91,9 @@ module.exports = class Tag extends Model {
// Tags to unrelate // Tags to unrelate
const tagsToUnrelate = _.differenceBy(currentTags, targetTags, 'id') const tagsToUnrelate = differenceBy(currentTags, targetTags, 'id')
if (tagsToUnrelate.length > 0) { if (tagsToUnrelate.length > 0) {
await page.$relatedQuery('tags').unrelate().whereIn('tags.id', _.map(tagsToUnrelate, 'id')) await page.$relatedQuery('tags').unrelate().whereIn('tags.id', tagsToUnrelate.map(t => t.id))
} }
page.tags = targetTags page.tags = targetTags
......
const Model = require('objection').Model import { Model } from 'objection'
const _ = require('lodash') import { differenceWith, dropRight, last, nth } from 'lodash-es'
import { decodeTreePath, encodeTreePath, generateHash } from '../helpers/common.mjs'
const commonHelper = require('../helpers/common') import { Locale } from './locales.mjs'
import { Site } from './sites.mjs'
const rePathName = /^[a-z0-9-]+$/ const rePathName = /^[a-z0-9-]+$/
const reTitle = /^[^<>"]+$/ const reTitle = /^[^<>"]+$/
...@@ -9,7 +11,7 @@ const reTitle = /^[^<>"]+$/ ...@@ -9,7 +11,7 @@ const reTitle = /^[^<>"]+$/
/** /**
* Tree model * Tree model
*/ */
module.exports = class Tree extends Model { export class Tree extends Model {
static get tableName() { return 'tree' } static get tableName() { return 'tree' }
static get jsonSchema () { static get jsonSchema () {
...@@ -37,7 +39,7 @@ module.exports = class Tree extends Model { ...@@ -37,7 +39,7 @@ module.exports = class Tree extends Model {
return { return {
locale: { locale: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./locales'), modelClass: Locale,
join: { join: {
from: 'tree.localeCode', from: 'tree.localeCode',
to: 'locales.code' to: 'locales.code'
...@@ -45,7 +47,7 @@ module.exports = class Tree extends Model { ...@@ -45,7 +47,7 @@ module.exports = class Tree extends Model {
}, },
site: { site: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./sites'), modelClass: Site,
join: { join: {
from: 'tree.siteId', from: 'tree.siteId',
to: 'sites.id' to: 'sites.id'
...@@ -82,11 +84,11 @@ module.exports = class Tree extends Model { ...@@ -82,11 +84,11 @@ module.exports = class Tree extends Model {
return parent return parent
} else { } else {
// Get by path // Get by path
const parentPath = commonHelper.encodeTreePath(path) const parentPath = encodeTreePath(path)
const parentPathParts = parentPath.split('.') const parentPathParts = parentPath.split('.')
const parentFilter = { const parentFilter = {
folderPath: _.dropRight(parentPathParts).join('.'), folderPath: dropRight(parentPathParts).join('.'),
fileName: _.last(parentPathParts) fileName: last(parentPathParts)
} }
const parent = await WIKI.db.knex('tree').where({ const parent = await WIKI.db.knex('tree').where({
...parentFilter, ...parentFilter,
...@@ -133,7 +135,7 @@ module.exports = class Tree extends Model { ...@@ -133,7 +135,7 @@ module.exports = class Tree extends Model {
folderPath: '', folderPath: '',
fileName: '' fileName: ''
} }
const folderPath = commonHelper.decodeTreePath(folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName) const folderPath = decodeTreePath(folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName)
const fullPath = folderPath ? `${folderPath}/${fileName}` : fileName const fullPath = folderPath ? `${folderPath}/${fileName}` : fileName
WIKI.logger.debug(`Adding page ${fullPath} to tree...`) WIKI.logger.debug(`Adding page ${fullPath} to tree...`)
...@@ -144,7 +146,7 @@ module.exports = class Tree extends Model { ...@@ -144,7 +146,7 @@ module.exports = class Tree extends Model {
fileName, fileName,
type: 'page', type: 'page',
title: title, title: title,
hash: commonHelper.generateHash(fullPath), hash: generateHash(fullPath),
localeCode: locale, localeCode: locale,
siteId, siteId,
meta meta
...@@ -177,7 +179,7 @@ module.exports = class Tree extends Model { ...@@ -177,7 +179,7 @@ module.exports = class Tree extends Model {
fileName: '' fileName: ''
} }
const folderPath = folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName const folderPath = folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName
const decodedFolderPath = commonHelper.decodeTreePath(folderPath) const decodedFolderPath = decodeTreePath(folderPath)
const fullPath = decodedFolderPath ? `${decodedFolderPath}/${fileName}` : fileName const fullPath = decodedFolderPath ? `${decodedFolderPath}/${fileName}` : fileName
WIKI.logger.debug(`Adding asset ${fullPath} to tree...`) WIKI.logger.debug(`Adding asset ${fullPath} to tree...`)
...@@ -188,7 +190,7 @@ module.exports = class Tree extends Model { ...@@ -188,7 +190,7 @@ module.exports = class Tree extends Model {
fileName, fileName,
type: 'asset', type: 'asset',
title: title, title: title,
hash: commonHelper.generateHash(fullPath), hash: generateHash(fullPath),
localeCode: locale, localeCode: locale,
siteId, siteId,
meta meta
...@@ -219,12 +221,12 @@ module.exports = class Tree extends Model { ...@@ -219,12 +221,12 @@ module.exports = class Tree extends Model {
throw new Error('ERR_INVALID_TITLE') throw new Error('ERR_INVALID_TITLE')
} }
parentPath = commonHelper.encodeTreePath(parentPath) parentPath = encodeTreePath(parentPath)
WIKI.logger.debug(`Creating new folder ${pathName}...`) WIKI.logger.debug(`Creating new folder ${pathName}...`)
const parentPathParts = parentPath.split('.') const parentPathParts = parentPath.split('.')
const parentFilter = { const parentFilter = {
folderPath: _.dropRight(parentPathParts).join('.'), folderPath: dropRight(parentPathParts).join('.'),
fileName: _.last(parentPathParts) fileName: last(parentPathParts)
} }
// Get parent path // Get parent path
...@@ -259,8 +261,8 @@ module.exports = class Tree extends Model { ...@@ -259,8 +261,8 @@ module.exports = class Tree extends Model {
const parentPathParts = parentPath.split('.') const parentPathParts = parentPath.split('.')
for (let i = 1; i <= parentPathParts.length; i++) { for (let i = 1; i <= parentPathParts.length; i++) {
const ancestor = { const ancestor = {
folderPath: _.dropRight(parentPathParts, i).join('.'), folderPath: dropRight(parentPathParts, i).join('.'),
fileName: _.nth(parentPathParts, i * -1) fileName: nth(parentPathParts, i * -1)
} }
expectedAncestors.push(ancestor) expectedAncestors.push(ancestor)
builder.orWhere({ builder.orWhere({
...@@ -269,14 +271,14 @@ module.exports = class Tree extends Model { ...@@ -269,14 +271,14 @@ module.exports = class Tree extends Model {
}) })
} }
}) })
for (const ancestor of _.differenceWith(expectedAncestors, existingAncestors, (expAnc, exsAnc) => expAnc.folderPath === exsAnc.folderPath && expAnc.fileName === exsAnc.fileName)) { for (const ancestor of differenceWith(expectedAncestors, existingAncestors, (expAnc, exsAnc) => expAnc.folderPath === exsAnc.folderPath && expAnc.fileName === exsAnc.fileName)) {
WIKI.logger.debug(`Creating missing parent folder ${ancestor.fileName} at path /${ancestor.folderPath}...`) WIKI.logger.debug(`Creating missing parent folder ${ancestor.fileName} at path /${ancestor.folderPath}...`)
const newAncestorFullPath = ancestor.folderPath ? `${commonHelper.decodeTreePath(ancestor.folderPath)}/${ancestor.fileName}` : ancestor.fileName const newAncestorFullPath = ancestor.folderPath ? `${decodeTreePath(ancestor.folderPath)}/${ancestor.fileName}` : ancestor.fileName
const newAncestor = await WIKI.db.knex('tree').insert({ const newAncestor = await WIKI.db.knex('tree').insert({
...ancestor, ...ancestor,
type: 'folder', type: 'folder',
title: ancestor.fileName, title: ancestor.fileName,
hash: commonHelper.generateHash(newAncestorFullPath), hash: generateHash(newAncestorFullPath),
localeCode: locale, localeCode: locale,
siteId: siteId, siteId: siteId,
meta: { meta: {
...@@ -292,13 +294,13 @@ module.exports = class Tree extends Model { ...@@ -292,13 +294,13 @@ module.exports = class Tree extends Model {
} }
// Create folder // Create folder
const fullPath = parentPath ? `${commonHelper.decodeTreePath(parentPath)}/${pathName}` : pathName const fullPath = parentPath ? `${decodeTreePath(parentPath)}/${pathName}` : pathName
const folder = await WIKI.db.knex('tree').insert({ const folder = await WIKI.db.knex('tree').insert({
folderPath: parentPath, folderPath: parentPath,
fileName: pathName, fileName: pathName,
type: 'folder', type: 'folder',
title: title, title: title,
hash: commonHelper.generateHash(fullPath), hash: generateHash(fullPath),
localeCode: locale, localeCode: locale,
siteId: siteId, siteId: siteId,
meta: { meta: {
...@@ -375,11 +377,11 @@ module.exports = class Tree extends Model { ...@@ -375,11 +377,11 @@ module.exports = class Tree extends Model {
}) })
// Rename the folder itself // Rename the folder itself
const fullPath = folder.folderPath ? `${commonHelper.decodeTreePath(folder.folderPath)}/${pathName}` : pathName const fullPath = folder.folderPath ? `${decodeTreePath(folder.folderPath)}/${pathName}` : pathName
await WIKI.db.knex('tree').where('id', folder.id).update({ await WIKI.db.knex('tree').where('id', folder.id).update({
fileName: pathName, fileName: pathName,
title: title, title: title,
hash: commonHelper.generateHash(fullPath) hash: generateHash(fullPath)
}) })
} else { } else {
// Update the folder title only // Update the folder title only
...@@ -437,8 +439,8 @@ module.exports = class Tree extends Model { ...@@ -437,8 +439,8 @@ module.exports = class Tree extends Model {
if (folder.folderPath) { if (folder.folderPath) {
const parentPathParts = folder.folderPath.split('.') const parentPathParts = folder.folderPath.split('.')
const parent = await WIKI.db.knex('tree').where({ const parent = await WIKI.db.knex('tree').where({
folderPath: _.dropRight(parentPathParts).join('.'), folderPath: dropRight(parentPathParts).join('.'),
fileName: _.last(parentPathParts) fileName: last(parentPathParts)
}).first() }).first()
await WIKI.db.knex('tree').where('id', parent.id).update({ await WIKI.db.knex('tree').where('id', parent.id).update({
meta: { meta: {
......
/* global WIKI */ /* global WIKI */
const Model = require('objection').Model import { Model } from 'objection'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
const { nanoid } = require('nanoid') import { nanoid } from 'nanoid'
import { User } from './users.mjs'
/** /**
* Users model * Users model
*/ */
module.exports = class UserKey extends Model { export class UserKey extends Model {
static get tableName() { return 'userKeys' } static get tableName() { return 'userKeys' }
static get jsonSchema () { static get jsonSchema () {
...@@ -29,7 +31,7 @@ module.exports = class UserKey extends Model { ...@@ -29,7 +31,7 @@ module.exports = class UserKey extends Model {
return { return {
user: { user: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: require('./users'), modelClass: User,
join: { join: {
from: 'userKeys.userId', from: 'userKeys.userId',
to: 'users.id' to: 'users.id'
......
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
"version": "3.0.0", "version": "3.0.0",
"releaseDate": "2023-01-01T01:01:01.000Z", "releaseDate": "2023-01-01T01:01:01.000Z",
"description": "The most powerful and extensible open source Wiki software", "description": "The most powerful and extensible open source Wiki software",
"main": "index.js", "main": "index.mjs",
"type": "module",
"private": true, "private": true,
"dev": true, "dev": true,
"scripts": { "scripts": {
"start": "cd .. && node server", "start": "cd .. && node server",
"dev": "cd .. && nodemon server --watch server --ext js,json,graphql,gql" "dev": "cd .. && nodemon server --watch server --ext mjs,js,json,graphql,gql"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -33,94 +34,91 @@ ...@@ -33,94 +34,91 @@
"node": ">=18.0" "node": ">=18.0"
}, },
"dependencies": { "dependencies": {
"@azure/storage-blob": "12.11.0", "@azure/storage-blob": "12.13.0",
"@exlinc/keycloak-passport": "1.0.2", "@exlinc/keycloak-passport": "1.0.2",
"@graphql-tools/schema": "8.3.7", "@graphql-tools/schema": "9.0.17",
"@graphql-tools/utils": "8.6.6", "@graphql-tools/utils": "9.2.1",
"@joplin/turndown-plugin-gfm": "1.0.44", "@joplin/turndown-plugin-gfm": "1.0.47",
"@root/csr": "0.8.1", "@root/csr": "0.8.1",
"@root/keypairs": "0.10.3", "@root/keypairs": "0.10.3",
"@root/pem": "1.0.4", "@root/pem": "1.0.4",
"acme": "3.0.3", "acme": "3.0.3",
"akismet-api": "5.3.0", "akismet-api": "6.0.0",
"apollo-fetch": "0.7.0", "apollo-fetch": "0.7.0",
"apollo-server": "3.6.7", "apollo-server": "3.6.7",
"apollo-server-express": "3.6.7", "apollo-server-express": "3.6.7",
"auto-load": "3.0.4", "auto-load": "3.0.4",
"aws-sdk": "2.1208.0", "aws-sdk": "2.1353.0",
"bcryptjs-then": "1.0.1", "bcryptjs-then": "1.0.1",
"body-parser": "1.20.0", "body-parser": "1.20.2",
"chalk": "4.1.2", "chalk": "5.2.0",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"chokidar": "3.5.3", "chokidar": "3.5.3",
"chromium-pickle-js": "0.2.0", "chromium-pickle-js": "0.2.0",
"clean-css": "4.2.3", "clean-css": "5.3.2",
"command-exists": "1.2.9", "command-exists": "1.2.9",
"compression": "1.7.4", "compression": "1.7.4",
"connect-session-knex": "3.0.0", "connect-session-knex": "3.0.1",
"cookie-parser": "1.4.6", "cookie-parser": "1.4.6",
"cors": "2.8.5", "cors": "2.8.5",
"cron-parser": "4.6.0", "cron-parser": "4.8.1",
"cuint": "0.2.2", "cuint": "0.2.2",
"custom-error-instance": "2.1.2", "custom-error-instance": "2.1.2",
"dependency-graph": "0.9.0", "dependency-graph": "0.11.0",
"diff": "4.0.2", "diff": "5.1.0",
"diff2html": "3.1.14", "diff2html": "3.4.34",
"dompurify": "2.4.0", "dompurify": "3.0.1",
"dotize": "0.3.0", "dotize": "0.3.0",
"emoji-regex": "10.1.0", "emoji-regex": "10.2.1",
"eventemitter2": "6.4.7", "eventemitter2": "6.4.9",
"express": "4.18.1", "express": "4.18.2",
"express-brute": "1.0.1", "express-brute": "1.0.1",
"express-session": "1.17.3", "express-session": "1.17.3",
"file-type": "15.0.1", "file-type": "18.2.1",
"filesize": "6.1.0", "filesize": "10.0.7",
"fs-extra": "9.0.1", "fs-extra": "11.1.1",
"getos": "3.2.1", "getos": "3.2.1",
"graphql": "16.3.0", "graphql": "16.6.0",
"graphql-list-fields": "2.0.2", "graphql-list-fields": "2.0.2",
"graphql-rate-limit-directive": "2.0.2", "graphql-rate-limit-directive": "2.0.3",
"graphql-tools": "8.2.5", "graphql-tools": "8.3.19",
"graphql-upload": "13.0.0", "graphql-upload": "16.0.2",
"he": "1.2.0", "he": "1.2.0",
"highlight.js": "10.3.1", "highlight.js": "11.7.0",
"i18next": "19.8.3", "i18next": "22.4.14",
"i18next-node-fs-backend": "2.1.3", "i18next-node-fs-backend": "2.1.3",
"image-size": "0.9.2", "image-size": "1.0.2",
"js-base64": "3.7.2", "js-base64": "3.7.5",
"js-binary": "1.2.0", "js-binary": "1.2.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsdom": "16.4.0", "jsdom": "21.1.1",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "9.0.0",
"katex": "0.12.0", "katex": "0.16.4",
"klaw": "4.0.1", "klaw": "4.1.0",
"knex": "2.3.0", "knex": "2.4.2",
"lodash": "4.17.21", "lodash": "4.17.21",
"luxon": "2.3.1", "lodash-es": "4.17.21",
"markdown-it": "11.0.1", "luxon": "3.3.0",
"markdown-it": "13.0.1",
"markdown-it-abbr": "1.0.4", "markdown-it-abbr": "1.0.4",
"markdown-it-attrs": "3.0.3", "markdown-it-emoji": "2.0.2",
"markdown-it-emoji": "1.4.0",
"markdown-it-expand-tabs": "1.0.13", "markdown-it-expand-tabs": "1.0.13",
"markdown-it-external-links": "0.0.6", "markdown-it-external-links": "0.0.6",
"markdown-it-footnote": "3.0.3", "markdown-it-footnote": "3.0.3",
"markdown-it-imsize": "2.0.1", "markdown-it-imsize": "2.0.1",
"markdown-it-mark": "3.0.1", "markdown-it-mark": "3.0.1",
"markdown-it-mathjax": "2.0.0", "markdown-it-mathjax": "2.0.0",
"markdown-it-multimd-table": "4.0.3",
"markdown-it-sub": "1.0.0", "markdown-it-sub": "1.0.0",
"markdown-it-sup": "1.0.0", "markdown-it-sup": "1.0.0",
"markdown-it-task-lists": "2.1.1", "markdown-it-task-lists": "2.1.1",
"mathjax": "3.1.2", "mathjax": "3.2.2",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"moment": "2.29.2",
"moment-timezone": "0.5.31",
"ms": "2.1.3", "ms": "2.1.3",
"multer": "1.4.4", "multer": "1.4.4",
"nanoid": "3.3.2", "nanoid": "4.0.2",
"node-2fa": "1.1.2", "node-2fa": "2.0.3",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "6.7.8", "nodemailer": "6.9.1",
"objection": "3.0.1", "objection": "3.0.1",
"passport": "0.6.0", "passport": "0.6.0",
"passport-auth0": "1.4.3", "passport-auth0": "1.4.3",
...@@ -132,60 +130,59 @@ ...@@ -132,60 +130,59 @@
"passport-github2": "0.1.12", "passport-github2": "0.1.12",
"passport-gitlab2": "5.0.0", "passport-gitlab2": "5.0.0",
"passport-google-oauth20": "2.0.0", "passport-google-oauth20": "2.0.0",
"passport-jwt": "4.0.0", "passport-jwt": "4.0.1",
"passport-ldapauth": "3.0.1", "passport-ldapauth": "3.0.1",
"passport-local": "1.0.0", "passport-local": "1.0.0",
"passport-microsoft": "1.0.0", "passport-microsoft": "1.0.0",
"passport-oauth2": "1.6.1", "passport-oauth2": "1.7.0",
"passport-okta-oauth": "0.0.1", "passport-okta-oauth": "0.0.1",
"passport-openidconnect": "0.1.1", "passport-openidconnect": "0.1.1",
"passport-saml": "3.2.1", "passport-saml": "3.2.1",
"passport-slack-oauth2": "1.1.1", "passport-slack-oauth2": "1.1.1",
"passport-twitch-strategy": "2.2.0", "passport-twitch-strategy": "2.2.0",
"pem-jwk": "2.0.0", "pem-jwk": "2.0.0",
"pg": "8.8.0", "pg": "8.10.0",
"pg-hstore": "2.3.4", "pg-hstore": "2.3.4",
"pg-pubsub": "0.8.0", "pg-pubsub": "0.8.1",
"pg-query-stream": "4.2.4", "pg-query-stream": "4.4.0",
"pg-tsquery": "8.4.0", "pg-tsquery": "8.4.1",
"poolifier": "2.2.0", "poolifier": "2.4.4",
"pug": "3.0.2", "pug": "3.0.2",
"punycode": "2.1.1", "punycode": "2.3.0",
"puppeteer-core": "17.1.3", "puppeteer-core": "19.8.5",
"qr-image": "3.2.0", "qr-image": "3.2.0",
"rate-limiter-flexible": "2.3.8", "rate-limiter-flexible": "2.4.1",
"remove-markdown": "0.3.0", "remove-markdown": "0.5.0",
"request": "2.88.2", "request": "2.88.2",
"request-promise": "4.2.6", "request-promise": "4.2.6",
"safe-regex": "2.1.1", "safe-regex": "2.1.1",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"scim-query-filter-parser": "2.0.4", "scim-query-filter-parser": "2.0.4",
"semver": "7.3.7", "semver": "7.3.8",
"serve-favicon": "2.5.0", "serve-favicon": "2.5.0",
"sharp": "0.31.0", "sharp": "0.32.0",
"simple-git": "2.21.0", "simple-git": "3.17.0",
"socket.io": "4.5.2", "socket.io": "4.6.1",
"ssh2": "1.9.0", "ssh2": "1.11.0",
"ssh2-promise": "1.0.2", "ssh2-promise": "1.0.3",
"striptags": "3.2.0", "striptags": "3.2.0",
"tar-fs": "2.1.1", "tar-fs": "2.1.1",
"turndown": "7.1.1", "turndown": "7.1.2",
"twemoji": "13.1.0",
"uslug": "1.0.4", "uslug": "1.0.4",
"uuid": "8.3.2", "uuid": "9.0.0",
"validate.js": "0.13.1", "validate.js": "0.13.1",
"xss": "1.0.14", "xss": "1.0.14",
"yargs": "16.1.0" "yargs": "17.7.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "7.12.0", "eslint": "8.38.0",
"eslint-config-requarks": "1.0.7", "eslint-config-requarks": "1.0.7",
"eslint-config-standard": "15.0.0", "eslint-config-standard": "17.0.0",
"eslint-plugin-import": "2.22.1", "eslint-plugin-import": "2.27.5",
"eslint-plugin-node": "11.1.0", "eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "4.2.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-standard": "4.0.2", "eslint-plugin-standard": "4.1.0",
"nodemon": "2.0.15" "nodemon": "2.0.22"
}, },
"collective": { "collective": {
"type": "opencollective", "type": "opencollective",
......
module.exports = async (payload) => { export async function task (payload) {
WIKI.logger.info('Checking for latest version...') WIKI.logger.info('Checking for latest version...')
try { try {
......
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
module.exports = async (payload) => { export async function task (payload) {
WIKI.logger.info('Cleaning scheduler job history...') WIKI.logger.info('Cleaning scheduler job history...')
try { try {
......
module.exports = async (payload) => { export async function task (payload) {
WIKI.logger.info('Fetching latest localization data...') WIKI.logger.info('Fetching latest localization data...')
try { try {
......
const path = require('node:path') import path from 'node:path'
const fs = require('fs-extra') import fse from 'fs-extra'
const { DateTime } = require('luxon') import { DateTime } from 'luxon'
module.exports = async ({ payload }) => { export async function task ({ payload }) {
WIKI.logger.info('Purging orphaned upload files...') WIKI.logger.info('Purging orphaned upload files...')
try { try {
const uplTempPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads') const uplTempPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads')
await fs.ensureDir(uplTempPath) await fse.ensureDir(uplTempPath)
const ls = await fs.readdir(uplTempPath) const ls = await fse.readdir(uplTempPath)
const fifteenAgo = DateTime.now().minus({ minutes: 15 }) const fifteenAgo = DateTime.now().minus({ minutes: 15 })
for (const f of ls) { for (const f of ls) {
const stat = fs.stat(path.join(uplTempPath, f)) const stat = fse.stat(path.join(uplTempPath, f))
if ((await stat).isFile && stat.ctime < fifteenAgo) { if ((await stat).isFile && stat.ctime < fifteenAgo) {
await fs.unlink(path.join(uplTempPath, f)) await fse.unlink(path.join(uplTempPath, f))
} }
} }
......
const _ = require('lodash') import { get, has, isEmpty, reduce, times, toSafeInteger } from 'lodash-es'
const cheerio = require('cheerio') import cheerio from 'cheerio'
module.exports = async ({ payload }) => { export async function task ({ payload }) {
WIKI.logger.info(`Rendering page ${payload.id}...`) WIKI.logger.info(`Rendering page ${payload.id}...`)
try { try {
...@@ -20,7 +20,7 @@ module.exports = async ({ payload }) => { ...@@ -20,7 +20,7 @@ module.exports = async ({ payload }) => {
let output = page.render let output = page.render
if (_.isEmpty(page.content)) { if (isEmpty(page.content)) {
WIKI.logger.warn(`Failed to render page ID ${payload.id} because content was empty: [ FAILED ]`) WIKI.logger.warn(`Failed to render page ID ${payload.id} because content was empty: [ FAILED ]`)
} }
...@@ -41,11 +41,11 @@ module.exports = async ({ payload }) => { ...@@ -41,11 +41,11 @@ module.exports = async ({ payload }) => {
let toc = { root: [] } let toc = { root: [] }
$('h1,h2,h3,h4,h5,h6').each((idx, el) => { $('h1,h2,h3,h4,h5,h6').each((idx, el) => {
const depth = _.toSafeInteger(el.name.substring(1)) - (isStrict ? 1 : 2) const depth = toSafeInteger(el.name.substring(1)) - (isStrict ? 1 : 2)
let leafPathError = false let leafPathError = false
const leafPath = _.reduce(_.times(depth), (curPath, curIdx) => { const leafPath = reduce(times(depth), (curPath, curIdx) => {
if (_.has(toc, curPath)) { if (has(toc, curPath)) {
const lastLeafIdx = _.get(toc, curPath).length - 1 const lastLeafIdx = _.get(toc, curPath).length - 1
if (lastLeafIdx >= 0) { if (lastLeafIdx >= 0) {
curPath = `${curPath}[${lastLeafIdx}].children` curPath = `${curPath}[${lastLeafIdx}].children`
...@@ -61,8 +61,8 @@ module.exports = async ({ payload }) => { ...@@ -61,8 +61,8 @@ module.exports = async ({ payload }) => {
const leafSlug = $('.toc-anchor', el).first().attr('href') const leafSlug = $('.toc-anchor', el).first().attr('href')
$('.toc-anchor', el).remove() $('.toc-anchor', el).remove()
_.get(toc, leafPath).push({ get(toc, leafPath).push({
label: _.trim($(el).text()), label: $(el).text().trim(),
key: leafSlug.substring(1), key: leafSlug.substring(1),
children: [] children: []
}) })
......
const autoload = require('auto-load') import bodyParser from 'body-parser'
const bodyParser = require('body-parser') import compression from 'compression'
const compression = require('compression') import cookieParser from 'cookie-parser'
const cookieParser = require('cookie-parser') import cors from 'cors'
const cors = require('cors') import express from 'express'
const express = require('express') import session from 'express-session'
const session = require('express-session') import KnexSessionStore from 'connect-session-knex'
const KnexSessionStore = require('connect-session-knex')(session) import favicon from 'serve-favicon'
const favicon = require('serve-favicon') import path from 'node:path'
const path = require('path') import { set } from 'lodash-es'
const _ = require('lodash')
import auth from './core/auth.mjs'
module.exports = async () => { import mail from './core/mail.mjs'
import system from './core/system.mjs'
import ctrlAuth from './controllers/auth.mjs'
import ctrlCommon from './controllers/common.mjs'
import ctrlSsl from './controllers/ssl.mjs'
import ctrlWs from './controllers/ws.mjs'
export async function init () {
// ---------------------------------------- // ----------------------------------------
// Load core modules // Load core modules
// ---------------------------------------- // ----------------------------------------
WIKI.auth = require('./core/auth').init() WIKI.auth = auth.init()
WIKI.mail = require('./core/mail').init() WIKI.mail = mail.init()
WIKI.system = require('./core/system').init() WIKI.system = system.init()
// ----------------------------------------
// Load middlewares
// ----------------------------------------
const mw = autoload(path.join(WIKI.SERVERPATH, '/middlewares'))
const ctrl = autoload(path.join(WIKI.SERVERPATH, '/controllers'))
// ---------------------------------------- // ----------------------------------------
// Define Express App // Define Express App
...@@ -41,24 +42,57 @@ module.exports = async () => { ...@@ -41,24 +42,57 @@ module.exports = async () => {
const useHTTPS = WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1' const useHTTPS = WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1'
await WIKI.servers.initHTTP() await WIKI.servers.initHTTP()
if (useHTTPS) { if (useHTTPS) {
await WIKI.servers.initHTTPS() await WIKI.servers.initHTTPS()
} }
await WIKI.servers.initWebSocket() await WIKI.servers.initWebSocket()
// ---------------------------------------- // ----------------------------------------
// Attach WebSocket Server // Attach WebSocket Server
// ---------------------------------------- // ----------------------------------------
ctrl.ws() ctrlWs()
// ---------------------------------------- // ----------------------------------------
// Security // Security
// ---------------------------------------- // ----------------------------------------
app.use(mw.security) app.use((req, res, next) => {
// -> Disable X-Powered-By
req.app.disable('x-powered-by')
// -> Disable Frame Embedding
if (WIKI.config.security.securityIframe) {
res.set('X-Frame-Options', 'deny')
}
// -> Re-enable XSS Fitler if disabled
res.set('X-XSS-Protection', '1; mode=block')
// -> Disable MIME-sniffing
res.set('X-Content-Type-Options', 'nosniff')
// -> Disable IE Compatibility Mode
res.set('X-UA-Compatible', 'IE=edge')
// -> Disables referrer header when navigating to a different origin
if (WIKI.config.security.securityReferrerPolicy) {
res.set('Referrer-Policy', 'same-origin')
}
// -> Enforce HSTS
if (WIKI.config.security.securityHSTS) {
res.set('Strict-Transport-Security', `max-age=${WIKI.config.security.securityHSTSDuration}; includeSubDomains`)
}
// -> Prevent Open Redirect from user provided URL
if (WIKI.config.security.securityOpenRedirect) {
// Strips out all repeating / character in the provided URL
req.url = req.url.replace(/(\/)(?=\/*\1)/g, '')
}
next()
})
app.use(cors({ origin: false })) app.use(cors({ origin: false }))
app.options('*', cors({ origin: false })) app.options('*', cors({ origin: false }))
if (WIKI.config.security.securityTrustProxy) { if (WIKI.config.security.securityTrustProxy) {
...@@ -86,7 +120,7 @@ module.exports = async () => { ...@@ -86,7 +120,7 @@ module.exports = async () => {
// SSL Handlers // SSL Handlers
// ---------------------------------------- // ----------------------------------------
app.use('/', ctrl.ssl) app.use('/', ctrlSsl())
// ---------------------------------------- // ----------------------------------------
// Passport Authentication // Passport Authentication
...@@ -97,7 +131,7 @@ module.exports = async () => { ...@@ -97,7 +131,7 @@ module.exports = async () => {
secret: WIKI.config.auth.secret, secret: WIKI.config.auth.secret,
resave: false, resave: false,
saveUninitialized: false, saveUninitialized: false,
store: new KnexSessionStore({ store: new KnexSessionStore(session)({
knex: WIKI.db.knex knex: WIKI.db.knex
}) })
})) }))
...@@ -115,7 +149,15 @@ module.exports = async () => { ...@@ -115,7 +149,15 @@ module.exports = async () => {
// SEO // SEO
// ---------------------------------------- // ----------------------------------------
app.use(mw.seo) app.use((req, res, next) => {
if (req.path.length > 1 && req.path.endsWith('/')) {
let query = req.url.slice(req.path.length) || ''
res.redirect(301, req.path.slice(0, -1) + query)
} else {
set(res.locals, 'pageMeta.url', `${WIKI.config.host}${req.path}`)
next()
}
})
// ---------------------------------------- // ----------------------------------------
// View Engine Setup // View Engine Setup
...@@ -177,9 +219,8 @@ module.exports = async () => { ...@@ -177,9 +219,8 @@ module.exports = async () => {
next() next()
}) })
app.use('/', ctrl.auth) app.use('/', ctrlAuth())
app.use('/', ctrl.upload) app.use('/', ctrlCommon())
app.use('/', ctrl.common)
// ---------------------------------------- // ----------------------------------------
// Error handling // Error handling
...@@ -202,7 +243,7 @@ module.exports = async () => { ...@@ -202,7 +243,7 @@ module.exports = async () => {
}) })
} else { } else {
res.status(err.status || 500) res.status(err.status || 500)
_.set(res.locals, 'pageMeta.title', 'Error') set(res.locals, 'pageMeta.title', 'Error')
res.render('error', { res.render('error', {
message: err.message, message: err.message,
error: WIKI.IS_DEBUG ? err : {} error: WIKI.IS_DEBUG ? err : {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment