From ec272c9150368f295034aa8b30e0d09ef8438c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 8 Oct 2026 17:13:49 +0200 Subject: [PATCH] =?UTF-8?q?Show=20the=20callback=20path,=20not=20'undefine?= =?UTF-8?q?d/api/=E2=80=A6',=20on=20an=20unset=20app=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With NEXT_PUBLIC_APP_URL unset, some providers spliced the missing value into their redirect template and the integration cards told the reader to register 'undefined/api/polar/callback'. A callback that is not an absolute http(s) URL now counts as not configured, so the card shows the fixed path it shows for every other unconfigured provider. --- src/lib/integrations/__tests__/callback-urls.test.ts | 12 ++++++++++++ src/lib/integrations/callback-urls.ts | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/integrations/__tests__/callback-urls.test.ts b/src/lib/integrations/__tests__/callback-urls.test.ts index 27d25c2e3..3214448c3 100644 --- a/src/lib/integrations/__tests__/callback-urls.test.ts +++ b/src/lib/integrations/__tests__/callback-urls.test.ts @@ -110,4 +110,16 @@ describe("getIntegrationCallbackUrls", () => { expect(urls.withings).toBe("http://health.example/api/withings/callback"); expect(urls.whoop).toBe("http://health.example/api/whoop/callback"); }); + + it("reports null rather than 'undefined/api/…' when the app URL is unset", () => { + // A resolver that splices the unset variable into its template would + // otherwise hand the card "undefined/api/polar/callback" to show. + const urls = getIntegrationCallbackUrls(); + for (const provider of INTEGRATION_CALLBACK_PROVIDERS) { + expect(urls[provider] ?? "", provider).not.toMatch(/^undefined/); + if (urls[provider] !== null) { + expect(urls[provider], provider).toMatch(/^https?:\/\//); + } + } + }); }); diff --git a/src/lib/integrations/callback-urls.ts b/src/lib/integrations/callback-urls.ts index d9e7036bf..5f6b00d39 100644 --- a/src/lib/integrations/callback-urls.ts +++ b/src/lib/integrations/callback-urls.ts @@ -59,7 +59,12 @@ const RESOLVERS: Record string> = { function resolve(provider: IntegrationCallbackProvider): string | null { try { - return RESOLVERS[provider](); + const url = RESOLVERS[provider](); + // Some resolvers splice an unset NEXT_PUBLIC_APP_URL into the template + // and hand back "undefined/api/…/callback". That is not a URL anyone can + // paste into a vendor form; report it as not configured so the card + // shows the path instead. + return /^https?:\/\//i.test(url) ? url : null; } catch { return null; }