diff --git a/docs/guide.md b/docs/guide.md index 6d164c694..0d0c13140 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -432,3 +432,12 @@ const config = { 3. Access app at `/absproxy/5173/` e.g. `http://localhost:8080/absproxy/5173/ For additional context, see [this Github Issue](https://github.com/sveltejs/kit/issues/2958) + +### Prefixing `/absproxy/` with a path + +This is a case where you need to serve an application via `absproxy` as explained above while serving `codeserver` itself from a path other than the root in your domain. + +For example: `http://my-code-server.com/user/123/workspace/my-app`. To achieve this result: + +1. start code server with the switch `--abs-proxy-base-path=/user/123/workspace` +2. Follow one of the instructions above for your framework. diff --git a/src/node/cli.ts b/src/node/cli.ts index 69890279e..36cb29e75 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -53,6 +53,7 @@ export interface UserProvidedCodeArgs { "disable-getting-started-override"?: boolean "disable-proxy"?: boolean "session-socket"?: string + "abs-proxy-base-path"?: string } /** @@ -117,18 +118,18 @@ interface Option { type OptionType = T extends boolean ? "boolean" : T extends OptionalString - ? typeof OptionalString - : T extends LogLevel - ? typeof LogLevel - : T extends AuthType - ? typeof AuthType - : T extends number - ? "number" - : T extends string - ? "string" - : T extends string[] - ? "string[]" - : "unknown" + ? typeof OptionalString + : T extends LogLevel + ? typeof LogLevel + : T extends AuthType + ? typeof AuthType + : T extends number + ? "number" + : T extends string + ? "string" + : T extends string[] + ? "string[]" + : "unknown" export type Options = { [P in keyof T]: Option> @@ -279,6 +280,10 @@ export const options: Options> = { short: "w", description: "Text to show on login page", }, + "abs-proxy-base-path": { + type: "string", + description: "The base path to prefix all absproxy requests", + }, } export const optionDescriptions = (opts: Partial>> = options): string[] => { diff --git a/src/node/routes/index.ts b/src/node/routes/index.ts index 1be3988ca..e61cbd657 100644 --- a/src/node/routes/index.ts +++ b/src/node/routes/index.ts @@ -121,11 +121,13 @@ export const register = async (app: App, args: DefaultedArgs): Promise { await pathProxy.proxy(req, res, { passthroughPath: true, + proxyBasePath: args["abs-proxy-base-path"], }) }) app.wsRouter.get("/absproxy/:port/:path(.*)?", async (req) => { await pathProxy.wsProxy(req as pluginapi.WebsocketRequest, { passthroughPath: true, + proxyBasePath: args["abs-proxy-base-path"], }) }) diff --git a/src/node/routes/pathProxy.ts b/src/node/routes/pathProxy.ts index 7f69bcf9e..ccfb0cc82 100644 --- a/src/node/routes/pathProxy.ts +++ b/src/node/routes/pathProxy.ts @@ -5,10 +5,15 @@ import { HttpCode, HttpError } from "../../common/http" import { ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http" import { proxy as _proxy } from "../proxy" -const getProxyTarget = (req: Request): string => { +const getProxyTarget = ( + req: Request, + opts?: { + proxyBasePath?: string + }, +): string => { // If there is a base path, strip it out. const base = (req as any).base || "" - return `http://0.0.0.0:${req.params.port}/${req.originalUrl.slice(base.length)}` + return `http://0.0.0.0:${req.params.port}${opts?.proxyBasePath || ""}/${req.originalUrl.slice(base.length)}` } export async function proxy( @@ -16,6 +21,7 @@ export async function proxy( res: Response, opts?: { passthroughPath?: boolean + proxyBasePath?: string }, ): Promise { ensureProxyEnabled(req) @@ -38,7 +44,7 @@ export async function proxy( _proxy.web(req, res, { ignorePath: true, - target: getProxyTarget(req), + target: getProxyTarget(req, opts), }) } @@ -46,6 +52,7 @@ export async function wsProxy( req: pluginapi.WebsocketRequest, opts?: { passthroughPath?: boolean + proxyBasePath?: string }, ): Promise { ensureProxyEnabled(req) @@ -59,6 +66,6 @@ export async function wsProxy( _proxy.ws(req, req.ws, req.head, { ignorePath: true, - target: getProxyTarget(req), + target: getProxyTarget(req, opts), }) } diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index 02b60b839..e596549da 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -106,6 +106,8 @@ describe("parser", () => { "--disable-proxy", + ["--abs-proxy-base-path", "/codeserver/app1"], + ["--session-socket", "/tmp/override-code-server-ipc-socket"], ["--host", "0.0.0.0"], @@ -143,6 +145,7 @@ describe("parser", () => { version: true, "bind-addr": "192.169.0.1:8080", "session-socket": "/tmp/override-code-server-ipc-socket", + "abs-proxy-base-path": "/codeserver/app1", }) }) diff --git a/test/unit/node/proxy.test.ts b/test/unit/node/proxy.test.ts index 9e8017e37..186cd475b 100644 --- a/test/unit/node/proxy.test.ts +++ b/test/unit/node/proxy.test.ts @@ -256,6 +256,18 @@ describe("proxy", () => { expect(spy).toHaveBeenCalledWith([test.expected, test.query]) } }) + + it("should allow specifying an absproxy path", async () => { + const prefixedPath = `/codeserver/app1${absProxyPath}` + e.get(prefixedPath, (req, res) => { + res.send("app being served behind a prefixed path") + }) + codeServer = await integration.setup(["--auth=none", "--abs-proxy-base-path=/codeserver/app1"], "") + const resp = await codeServer.fetch(absProxyPath) + expect(resp.status).toBe(200) + const text = await resp.text() + expect(text).toBe("app being served behind a prefixed path") + }) }) // NOTE@jsjoeio