import * as plugins from '../plugins.js'; import { Handler } from './classes.handler.js'; import * as interfaces from '../../dist_ts_interfaces/index.js'; export class HandlerProxy extends Handler { /** * The constuctor of HandlerProxy * @param remoteMountPointArg */ constructor( remoteMountPointArg: string, optionsArg?: { responseModifier?: interfaces.TResponseModifier; headers?: { [key: string]: string }; } ) { super('ALL', async (req, res) => { // Extract the path using Express 5's params or fallback methods let relativeRequestPath: string; if (req.params && req.params.splat !== undefined) { // Express 5 wildcard route (/*splat or /{*splat}) // Handle array values - join them if array, otherwise use as-is relativeRequestPath = Array.isArray(req.params.splat) ? req.params.splat.join('/') : String(req.params.splat || ''); } else if (req.params && req.params[0] !== undefined) { // Numbered parameter fallback relativeRequestPath = Array.isArray(req.params[0]) ? req.params[0].join('/') : String(req.params[0] || ''); } else if (req.baseUrl) { // If there's a baseUrl, remove it from the path relativeRequestPath = req.path.slice(req.baseUrl.length); } else if (req.route && req.route.path === '/') { // Root route - use full path minus leading slash relativeRequestPath = req.path.slice(1); } else { // Fallback to the original slicing logic for compatibility relativeRequestPath = req.path.slice(req.route.path.length - 1); } // Ensure relativeRequestPath is a string and has no leading slash relativeRequestPath = String(relativeRequestPath || ''); if (relativeRequestPath.startsWith('/')) { relativeRequestPath = relativeRequestPath.slice(1); } const proxyRequestUrl = remoteMountPointArg + relativeRequestPath; console.log(`proxy ${req.path} to ${proxyRequestUrl}`); let proxiedResponse: plugins.smartrequest.ICoreResponse; try { const smartRequest = plugins.smartrequest.SmartRequest.create() .url(proxyRequestUrl); // Execute request based on method switch (req.method.toUpperCase()) { case 'GET': proxiedResponse = await smartRequest.get(); break; case 'POST': proxiedResponse = await smartRequest.post(); break; case 'PUT': proxiedResponse = await smartRequest.put(); break; case 'DELETE': proxiedResponse = await smartRequest.delete(); break; case 'PATCH': proxiedResponse = await smartRequest.patch(); break; default: // For other methods, default to GET proxiedResponse = await smartRequest.get(); break; } } catch { res.end('failed to fullfill request'); return; } const headers = proxiedResponse.headers; for (const header of Object.keys(headers)) { res.set(header, headers[header] as string); } // set additional headers if (optionsArg && optionsArg.headers) { for (const key of Object.keys(optionsArg.headers)) { res.set(key, optionsArg.headers[key]); } } // Get response body as buffer let responseToSend: Buffer; try { const arrayBuffer = await proxiedResponse.arrayBuffer(); responseToSend = Buffer.from(arrayBuffer); } catch { // If we can't get arrayBuffer, try text try { const text = await proxiedResponse.text(); responseToSend = Buffer.from(text); } catch { // Provide a default empty buffer if body cannot be read responseToSend = Buffer.from(''); } } if (optionsArg && optionsArg.responseModifier) { const modifiedResponse = await optionsArg.responseModifier({ headers: res.getHeaders(), path: req.path, responseContent: responseToSend, }); // headers for (const key of Object.keys(res.getHeaders())) { if (!modifiedResponse.headers[key]) { res.removeHeader(key); } } for (const key of Object.keys(modifiedResponse.headers)) { res.setHeader(key, modifiedResponse.headers[key]); } // responseContent responseToSend = modifiedResponse.responseContent; } res.status(200); res.write(responseToSend); res.end(); }); } }