Add support for extra proxies via external file

Allows defining custom reverse proxy routes via an `extra-proxies.js`
file at the project root. The Gateway loads these configurations on
startup.

Additionally, the Nginx gateway manager no longer applies default
prefix-stripping rewrites. Explicit `pathRewrite` rules are now
required if prefix stripping is needed for any proxied service,
including those defined externally.
This commit is contained in:
SrGooglo 2025-05-12 02:26:40 +00:00
parent 80d84b3e17
commit 1b6d1c74a1
3 changed files with 100 additions and 9 deletions

View File

@ -0,0 +1,7 @@
export default {
"/spectrum/*": {
target: process.env.SPECTRUM_API ?? "https://live.ragestudio.net",
pathRewrite: { "^/spectrum/(.*)": "/$1", "^/spectrum": "/" },
websocket: true,
},
}

View File

@ -165,6 +165,92 @@ export default class Gateway {
}
}
/**
* Loads and registers additional proxy routes from ../../extra-proxies.js
*/
async registerExtraProxies() {
try {
// Dynamic import is relative to the current file.
// extra-proxies.js can be CJS (module.exports = ...) or ESM (export default ...)
const extraProxiesModule = require(
path.resolve(process.cwd(), "extra-proxies.js"),
)
const extraProxies = extraProxiesModule.default // Node's CJS/ESM interop puts module.exports on .default
if (
!extraProxies ||
typeof extraProxies !== "object" ||
Object.keys(extraProxies).length === 0
) {
console.log(
"[Gateway] No extra proxies defined in `extra-proxies.js`, file is empty, or format is invalid. Skipping.",
)
return
}
console.log(
`[Gateway] Registering extra proxies from 'extra-proxies.js'...`,
)
for (const proxyPathKey in extraProxies) {
if (
Object.prototype.hasOwnProperty.call(
extraProxies,
proxyPathKey,
)
) {
const config = extraProxies[proxyPathKey]
if (!config || typeof config.target !== "string") {
console.warn(
`[Gateway] Skipping invalid extra proxy config for path: '${proxyPathKey}' in 'extra-proxies.js'. Target is missing or not a string.`,
)
continue
}
let registrationPath = proxyPathKey
// Normalize paths ending with /*
// e.g., "/spectrum/*" becomes "/spectrum"
// e.g., "/*" becomes "/"
if (registrationPath.endsWith("/*")) {
registrationPath = registrationPath.slice(0, -2)
if (registrationPath === "") {
registrationPath = "/"
}
}
console.log(
`[Gateway] Registering extra proxy: '${proxyPathKey}' (as '${registrationPath}') -> ${config.target}`,
)
await this.gateway.register({
serviceId: `extra-proxy:${registrationPath}`, // Unique ID for this proxy rule
path: registrationPath,
target: config.target,
pathRewrite: config.pathRewrite, // undefined if not present
websocket: !!config.websocket, // false if not present or falsy
})
}
}
} catch (error) {
// Handle cases where the extra-proxies.js file might not exist
if (
error.code === "ERR_MODULE_NOT_FOUND" ||
(error.message &&
error.message.toLowerCase().includes("cannot find module"))
) {
console.log(
"[Gateway] `extra-proxies.js` not found. Skipping extra proxy registration.",
)
} else {
console.error(
"[Gateway] Error loading or registering extra proxies from `extra-proxies.js`:",
error,
)
}
}
}
/**
* Handle both router and websocket registration requests from services
* @param {Service} service - Service registering a route or websocket
@ -304,6 +390,9 @@ export default class Gateway {
await this.gateway.initialize()
}
// Register any externally defined proxies before services start
await this.registerExtraProxies()
// Watch for service state changes
Observable.observe(this.serviceRegistry, (changes) => {
this.checkAllServicesReady()

View File

@ -371,19 +371,12 @@ http {
if (route.pathRewrite && Object.keys(route.pathRewrite).length > 0) {
rewriteConfig += "# Path rewrite rules\n"
for (const [pattern, replacement] of Object.entries(
route.pathRewrite,
)) {
// Improved rewrite pattern that preserves query parameters
rewriteConfig += `\trewrite ${pattern} ${replacement}$is_args$args break;`
}
} else {
// If no explicit rewrite is defined, but we need to strip the path prefix,
// Generate a default rewrite that preserves the URL structure
if (path !== "/") {
rewriteConfig += "# Default path rewrite to strip prefix\n"
rewriteConfig += `\trewrite ^${path}(/.*)$ $1$is_args$args break;\n`
rewriteConfig += `\trewrite ^${path}$ / break;`
rewriteConfig += `\nrewrite ${pattern} ${replacement} break;`
}
}
@ -423,6 +416,8 @@ ${locationDirective} {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
${rewriteConfig}
# Proxy pass to service
proxy_pass ${route.target};
}