| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- const fs = require('fs')
- const Path = require('path')
- const async = require('async')
- const { promisify } = require('util')
- const Settings = require('@overleaf/settings')
- const Views = require('./Views')
- const MODULE_BASE_PATH = Path.join(__dirname, '/../../../modules')
- const _modules = []
- let _modulesLoaded = false
- const _hooks = {}
- const _middleware = {}
- let _viewIncludes = {}
- function modules() {
- if (!_modulesLoaded) {
- loadModules()
- }
- return _modules
- }
- function loadModules() {
- const settingsCheckModule = Path.join(
- MODULE_BASE_PATH,
- 'settings-check',
- 'index.js'
- )
- if (fs.existsSync(settingsCheckModule)) {
- require(settingsCheckModule)
- }
- for (const moduleName of Settings.moduleImportSequence || []) {
- const loadedModule = require(
- Path.join(MODULE_BASE_PATH, moduleName, 'index.js')
- )
- loadedModule.name = moduleName
- _modules.push(loadedModule)
- if (loadedModule.viewIncludes) {
- throw new Error(
- `${moduleName}: module.viewIncludes moved into Settings.viewIncludes`
- )
- }
- if (loadedModule.dependencies) {
- for (const dependency of loadedModule.dependencies) {
- if (!Settings.moduleImportSequence.includes(dependency)) {
- throw new Error(
- `Module '${dependency}' listed as a dependency of '${moduleName}' is missing in the moduleImportSequence. Please also verify that it is available in the current environment.`
- )
- }
- }
- }
- }
- _modulesLoaded = true
- attachHooks()
- attachMiddleware()
- }
- function applyRouter(webRouter, privateApiRouter, publicApiRouter) {
- for (const module of modules()) {
- if (module.router && module.router.apply) {
- module.router.apply(webRouter, privateApiRouter, publicApiRouter)
- }
- }
- }
- function applyNonCsrfRouter(webRouter, privateApiRouter, publicApiRouter) {
- for (const module of modules()) {
- if (module.nonCsrfRouter != null) {
- module.nonCsrfRouter.apply(webRouter, privateApiRouter, publicApiRouter)
- }
- if (module.router && module.router.applyNonCsrfRouter) {
- module.router.applyNonCsrfRouter(
- webRouter,
- privateApiRouter,
- publicApiRouter
- )
- }
- }
- }
- function loadViewIncludes(app) {
- _viewIncludes = Views.compileViewIncludes(app)
- }
- function applyMiddleware(appOrRouter, middlewareName, options) {
- if (!middlewareName) {
- throw new Error(
- 'middleware name must be provided to register module middleware'
- )
- }
- for (const module of modules()) {
- if (module[middlewareName]) {
- module[middlewareName](appOrRouter, options)
- }
- }
- }
- function moduleIncludes(view, locals) {
- const compiledPartials = _viewIncludes[view] || []
- let html = ''
- for (const compiledPartial of compiledPartials) {
- html += compiledPartial(locals)
- }
- return html
- }
- function moduleIncludesAvailable(view) {
- return (_viewIncludes[view] || []).length > 0
- }
- function linkedFileAgentsIncludes() {
- const agents = {}
- for (const module of modules()) {
- for (const name in module.linkedFileAgents) {
- const agentFunction = module.linkedFileAgents[name]
- agents[name] = agentFunction()
- }
- }
- return agents
- }
- function attachHooks() {
- for (const module of modules()) {
- for (const hook in module.hooks || {}) {
- const method = module.hooks[hook]
- attachHook(hook, method)
- }
- }
- }
- function attachHook(name, method) {
- if (_hooks[name] == null) {
- _hooks[name] = []
- }
- _hooks[name].push(method)
- }
- function attachMiddleware() {
- for (const module of modules()) {
- for (const middleware in module.middleware || {}) {
- const method = module.middleware[middleware]
- if (_middleware[middleware] == null) {
- _middleware[middleware] = []
- }
- _middleware[middleware].push(method)
- }
- }
- }
- function fireHook(name, ...rest) {
- // 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
- if (!_modulesLoaded) {
- loadModules()
- }
- 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) {
- // ensure that modules are loaded if we need to call a middleware
- if (!_modulesLoaded) {
- loadModules()
- }
- return _middleware[name] || []
- }
- module.exports = {
- applyNonCsrfRouter,
- applyRouter,
- linkedFileAgentsIncludes,
- loadViewIncludes,
- moduleIncludes,
- moduleIncludesAvailable,
- applyMiddleware,
- hooks: {
- attach: attachHook,
- fire: fireHook,
- },
- middleware: getMiddleware,
- promises: {
- hooks: {
- fire: promisify(fireHook),
- },
- },
- }
|