mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-23 18:17:14 +00:00
refactor(node/hooks): partial CJS->ESM (express, i18n, 7 of 14 express/* files)
Done: express.ts (incl. https/http hoisted to top-level imports, exports.server + exports.sessionMiddleware -> export let), i18n.ts, admin.ts, webaccess.ts (authnFailureDelayMs preserved as export let + setter), padurlsanitize.ts, pwa.ts, errorhandling.ts (exports.app preserved as export let), tokenTransfer.ts, adminplugins.ts. Still CJS in hooks/express/: adminsettings, apicalls, importexport, openapi, socketio, specialpages, static.
This commit is contained in:
parent
18a290a83f
commit
63de3c6e30
9 changed files with 77 additions and 72 deletions
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
import {Socket} from "node:net";
|
||||
import type {MapArrayType} from "../types/MapType";
|
||||
import type {MapArrayType} from "../types/MapType.js";
|
||||
|
||||
import _ from 'underscore';
|
||||
import cookieParser from 'cookie-parser';
|
||||
|
|
@ -9,15 +9,17 @@ import events from 'events';
|
|||
import express from 'express';
|
||||
import expressSession, {Store} from 'express-session';
|
||||
import fs from 'fs';
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
import hooks from '../../static/js/pluginfw/hooks.js';
|
||||
import log4js from 'log4js';
|
||||
const SessionStore = require('../db/SessionStore');
|
||||
import settings, {getEpVersion, getGitCommit} from '../utils/Settings';
|
||||
const stats = require('../stats')
|
||||
import SessionStore from '../db/SessionStore.js';
|
||||
import settings, {getEpVersion, getGitCommit} from '../utils/Settings.js';
|
||||
import stats from '../stats.js';
|
||||
import util from 'util';
|
||||
const webaccess = require('./express/webaccess');
|
||||
import * as webaccess from './express/webaccess.js';
|
||||
import https from 'https';
|
||||
import http from 'http';
|
||||
|
||||
import SecretRotator from '../security/SecretRotator';
|
||||
import SecretRotator from '../security/SecretRotator.js';
|
||||
|
||||
let secretRotator: SecretRotator|null = null;
|
||||
const logger = log4js.getLogger('http');
|
||||
|
|
@ -27,14 +29,15 @@ const sockets:Set<Socket> = new Set();
|
|||
const socketsEvents = new events.EventEmitter();
|
||||
const startTime = stats.settableGauge('httpStartTime');
|
||||
|
||||
exports.server = null;
|
||||
export let server: any = null;
|
||||
export let sessionMiddleware: any = null;
|
||||
|
||||
const closeServer = async () => {
|
||||
if (exports.server != null) {
|
||||
if (server != null) {
|
||||
logger.info('Closing HTTP server...');
|
||||
// Call exports.server.close() to reject new connections but don't await just yet because the
|
||||
// Call server.close() to reject new connections but don't await just yet because the
|
||||
// Promise won't resolve until all preexisting connections are closed.
|
||||
const p = util.promisify(exports.server.close.bind(exports.server))();
|
||||
const p = util.promisify(server.close.bind(server))();
|
||||
await hooks.aCallAll('expressCloseServer');
|
||||
// Give existing connections some time to close on their own before forcibly terminating. The
|
||||
// time should be long enough to avoid interrupting most preexisting transmissions but short
|
||||
|
|
@ -53,7 +56,7 @@ const closeServer = async () => {
|
|||
}
|
||||
await p;
|
||||
clearTimeout(timeout);
|
||||
exports.server = null;
|
||||
server = null;
|
||||
startTime.setValue(0);
|
||||
logger.info('HTTP server closed');
|
||||
}
|
||||
|
|
@ -64,14 +67,14 @@ const closeServer = async () => {
|
|||
secretRotator = null;
|
||||
};
|
||||
|
||||
exports.createServer = async () => {
|
||||
export const createServer = async () => {
|
||||
console.log('Report bugs at https://github.com/ether/etherpad/issues');
|
||||
|
||||
serverName = `Etherpad ${getGitCommit()} (https://etherpad.org)`;
|
||||
|
||||
console.log(`Your Etherpad version is ${getEpVersion()} (${getGitCommit()})`);
|
||||
|
||||
await exports.restartServer();
|
||||
await restartServer();
|
||||
|
||||
if (settings.ip === '') {
|
||||
// using Unix socket for connectivity
|
||||
|
|
@ -96,7 +99,7 @@ exports.createServer = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
exports.restartServer = async () => {
|
||||
export const restartServer = async () => {
|
||||
await closeServer();
|
||||
|
||||
const app = express(); // New syntax for express v3
|
||||
|
|
@ -119,11 +122,9 @@ exports.restartServer = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const https = require('https');
|
||||
exports.server = https.createServer(options, app);
|
||||
server = https.createServer(options, app);
|
||||
} else {
|
||||
const http = require('http');
|
||||
exports.server = http.createServer(app);
|
||||
server = http.createServer(app);
|
||||
}
|
||||
|
||||
app.use((req, res, next) => {
|
||||
|
|
@ -205,7 +206,7 @@ exports.restartServer = async () => {
|
|||
store.startCleanup();
|
||||
}
|
||||
sessionStore = store;
|
||||
exports.sessionMiddleware = expressSession({
|
||||
sessionMiddleware = expressSession({
|
||||
rolling: true,
|
||||
secret,
|
||||
store: sessionStore ?? undefined,
|
||||
|
|
@ -242,15 +243,15 @@ exports.restartServer = async () => {
|
|||
// middleware. This allows plugins to avoid creating an express-session record in the database
|
||||
// when it is not needed (e.g., public static content).
|
||||
await hooks.aCallAll('expressPreSession', {app, settings});
|
||||
app.use(exports.sessionMiddleware);
|
||||
app.use(sessionMiddleware);
|
||||
|
||||
app.use(webaccess.checkAccess);
|
||||
|
||||
await Promise.all([
|
||||
hooks.aCallAll('expressConfigure', {app}),
|
||||
hooks.aCallAll('expressCreateServer', {app, server: exports.server}),
|
||||
hooks.aCallAll('expressCreateServer', {app, server: server}),
|
||||
]);
|
||||
exports.server.on('connection', (socket:Socket) => {
|
||||
server.on('connection', (socket:Socket) => {
|
||||
sockets.add(socket);
|
||||
socketsEvents.emit('updated');
|
||||
socket.on('close', () => {
|
||||
|
|
@ -258,11 +259,11 @@ exports.restartServer = async () => {
|
|||
socketsEvents.emit('updated');
|
||||
});
|
||||
});
|
||||
await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip);
|
||||
await util.promisify(server.listen).bind(server)(settings.port, settings.ip);
|
||||
startTime.setValue(Date.now());
|
||||
logger.info('HTTP server listening for connections');
|
||||
};
|
||||
|
||||
exports.shutdown = async (hookName:string, context: any) => {
|
||||
export const shutdown = async (hookName:string, context: any) => {
|
||||
await closeServer();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
'use strict';
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType.js";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {MapArrayType} from "../../types/MapType";
|
||||
import {MapArrayType} from "../../types/MapType.js";
|
||||
|
||||
import settings from 'ep_etherpad-lite/node/utils/Settings';
|
||||
import settings from '../../utils/Settings.js';
|
||||
|
||||
const ADMIN_PATH = path.join(settings.root, 'src', 'templates');
|
||||
const PROXY_HEADER = "x-proxy-path"
|
||||
|
|
@ -15,7 +15,7 @@ const PROXY_HEADER = "x-proxy-path"
|
|||
* @param {Function} cb the callback function
|
||||
* @return {*}
|
||||
*/
|
||||
exports.expressCreateServer = (hookName: string, args: ArgsExpressType, cb: Function): any => {
|
||||
export const expressCreateServer = (hookName: string, args: ArgsExpressType, cb: Function): any => {
|
||||
|
||||
if (!fs.existsSync(ADMIN_PATH)) {
|
||||
console.error('admin template not found, skipping admin interface. You need to rebuild it in /admin with pnpm run build-copy')
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
import {ErrorCaused} from "../../types/ErrorCaused";
|
||||
import {QueryType} from "../../types/QueryType";
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType.js";
|
||||
import {ErrorCaused} from "../../types/ErrorCaused.js";
|
||||
import {QueryType} from "../../types/QueryType.js";
|
||||
|
||||
import {getAvailablePlugins, install, search, uninstall} from "../../../static/js/pluginfw/installer";
|
||||
import {PackageData, PackageInfo} from "../../types/PackageInfo";
|
||||
import {getAvailablePlugins, install, search, uninstall} from "../../../static/js/pluginfw/installer.js";
|
||||
import {PackageData, PackageInfo} from "../../types/PackageInfo.js";
|
||||
import semver from 'semver';
|
||||
import log4js from 'log4js';
|
||||
import {MapArrayType} from "../../types/MapType";
|
||||
import {MapArrayType} from "../../types/MapType.js";
|
||||
|
||||
const pluginDefs = require('../../../static/js/pluginfw/plugin_defs');
|
||||
import pluginDefs from '../../../static/js/pluginfw/plugin_defs.js';
|
||||
import stats from '../../stats.js';
|
||||
const logger = log4js.getLogger('adminPlugins');
|
||||
|
||||
|
||||
exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
export const socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
const io = args.io.of('/pluginfw/installer');
|
||||
io.on('connection', (socket:any) => {
|
||||
// @ts-ignore
|
||||
|
|
@ -41,7 +42,7 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
|||
|
||||
socket.on('getStats', ()=>{
|
||||
console.log("Getting stats for admin plugins");
|
||||
socket.emit('results:stats', require('../../stats').toJSON());
|
||||
socket.emit('results:stats', stats.toJSON());
|
||||
})
|
||||
|
||||
socket.on('getInstalled', async (query: string) => {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
import {ErrorCaused} from "../../types/ErrorCaused";
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType.js";
|
||||
import {ErrorCaused} from "../../types/ErrorCaused.js";
|
||||
|
||||
const stats = require('../../stats')
|
||||
import stats from '../../stats.js';
|
||||
|
||||
exports.expressCreateServer = (hook_name:string, args: ArgsExpressType, cb:Function) => {
|
||||
exports.app = args.app;
|
||||
export let app: any = null;
|
||||
export const expressCreateServer = (hook_name:string, args: ArgsExpressType, cb:Function) => {
|
||||
app = args.app;
|
||||
|
||||
// Handle errors
|
||||
args.app.use((err:ErrorCaused, req:any, res:any, next:Function) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType.js";
|
||||
|
||||
const padManager = require('../../db/PadManager');
|
||||
import * as padManager from '../../db/PadManager.js';
|
||||
|
||||
exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
export const expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
// redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
||||
args.app.param('pad', (req:any, res:any, next:Function, padId:string) => {
|
||||
(async () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
import settings from '../../utils/Settings';
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType.js";
|
||||
import settings from '../../utils/Settings.js';
|
||||
|
||||
const pwa = {
|
||||
name: settings.title || "Etherpad",
|
||||
|
|
@ -23,7 +23,7 @@ const pwa = {
|
|||
background_color: "#0f775b"
|
||||
}
|
||||
|
||||
exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
export const expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
args.app.get('/manifest.json', (req:any, res:any) => {
|
||||
res.json(pwa);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
||||
const db = require('../../db/DB');
|
||||
import {ArgsExpressType} from "../../types/ArgsExpressType.js";
|
||||
import db from '../../db/DB.js';
|
||||
import crypto from 'crypto'
|
||||
import settings from '../../utils/Settings';
|
||||
import settings from '../../utils/Settings.js';
|
||||
|
||||
|
||||
type TokenTransferRequest = {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
import {strict as assert} from "assert";
|
||||
import log4js from 'log4js';
|
||||
import {SocketClientRequest} from "../../types/SocketClientRequest";
|
||||
import {WebAccessTypes} from "../../types/WebAccessTypes";
|
||||
import {SettingsUser} from "../../types/SettingsUser";
|
||||
import {SocketClientRequest} from "../../types/SocketClientRequest.js";
|
||||
import {WebAccessTypes} from "../../types/WebAccessTypes.js";
|
||||
import {SettingsUser} from "../../types/SettingsUser.js";
|
||||
const httpLogger = log4js.getLogger('http');
|
||||
import settings from '../../utils/Settings';
|
||||
const hooks = require('../../../static/js/pluginfw/hooks');
|
||||
import readOnlyManager from '../../db/ReadOnlyManager';
|
||||
import settings from '../../utils/Settings.js';
|
||||
import hooks from '../../../static/js/pluginfw/hooks.js';
|
||||
import readOnlyManager from '../../db/ReadOnlyManager.js';
|
||||
|
||||
hooks.deprecationNotices.authFailure = 'use the authnFailure and authzFailure hooks instead';
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ const aCallFirst0 =
|
|||
// @ts-ignore
|
||||
async (hookName: string, context:any, pred = null) => (await aCallFirst(hookName, context, pred))[0];
|
||||
|
||||
exports.normalizeAuthzLevel = (level: string|boolean) => {
|
||||
export const normalizeAuthzLevel = (level: string|boolean) => {
|
||||
if (!level) return false;
|
||||
switch (level) {
|
||||
case true:
|
||||
|
|
@ -36,18 +36,19 @@ exports.normalizeAuthzLevel = (level: string|boolean) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
exports.userCanModify = (padId: string, req: SocketClientRequest) => {
|
||||
export const userCanModify = (padId: string, req: SocketClientRequest) => {
|
||||
if (readOnlyManager.isReadOnlyId(padId)) return false;
|
||||
if (!settings.requireAuthentication) return true;
|
||||
const {session: {user} = {}} = req;
|
||||
if (!user || user.readOnly) return false;
|
||||
assert(user.padAuthorizations); // This is populated even if !settings.requireAuthorization.
|
||||
const level = exports.normalizeAuthzLevel(user.padAuthorizations[padId]);
|
||||
const level = normalizeAuthzLevel(user.padAuthorizations[padId]);
|
||||
return level && level !== 'readOnly';
|
||||
};
|
||||
|
||||
// Exported so that tests can set this to 0 to avoid unnecessary test slowness.
|
||||
exports.authnFailureDelayMs = 1000;
|
||||
export let authnFailureDelayMs = 1000;
|
||||
export const setAuthnFailureDelayMs = (v: number) => { authnFailureDelayMs = v; };
|
||||
|
||||
const staticResources = [
|
||||
/^\/padbootstrap-[a-zA-Z0-9]+\.min\.js$/,
|
||||
|
|
@ -106,7 +107,7 @@ const checkAccess = async (req:any, res:any, next: Function) => {
|
|||
// authentication is checked and once after (if settings.requireAuthorization is true).
|
||||
const authorize = async () => {
|
||||
const grant = async (level: string|false) => {
|
||||
level = exports.normalizeAuthzLevel(level);
|
||||
level = normalizeAuthzLevel(level);
|
||||
if (!level) return false;
|
||||
const user = req.session.user;
|
||||
if (user == null) return true; // This will happen if authentication is not required.
|
||||
|
|
@ -186,7 +187,7 @@ const checkAccess = async (req:any, res:any, next: Function) => {
|
|||
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
||||
}
|
||||
// Delay the error response for 1s to slow down brute force attacks.
|
||||
await new Promise((resolve) => setTimeout(resolve, exports.authnFailureDelayMs));
|
||||
await new Promise((resolve) => setTimeout(resolve, authnFailureDelayMs));
|
||||
res.status(401).send('Authentication Required');
|
||||
return;
|
||||
}
|
||||
|
|
@ -230,6 +231,7 @@ const checkAccess = async (req:any, res:any, next: Function) => {
|
|||
* Express middleware to authenticate the user and check authorization. Must be installed after the
|
||||
* express-session middleware.
|
||||
*/
|
||||
exports.checkAccess = (req:any, res:any, next:Function) => {
|
||||
export const checkAccessMiddleware = (req:any, res:any, next:Function) => {
|
||||
checkAccess(req, res, next).catch((err) => next(err || new Error(err)));
|
||||
};
|
||||
export { checkAccessMiddleware as checkAccess };
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
import type {MapArrayType} from "../types/MapType";
|
||||
import {I18nPluginDefs} from "../types/I18nPluginDefs";
|
||||
import type {MapArrayType} from "../types/MapType.js";
|
||||
import {I18nPluginDefs} from "../types/I18nPluginDefs.js";
|
||||
|
||||
const languages = require('languages4translatewiki');
|
||||
import languages from 'languages4translatewiki';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import _ from 'underscore';
|
||||
const pluginDefs = require('../../static/js/pluginfw/plugin_defs');
|
||||
import existsSync from '../utils/path_exists';
|
||||
import settings from '../utils/Settings';
|
||||
import pluginDefs from '../../static/js/pluginfw/plugin_defs.js';
|
||||
import existsSync from '../utils/path_exists.js';
|
||||
import settings from '../utils/Settings.js';
|
||||
|
||||
// returns all existing messages merged together and grouped by langcode
|
||||
// {es: {"foo": "string"}, en:...}
|
||||
|
|
@ -131,7 +131,7 @@ const generateLocaleIndex = (locales:MapArrayType<string>) => {
|
|||
};
|
||||
|
||||
|
||||
exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||
export const expressPreSession = async (hookName:string, {app}:any) => {
|
||||
// regenerate locales on server restart
|
||||
const locales = getAllLocales();
|
||||
const localeIndex = generateLocaleIndex(locales);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue