diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e22bf5c6..1f1b93941 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,6 +203,9 @@ importers: languages4translatewiki: specifier: 0.1.3 version: 0.1.3 + live-directory: + specifier: ^3.0.3 + version: 3.0.3 live-plugin-manager: specifier: ^1.0.0 version: 1.0.0 @@ -3297,6 +3300,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + live-directory@3.0.3: + resolution: {integrity: sha512-d5jchsscPvkDqwv8lypjpxIIUz4w8fu+czfEkNEMGub4+EZ0SBj5Nclb4E2QmJNC5HJ4BwEdc5DHvoHZfIAK+w==} + live-plugin-manager@1.0.0: resolution: {integrity: sha512-ZzSagtubz5lrlyRZzpJ4L3KCZwdPeYKQJqz3py7hO3R4BDbt6NH2Dn70rWoQTF6ichM8P91xji1cN6ytmZ2VjQ==} @@ -7916,6 +7922,10 @@ snapshots: lines-and-columns@1.2.4: {} + live-directory@3.0.3: + dependencies: + chokidar: 3.6.0 + live-plugin-manager@1.0.0: dependencies: '@types/debug': 4.1.12 diff --git a/src/node/hooks/express.ts b/src/node/hooks/express.ts index 8715fc151..950de28de 100644 --- a/src/node/hooks/express.ts +++ b/src/node/hooks/express.ts @@ -18,9 +18,10 @@ const settings = require('../utils/Settings'); const stats = require('../stats') import util from 'util'; const webaccess = require('./express/webaccess'); -import HyperExpress from 'hyper-express'; +import HyperExpress, {ServerConstructorOptions} from 'hyper-express'; import SecretRotator from '../security/SecretRotator'; +import {Server} from "hyper-express/types/components/Server"; let secretRotator: SecretRotator|null = null; const logger = log4js.getLogger('http'); @@ -31,6 +32,7 @@ const socketsEvents = new events.EventEmitter(); const startTime = stats.settableGauge('httpStartTime'); exports.server = null; +let appInstance: Server|null = null; const closeServer = async () => { if (exports.server != null) { @@ -55,8 +57,10 @@ const closeServer = async () => { await events.once(socketsEvents, 'updated'); } await p; + appInstance!.close() clearTimeout(timeout); exports.server = null; + appInstance = null startTime.setValue(0); logger.info('HTTP server closed'); } @@ -100,14 +104,21 @@ exports.createServer = async () => { exports.restartServer = async () => { await closeServer(); + const opts: ServerConstructorOptions = { + auto_close: true - const app = new HyperExpress.Server(); // New syntax for express v3 + } + let app: Server if (settings.ssl) { console.log('SSL -- enabled'); console.log(`SSL -- server key file: ${settings.ssl.key}`); console.log(`SSL -- Certificate Authority's certificate file: ${settings.ssl.cert}`); + + opts.cert_file_name = settings.ssl.cert + opts.key_file_name = settings.ssl.key + const options: MapArrayType = { key: fs.readFileSync(settings.ssl.key), cert: fs.readFileSync(settings.ssl.cert), @@ -120,13 +131,15 @@ exports.restartServer = async () => { options.ca.push(fs.readFileSync(caFileName)); } } - + app = new HyperExpress.Server(opts); // New syntax for express v3 const https = require('https'); exports.server = https.createServer(options, app); } else { + app = new HyperExpress.Server(opts); // New syntax for express v3 const http = require('http'); exports.server = http.createServer(app); } + exports.appInstance = app app.use((req, res, next) => { // res.header("X-Frame-Options", "deny"); // breaks embedded pads @@ -163,7 +176,7 @@ exports.restartServer = async () => { * * Source: https://expressjs.com/en/guide/behind-proxies.html */ - app.enable('trust proxy'); + opts.trust_proxy = true } // Measure response time diff --git a/src/node/hooks/express/errorhandling.ts b/src/node/hooks/express/errorhandling.ts index cb2059492..bd7addaef 100644 --- a/src/node/hooks/express/errorhandling.ts +++ b/src/node/hooks/express/errorhandling.ts @@ -6,17 +6,16 @@ import {ErrorCaused} from "../../types/ErrorCaused"; const stats = require('../../stats') exports.expressCreateServer = (hook_name:string, args: ArgsExpressType, cb:Function) => { - exports.app = args.app; - // Handle errors - /*args.app.use((req:any, res:any, next:Function) => { + args.app.set_error_handler((req, res, error)=>{ // if an error occurs Connect will pass it down // through these "error-handling" middleware // allowing you to respond however you like - res.status(500).send({error: 'Sorry, something bad happened!'}); - console.error(err.stack ? err.stack : err.toString()); + res.status(500).json({error: 'Sorry, something bad happened!'}); + console.error(error.stack ? error.stack : error.toString()); stats.meter('http500').mark(); - });*/ + }) + return cb(); }; diff --git a/src/node/hooks/express/socketio.ts b/src/node/hooks/express/socketio.ts index bbdec1c1c..cdef7dc63 100644 --- a/src/node/hooks/express/socketio.ts +++ b/src/node/hooks/express/socketio.ts @@ -76,6 +76,8 @@ export const expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Fu maxHttpBufferSize: settings.socketIo.maxHttpBufferSize, }) + io.attachApp(args.app) + const handleConnection = (socket:Socket) => { sockets.add(socket); diff --git a/src/node/security/OAuth2Provider.ts b/src/node/security/OAuth2Provider.ts index e21211350..f7daa8bb5 100644 --- a/src/node/security/OAuth2Provider.ts +++ b/src/node/security/OAuth2Provider.ts @@ -10,6 +10,7 @@ import {format} from 'url' import {ParsedUrlQuery} from "node:querystring"; import {Http2ServerRequest, Http2ServerResponse} from "node:http2"; import {MapArrayType} from "../types/MapType"; +import LiveDirectory from "live-directory"; const configuration: Configuration = { scopes: ['openid', 'profile', 'email'], @@ -153,12 +154,13 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp }); - args.app.post('/interaction/:uid', async (req: Http2ServerRequest, res: Http2ServerResponse, next:Function) => { + args.app.post('/interaction/:uid', async (req, res, next) => { const formid = new IncomingForm(); try { // @ts-ignore const {login, password} = (await formid.parse(req))[0] - const {prompt, jti, session,cid, params, grantId} = await oidc.interactionDetails(req, res); + // @ts-ignore + const {prompt, jti, session,cid, params, grantId} = await oidc.interactionDetails(req, res); const client = await oidc.Client.find(params.client_id as string); @@ -181,7 +183,8 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp } if (account) { - await oidc.interactionFinished(req, res, { + // @ts-ignore + await oidc.interactionFinished(req, res, { login: {accountId: account.username} }, {mergeWithLastSubmission: false}); } @@ -213,24 +216,26 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp } } const result = {consent: {grantId: await grant!.save()}}; - await oidc.interactionFinished(req, res, result, { + // @ts-ignore + await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: true, }); break; } } - await next(); + next(); } catch (err:any) { - return res.writeHead(500).end(err.message); + return res.status(500).end(err.message); } }) - args.app.get('/interaction/:uid', async (req: Request, res: Response, next: Function) => { + args.app.get('/interaction/:uid', async (req, res, next) => { try { - const { + const { uid, prompt, params, session, - } = await oidc.interactionDetails(req, res); + // @ts-ignore + } = await oidc.interactionDetails(req, res); params["state"] = uid @@ -253,12 +258,35 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp return res.sendFile(path.join(settings.root,'src','static', 'oidc','login.html')); } } catch (err) { - return next(err); + throw new Error("Invalid name"); } }); + const LiveAssets = new LiveDirectory(path.join(settings.root,'src','static', 'oidc'), { + }) - args.app.use('/views/', express.static(path.join(settings.root,'src','static', 'oidc'), {maxAge: 1000 * 60 * 60 * 24})); + args.app.get('/views/*', (request, response)=>{ + // Strip away '/assets' from the request path to get asset relative path + // Lookup LiveFile instance from our LiveDirectory instance. + const path = request.path.replace('/views', ''); + const file = LiveAssets.get(path); + + // Return a 404 if no asset/file exists on the derived path + if (file === undefined) return response.status(404).send(); + + const fileParts = file.path.split("."); + const extension = fileParts[fileParts.length - 1]; + + // Retrieve the file content and serve it depending on the type of content available for this file + const content = file.content; + if (content instanceof Buffer) { + // Set appropriate mime-type and serve file content Buffer as response body (This means that the file content was cached in memory) + return response.type(extension).send(content); + } else { + // Set the type and stream the content as the response body (This means that the file content was NOT cached in memory) + return response.type(extension).stream(content); + } + }); oidc.on('authorization.error', (ctx, error) => { @@ -277,6 +305,7 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp oidc.on('revocation.error', (ctx, error) => { console.log('revocation.error', error); }) - args.app.use("/oidc", oidc.callback()); + // @ts-ignore + args.app.use("/oidc", oidc.callback()); //cb(); } diff --git a/src/node/types/ArgsExpressType.ts b/src/node/types/ArgsExpressType.ts index 5c0675b97..403671758 100644 --- a/src/node/types/ArgsExpressType.ts +++ b/src/node/types/ArgsExpressType.ts @@ -1,5 +1,7 @@ +import {Server} from "hyper-express/types/components/Server"; + export type ArgsExpressType = { - app:any, + app: Server, io: any, server:any -} \ No newline at end of file +} diff --git a/src/package.json b/src/package.json index 726c17b09..434e10d1a 100644 --- a/src/package.json +++ b/src/package.json @@ -44,19 +44,20 @@ "find-root": "1.1.0", "formidable": "^3.5.1", "http-errors": "^2.0.0", + "hyper-express": "^6.16.4", "jose": "^5.6.3", "js-cookie": "^3.0.5", "jsdom": "^24.1.1", "jsonminify": "0.4.2", "jsonwebtoken": "^9.0.2", "languages4translatewiki": "0.1.3", + "live-directory": "^3.0.3", "live-plugin-manager": "^1.0.0", "lodash.clonedeep": "4.5.0", "log4js": "^6.9.1", "lru-cache": "^11.0.0", "measured-core": "^2.0.0", "mime-types": "^2.1.35", - "hyper-express": "^6.16.4", "oidc-provider": "^8.5.1", "openapi-backend": "^5.10.6", "proxy-addr": "^2.0.7",