refactor(node): convert root files (server, metrics, prometheus, stats, padaccess) to ESM

This commit is contained in:
SamTV12345 2026-04-26 13:48:17 +02:00
parent 848ccdef69
commit 897ac4581a
4 changed files with 61 additions and 42 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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<unknown>
exports.start = async () => {
export const start = async (): Promise<any> => {
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<any> => {
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<any> => {
/* 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();

View file

@ -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();
};
stats.shutdown = async (hookName: string, context: any) => {
stats.end();
};
export default stats;