diff --git a/src/node/padaccess.ts b/src/node/padaccess.ts index 6db856fb6..44db18795 100644 --- a/src/node/padaccess.ts +++ b/src/node/padaccess.ts @@ -1,9 +1,12 @@ 'use strict'; -const securityManager = require('./db/SecurityManager'); -import settings from './utils/Settings'; +import * as securityManager from './db/SecurityManager.js'; +import settings from './utils/Settings.js'; // checks for padAccess -module.exports = async (req: { params?: any; cookies?: any; session?: any; }, res: { status: (arg0: number) => { (): any; new(): any; send: { (arg0: string): void; new(): any; }; }; }) => { +const hasPadAccess = async ( + req: { params?: any; cookies?: any; session?: any; }, + res: { status: (arg0: number) => { (): any; new(): any; send: { (arg0: string): void; new(): any; }; }; }, +) => { const {session: {user} = {}} = req; const p = settings.cookie.prefix; const accessObj = await securityManager.checkAccess( @@ -21,3 +24,5 @@ module.exports = async (req: { params?: any; cookies?: any; session?: any; }, re return false; } }; + +export default hasPadAccess; diff --git a/src/node/prometheus.ts b/src/node/prometheus.ts index 8b0ac7598..3f1aa27cd 100644 --- a/src/node/prometheus.ts +++ b/src/node/prometheus.ts @@ -1,7 +1,6 @@ import client from 'prom-client'; - -const db = require('./db/DB').db; -const PadMessageHandler = require('./handler/PadMessageHandler'); +import dbModule from './db/DB.js'; +import * as PadMessageHandler from './handler/PadMessageHandler.js'; const register = new client.Registry(); const gaugeDB = new client.Gauge({ @@ -26,6 +25,7 @@ register.registerMetric(activePadsGauge); client.collectDefaultMetrics({register}); const monitor = async function () { + const db = dbModule.db; for (const [metric, value] of Object.entries(db.metrics)) { if (typeof value !== 'number') continue; gaugeDB.set({type: metric}, value); diff --git a/src/node/server.ts b/src/node/server.ts index 331136746..49227e567 100755 --- a/src/node/server.ts +++ b/src/node/server.ts @@ -22,20 +22,21 @@ * limitations under the License. */ -import {PluginType} from "./types/Plugin"; -import {ErrorCaused} from "./types/ErrorCaused"; +import {fileURLToPath} from 'node:url'; +import {PluginType} from "./types/Plugin.js"; +import {ErrorCaused} from "./types/ErrorCaused.js"; import log4js from 'log4js'; -import pkg from '../package.json'; -import {checkForMigration} from "../static/js/pluginfw/installer"; +import pkg from '../package.json' with { type: 'json' }; +import {checkForMigration} from "../static/js/pluginfw/installer.js"; import axios from "axios"; -import settings from './utils/Settings'; +import settings from './utils/Settings.js'; let wtfnode: any; if (settings.dumpOnUncleanExit) { // wtfnode should be loaded after log4js.replaceConsole() so that it uses log4js for logging, and // it should be above everything else so that it can hook in before resources are used. - wtfnode = require('wtfnode'); + wtfnode = (await import('wtfnode')).default; } @@ -68,18 +69,18 @@ if (process.env['https_proxy']) { * early check for version compatibility before calling * any modules that require newer versions of NodeJS */ -import {enforceMinNodeVersion, checkDeprecationStatus} from './utils/NodeVersion'; +import {enforceMinNodeVersion, checkDeprecationStatus} from './utils/NodeVersion.js'; enforceMinNodeVersion(pkg.engines.node.replace(">=", "")); checkDeprecationStatus(pkg.engines.node.replace(">=", ""), '2.1.0'); -import {check} from './utils/UpdateCheck'; -const db = require('./db/DB'); -const express = require('./hooks/express'); -const hooks = require('../static/js/pluginfw/hooks'); -const pluginDefs = require('../static/js/pluginfw/plugin_defs'); -const plugins = require('../static/js/pluginfw/plugins'); -import {Gate} from './utils/promises'; -const stats = require('./stats') +import {check} from './utils/UpdateCheck.js'; +import db from './db/DB.js'; +import * as express from './hooks/express.js'; +import hooks from '../static/js/pluginfw/hooks.js'; +import pluginDefs from '../static/js/pluginfw/plugin_defs.js'; +import plugins from '../static/js/pluginfw/plugins.js'; +import {Gate} from './utils/promises.js'; +import stats from './stats.js'; const logger = log4js.getLogger('server'); @@ -105,14 +106,14 @@ const removeSignalListener = (signal: NodeJS.Signals, listener: any) => { let startDoneGate: Gate -exports.start = async () => { +export const start = async (): Promise => { switch (state) { case State.INITIAL: break; case State.STARTING: await startDoneGate; // Retry. Don't fall through because it might have transitioned to STATE_TRANSITION_FAILED. - return await exports.start(); + return await start(); case State.RUNNING: return express.server; case State.STOPPING: @@ -140,7 +141,7 @@ exports.start = async () => { logger.debug(`uncaught exception: ${err.stack || err}`); // eslint-disable-next-line promise/no-promise-in-callback - exports.exit(err) + exit(err) .catch((err: ErrorCaused) => { logger.error('Error in process exit', err); // eslint-disable-next-line n/no-process-exit @@ -162,7 +163,7 @@ exports.start = async () => { for (const listener of process.listeners(signal)) { removeSignalListener(signal, listener); } - process.on(signal, exports.exit); + process.on(signal, exit); // Prevent signal listeners from being added in the future. process.on('newListener', (event, listener) => { if (event !== signal) return; @@ -187,7 +188,7 @@ exports.start = async () => { state = State.STATE_TRANSITION_FAILED; // @ts-ignore startDoneGate.resolve(); - return await exports.exit(err); + return await exit(err as ErrorCaused); } logger.info('Etherpad is running'); @@ -200,12 +201,12 @@ exports.start = async () => { }; const stopDoneGate = new Gate(); -exports.stop = async () => { +export const stop = async (): Promise => { switch (state) { case State.STARTING: - await exports.start(); + await start(); // Don't fall through to State.RUNNING in case another caller is also waiting for startup. - return await exports.stop(); + return await stop(); case State.RUNNING: break; case State.STOPPING: @@ -236,7 +237,7 @@ exports.stop = async () => { state = State.STATE_TRANSITION_FAILED; // @ts-ignore stopDoneGate.resolve(); - return await exports.exit(err); + return await exit(err as ErrorCaused); } logger.info('Etherpad stopped'); state = State.STOPPED; @@ -246,7 +247,7 @@ exports.stop = async () => { let exitGate: any; let exitCalled = false; -exports.exit = async (err: ErrorCaused|string|null = null) => { +export const exit = async (err: ErrorCaused|string|null = null): Promise => { /* eslint-disable no-process-exit */ if (err === 'SIGTERM') { // Termination from SIGTERM is not treated as an abnormal termination. @@ -267,11 +268,11 @@ exports.exit = async (err: ErrorCaused|string|null = null) => { case State.STARTING: case State.RUNNING: case State.STOPPING: - await exports.stop(); + await stop(); // Don't fall through to State.STOPPED in case another caller is also waiting for stop(). - // Don't pass err to exports.exit() because this err has already been processed. (If err is + // Don't pass err to exit() because this err has already been processed. (If err is // passed again to exit() then exit() will think that a second error occurred while exiting.) - return await exports.exit(); + return await exit(); case State.INITIAL: case State.STOPPED: case State.STATE_TRANSITION_FAILED: @@ -311,7 +312,19 @@ exports.exit = async (err: ErrorCaused|string|null = null) => { /* eslint-enable no-process-exit */ }; -if (require.main === module) exports.start(); +// ESM equivalent of `require.main === module`: check whether this file was the +// process entry point. +const isEntryPoint = (() => { + try { + const entry = process.argv[1]; + if (!entry) return false; + return fileURLToPath(import.meta.url) === entry; + } catch { + return false; + } +})(); + +if (isEntryPoint) start(); // @ts-ignore -if (typeof(PhusionPassenger) !== 'undefined') exports.start(); +if (typeof(PhusionPassenger) !== 'undefined') start(); diff --git a/src/node/stats.ts b/src/node/stats.ts index f1fc0cccf..2df279b77 100644 --- a/src/node/stats.ts +++ b/src/node/stats.ts @@ -1,10 +1,11 @@ 'use strict'; -const measured = require('measured-core'); +import measured from 'measured-core'; -module.exports = measured.createCollection(); +const stats: any = measured.createCollection(); -// @ts-ignore -module.exports.shutdown = async (hookName, context) => { - module.exports.end(); -}; \ No newline at end of file +stats.shutdown = async (hookName: string, context: any) => { + stats.end(); +}; + +export default stats;