diff --git a/src/node/cli.ts b/src/node/cli.ts index 23006147..8feaf982 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -39,6 +39,7 @@ export interface Args extends VsArgs { readonly "install-extension"?: string[] readonly "show-versions"?: boolean readonly "uninstall-extension"?: string[] + readonly "proxy-domain"?: string[] readonly locale?: string readonly _: string[] } @@ -111,6 +112,7 @@ const options: Options> = { "install-extension": { type: "string[]", description: "Install or update a VS Code extension by id or vsix." }, "uninstall-extension": { type: "string[]", description: "Uninstall a VS Code extension by id." }, "show-versions": { type: "boolean", description: "Show VS Code extension versions." }, + "proxy-domain": { type: "string[]", description: "Domain used for proxying ports." }, locale: { type: "string" }, log: { type: LogLevel }, diff --git a/src/node/entry.ts b/src/node/entry.ts index 81e40a21..ed5d41ea 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -35,6 +35,14 @@ const main = async (args: Args): Promise => { const auth = args.auth || AuthType.Password const originalPassword = auth === AuthType.Password && (process.env.PASSWORD || (await generatePassword())) + /** + * Domains can be in the form `coder.com` or `*.coder.com`. Either way, + * `[number].coder.com` will be proxied to `number`. + */ + const normalizeProxyDomains = (domains?: string[]): string[] => { + return domains ? domains.map((d) => d.replace(/^\*\./, "")).filter((d, i) => domains.indexOf(d) === i) : [] + } + // Spawn the main HTTP server. const options: HttpServerOptions = { auth, @@ -42,6 +50,7 @@ const main = async (args: Args): Promise => { host: args.host || (args.auth === AuthType.Password && typeof args.cert !== "undefined" ? "0.0.0.0" : "localhost"), password: originalPassword ? hash(originalPassword) : undefined, port: typeof args.port !== "undefined" ? args.port : process.env.PORT ? parseInt(process.env.PORT, 10) : 8080, + proxyDomains: normalizeProxyDomains(args["proxy-domain"]), socket: args.socket, ...(args.cert && !args.cert.value ? await generateCertificate() @@ -91,6 +100,15 @@ const main = async (args: Args): Promise => { logger.info(" - Not serving HTTPS") } + if (options.proxyDomains && options.proxyDomains.length === 1) { + logger.info(` - Proxying *.${options.proxyDomains[0]}`) + } else if (options.proxyDomains && options.proxyDomains.length > 1) { + logger.info(" - Proxying the following domains:") + options.proxyDomains.forEach((domain) => { + logger.info(` - *.${domain}`) + }) + } + logger.info(`Automatic updates are ${update.enabled ? "enabled" : "disabled"}`) let sshHostKey = args["ssh-host-key"] diff --git a/src/node/http.ts b/src/node/http.ts index 06b7a167..dd25ec72 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -99,6 +99,7 @@ export interface HttpServerOptions { readonly commit?: string readonly host?: string readonly password?: string + readonly proxyDomains?: string[] readonly port?: number readonly socket?: string } diff --git a/test/cli.test.ts b/test/cli.test.ts index 9de3900e..aab12684 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -117,6 +117,7 @@ describe("cli", () => { assert.throws(() => parse(["--auth=", "--log=debug"]), /--auth requires a value/) assert.throws(() => parse(["--auth", "--log"]), /--auth requires a value/) assert.throws(() => parse(["--auth", "--invalid"]), /--auth requires a value/) + assert.throws(() => parse(["--ssh-host-key"]), /--ssh-host-key requires a value/) }) it("should error if value is invalid", () => { @@ -160,4 +161,19 @@ describe("cli", () => { auth: "none", }) }) + + it("should support repeatable flags", () => { + assert.deepEqual(parse(["--proxy-domain", "*.coder.com"]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + "proxy-domain": ["*.coder.com"], + }) + assert.deepEqual(parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"]), { + _: [], + "extensions-dir": path.join(xdgLocalDir, "extensions"), + "user-data-dir": xdgLocalDir, + "proxy-domain": ["*.coder.com", "test.com"], + }) + }) })