From f4bb080b657f20100c43d77a0575e4e86df1b141 Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Tue, 24 Feb 2026 18:28:58 +0100 Subject: [PATCH] fix(cli): use correct subpath during setup slack app for self hosted deployments --- .../blink/src/cli/setup-slack-app.test.ts | 39 +++++++++++++++++++ packages/blink/src/cli/setup-slack-app.ts | 11 +++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/blink/src/cli/setup-slack-app.test.ts b/packages/blink/src/cli/setup-slack-app.test.ts index b3b6fc16..ef866dc4 100644 --- a/packages/blink/src/cli/setup-slack-app.test.ts +++ b/packages/blink/src/cli/setup-slack-app.test.ts @@ -11,6 +11,7 @@ import { } from "./lib/in-memory-cli"; import { makeTmpDir } from "./lib/terminal"; import { + appendSubpath, setupSlackApp, updateEnvCredentials, verifySlackSignature, @@ -111,6 +112,44 @@ describe("verifySlackSignature", () => { }); }); +describe("appendSubpath", () => { + it("should append a subpath to a simple URL", () => { + expect(appendSubpath("https://example.com", "/slack")).toBe( + "https://example.com/slack" + ); + }); + + it("should append a subpath to a URL with an existing path", () => { + expect(appendSubpath("https://example.com/devhook/abc", "/slack")).toBe( + "https://example.com/devhook/abc/slack" + ); + }); + + it("should handle trailing slashes on the base URL", () => { + expect(appendSubpath("https://example.com/devhook/", "/slack")).toBe( + "https://example.com/devhook/slack" + ); + }); + + it("should handle subpath without leading slash", () => { + expect(appendSubpath("https://example.com/devhook", "slack")).toBe( + "https://example.com/devhook/slack" + ); + }); + + it("should handle both trailing and leading slashes", () => { + expect(appendSubpath("https://example.com/devhook///", "///slack")).toBe( + "https://example.com/devhook/slack" + ); + }); + + it("should preserve query parameters", () => { + expect( + appendSubpath("https://example.com/devhook?token=abc", "/slack") + ).toBe("https://example.com/devhook/slack?token=abc"); + }); +}); + describe("updateEnvCredentials", () => { it("should add credentials to an empty env file", async () => { await using tempDir = await makeTmpDir(); diff --git a/packages/blink/src/cli/setup-slack-app.ts b/packages/blink/src/cli/setup-slack-app.ts index cc0d21e3..5cb887f4 100644 --- a/packages/blink/src/cli/setup-slack-app.ts +++ b/packages/blink/src/cli/setup-slack-app.ts @@ -97,6 +97,12 @@ export function verifySlackSignature( ); } +export function appendSubpath(baseUrl: string, subpath: string): string { + const url = new URL(baseUrl); + url.pathname = `${url.pathname.replace(/\/+$/, "")}/${subpath.replace(/^\/+/, "")}`; + return url.toString(); +} + const makeDisposable = (value: unknown): Disposable => { if (!(typeof value === "object" && value !== null)) { throw new Error("Unable to make value disposable, it's not an object"); @@ -234,7 +240,10 @@ export async function setupSlackApp( baseURL: host, authToken: getAuthToken(), }); - const webhookUrl = await client.devhook.getUrl(devhookId); + const webhookUrl = appendSubpath( + await client.devhook.getUrl(devhookId), + "/slack" + ); log.info("Starting webhook listener...");