|
@@ -4,6 +4,7 @@ const async = require('async')
|
|
|
const { promisify } = require('util')
|
|
const { promisify } = require('util')
|
|
|
const Settings = require('@overleaf/settings')
|
|
const Settings = require('@overleaf/settings')
|
|
|
const Views = require('./Views')
|
|
const Views = require('./Views')
|
|
|
|
|
+const _ = require('lodash')
|
|
|
|
|
|
|
|
const MODULE_BASE_PATH = Path.join(__dirname, '/../../../modules')
|
|
const MODULE_BASE_PATH = Path.join(__dirname, '/../../../modules')
|
|
|
|
|
|
|
@@ -13,27 +14,33 @@ const _hooks = {}
|
|
|
const _middleware = {}
|
|
const _middleware = {}
|
|
|
let _viewIncludes = {}
|
|
let _viewIncludes = {}
|
|
|
|
|
|
|
|
-function modules() {
|
|
|
|
|
|
|
+async function modules() {
|
|
|
if (!_modulesLoaded) {
|
|
if (!_modulesLoaded) {
|
|
|
- loadModules()
|
|
|
|
|
|
|
+ await loadModules()
|
|
|
}
|
|
}
|
|
|
return _modules
|
|
return _modules
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function loadModules() {
|
|
|
|
|
|
|
+async function loadModulesImpl() {
|
|
|
const settingsCheckModule = Path.join(
|
|
const settingsCheckModule = Path.join(
|
|
|
MODULE_BASE_PATH,
|
|
MODULE_BASE_PATH,
|
|
|
'settings-check',
|
|
'settings-check',
|
|
|
'index.js'
|
|
'index.js'
|
|
|
)
|
|
)
|
|
|
if (fs.existsSync(settingsCheckModule)) {
|
|
if (fs.existsSync(settingsCheckModule)) {
|
|
|
- require(settingsCheckModule)
|
|
|
|
|
|
|
+ await import(settingsCheckModule)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (const moduleName of Settings.moduleImportSequence || []) {
|
|
for (const moduleName of Settings.moduleImportSequence || []) {
|
|
|
- const loadedModule = require(
|
|
|
|
|
- Path.join(MODULE_BASE_PATH, moduleName, 'index.js')
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ let path
|
|
|
|
|
+ if (fs.existsSync(Path.join(MODULE_BASE_PATH, moduleName, 'index.mjs'))) {
|
|
|
|
|
+ path = Path.join(MODULE_BASE_PATH, moduleName, 'index.mjs')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ path = Path.join(MODULE_BASE_PATH, moduleName, 'index.js')
|
|
|
|
|
+ }
|
|
|
|
|
+ const module = await import(path)
|
|
|
|
|
+ const loadedModule = module.default || module
|
|
|
|
|
+
|
|
|
loadedModule.name = moduleName
|
|
loadedModule.name = moduleName
|
|
|
_modules.push(loadedModule)
|
|
_modules.push(loadedModule)
|
|
|
if (loadedModule.viewIncludes) {
|
|
if (loadedModule.viewIncludes) {
|
|
@@ -52,20 +59,26 @@ function loadModules() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
_modulesLoaded = true
|
|
_modulesLoaded = true
|
|
|
- attachHooks()
|
|
|
|
|
- attachMiddleware()
|
|
|
|
|
|
|
+ await attachHooks()
|
|
|
|
|
+ await attachMiddleware()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function applyRouter(webRouter, privateApiRouter, publicApiRouter) {
|
|
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+const loadModules = _.memoize(loadModulesImpl)
|
|
|
|
|
+
|
|
|
|
|
+async function applyRouter(webRouter, privateApiRouter, publicApiRouter) {
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
if (module.router && module.router.apply) {
|
|
if (module.router && module.router.apply) {
|
|
|
module.router.apply(webRouter, privateApiRouter, publicApiRouter)
|
|
module.router.apply(webRouter, privateApiRouter, publicApiRouter)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function applyNonCsrfRouter(webRouter, privateApiRouter, publicApiRouter) {
|
|
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+async function applyNonCsrfRouter(
|
|
|
|
|
+ webRouter,
|
|
|
|
|
+ privateApiRouter,
|
|
|
|
|
+ publicApiRouter
|
|
|
|
|
+) {
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
if (module.nonCsrfRouter != null) {
|
|
if (module.nonCsrfRouter != null) {
|
|
|
module.nonCsrfRouter.apply(webRouter, privateApiRouter, publicApiRouter)
|
|
module.nonCsrfRouter.apply(webRouter, privateApiRouter, publicApiRouter)
|
|
|
}
|
|
}
|
|
@@ -80,7 +93,7 @@ function applyNonCsrfRouter(webRouter, privateApiRouter, publicApiRouter) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function start() {
|
|
async function start() {
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
await module.start?.()
|
|
await module.start?.()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -89,13 +102,13 @@ function loadViewIncludes(app) {
|
|
|
_viewIncludes = Views.compileViewIncludes(app)
|
|
_viewIncludes = Views.compileViewIncludes(app)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function applyMiddleware(appOrRouter, middlewareName, options) {
|
|
|
|
|
|
|
+async function applyMiddleware(appOrRouter, middlewareName, options) {
|
|
|
if (!middlewareName) {
|
|
if (!middlewareName) {
|
|
|
throw new Error(
|
|
throw new Error(
|
|
|
'middleware name must be provided to register module middleware'
|
|
'middleware name must be provided to register module middleware'
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
if (module[middlewareName]) {
|
|
if (module[middlewareName]) {
|
|
|
module[middlewareName](appOrRouter, options)
|
|
module[middlewareName](appOrRouter, options)
|
|
|
}
|
|
}
|
|
@@ -115,9 +128,9 @@ function moduleIncludesAvailable(view) {
|
|
|
return (_viewIncludes[view] || []).length > 0
|
|
return (_viewIncludes[view] || []).length > 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function linkedFileAgentsIncludes() {
|
|
|
|
|
|
|
+async function linkedFileAgentsIncludes() {
|
|
|
const agents = {}
|
|
const agents = {}
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
for (const name in module.linkedFileAgents) {
|
|
for (const name in module.linkedFileAgents) {
|
|
|
const agentFunction = module.linkedFileAgents[name]
|
|
const agentFunction = module.linkedFileAgents[name]
|
|
|
agents[name] = agentFunction()
|
|
agents[name] = agentFunction()
|
|
@@ -126,8 +139,8 @@ function linkedFileAgentsIncludes() {
|
|
|
return agents
|
|
return agents
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function attachHooks() {
|
|
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+async function attachHooks() {
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
for (const hook in module.hooks || {}) {
|
|
for (const hook in module.hooks || {}) {
|
|
|
const method = module.hooks[hook]
|
|
const method = module.hooks[hook]
|
|
|
attachHook(hook, method)
|
|
attachHook(hook, method)
|
|
@@ -142,8 +155,8 @@ function attachHook(name, method) {
|
|
|
_hooks[name].push(method)
|
|
_hooks[name].push(method)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function attachMiddleware() {
|
|
|
|
|
- for (const module of modules()) {
|
|
|
|
|
|
|
+async function attachMiddleware() {
|
|
|
|
|
+ for (const module of await modules()) {
|
|
|
for (const middleware in module.middleware || {}) {
|
|
for (const middleware in module.middleware || {}) {
|
|
|
const method = module.middleware[middleware]
|
|
const method = module.middleware[middleware]
|
|
|
if (_middleware[middleware] == null) {
|
|
if (_middleware[middleware] == null) {
|
|
@@ -155,28 +168,37 @@ function attachMiddleware() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function fireHook(name, ...rest) {
|
|
function fireHook(name, ...rest) {
|
|
|
|
|
+ const adjustedLength = Math.max(rest.length, 1)
|
|
|
|
|
+ const args = rest.slice(0, adjustedLength - 1)
|
|
|
|
|
+ const callback = rest[adjustedLength - 1]
|
|
|
|
|
+ function fire() {
|
|
|
|
|
+ const methods = _hooks[name] || []
|
|
|
|
|
+ const callMethods = methods.map(method => cb => method(...args, cb))
|
|
|
|
|
+ async.series(callMethods, function (error, results) {
|
|
|
|
|
+ if (error) {
|
|
|
|
|
+ return callback(error)
|
|
|
|
|
+ }
|
|
|
|
|
+ callback(null, results)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// ensure that modules are loaded if we need to fire a hook
|
|
// ensure that modules are loaded if we need to fire a hook
|
|
|
// this can happen if a script calls a method that fires a hook
|
|
// this can happen if a script calls a method that fires a hook
|
|
|
if (!_modulesLoaded) {
|
|
if (!_modulesLoaded) {
|
|
|
loadModules()
|
|
loadModules()
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ fire()
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(err => callback(err))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fire()
|
|
|
}
|
|
}
|
|
|
- const adjustedLength = Math.max(rest.length, 1)
|
|
|
|
|
- const args = rest.slice(0, adjustedLength - 1)
|
|
|
|
|
- const callback = rest[adjustedLength - 1]
|
|
|
|
|
- const methods = _hooks[name] || []
|
|
|
|
|
- const callMethods = methods.map(method => cb => method(...args, cb))
|
|
|
|
|
- async.series(callMethods, function (error, results) {
|
|
|
|
|
- if (error) {
|
|
|
|
|
- return callback(error)
|
|
|
|
|
- }
|
|
|
|
|
- callback(null, results)
|
|
|
|
|
- })
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function getMiddleware(name) {
|
|
|
|
|
|
|
+async function getMiddleware(name) {
|
|
|
// ensure that modules are loaded if we need to call a middleware
|
|
// ensure that modules are loaded if we need to call a middleware
|
|
|
if (!_modulesLoaded) {
|
|
if (!_modulesLoaded) {
|
|
|
- loadModules()
|
|
|
|
|
|
|
+ await loadModules()
|
|
|
}
|
|
}
|
|
|
return _middleware[name] || []
|
|
return _middleware[name] || []
|
|
|
}
|
|
}
|