From bea542b3103ea3716cb69da716be6fbb3b474bb3 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Tue, 20 Jan 2026 18:18:30 +0530 Subject: [PATCH] fix: correct bookingUrl for EU instance in API responses (#27046) * fix: use WEBAPP_URL for booking URLs when domains differ For EU deployments where WEBAPP_URL (app.cal.eu) differs from WEBSITE_URL (cal.com), use WEBAPP_URL for non-org booking URLs. This fixes incorrect bookingUrl in API responses for EU instance. * Created new shared utility getTldPlus1 and updated code to use shared function instead of inline copy * use same comment --- .../ee/organizations/lib/orgDomains.test.ts | 15 +++++++++++---- .../features/ee/organizations/lib/orgDomains.ts | 10 ++++++++-- packages/lib/getSafeRedirectUrl.ts | 8 ++------ packages/lib/getTldPlus1.ts | 7 +++++++ 4 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 packages/lib/getTldPlus1.ts diff --git a/packages/features/ee/organizations/lib/orgDomains.test.ts b/packages/features/ee/organizations/lib/orgDomains.test.ts index dd5532a185..ca1b418de0 100644 --- a/packages/features/ee/organizations/lib/orgDomains.test.ts +++ b/packages/features/ee/organizations/lib/orgDomains.test.ts @@ -7,7 +7,7 @@ import { } from "@calcom/features/ee/organizations/lib/orgDomains"; import * as constants from "@calcom/lib/constants"; -function setupEnvs({ WEBAPP_URL = "https://app.cal.com", WEBSITE_URL } = {}) { +function setupEnvs({ WEBAPP_URL = "https://app.cal.com", WEBSITE_URL = "https://cal.com" } = {}) { Object.defineProperty(constants, "WEBAPP_URL", { value: WEBAPP_URL }); Object.defineProperty(constants, "WEBSITE_URL", { value: WEBSITE_URL }); Object.defineProperty(constants, "ALLOWED_HOSTNAMES", { @@ -89,12 +89,19 @@ describe("Org Domains Utils", () => { }); describe("getOrgFullOrigin", () => { - it("should return the regular(non-org) origin if slug is null", () => { + it("should return WEBSITE_URL when slug is null and domains match", () => { setupEnvs({ WEBAPP_URL: "https://app.cal.com", - WEBSITE_URL: "https://abc.com", + WEBSITE_URL: "https://cal.com", }); - expect(getOrgFullOrigin(null)).toEqual("https://abc.com"); + expect(getOrgFullOrigin(null)).toEqual("https://cal.com"); + }); + it("should return WEBAPP_URL when slug is null and domains differ (EU case)", () => { + setupEnvs({ + WEBAPP_URL: "https://app.cal.eu", + WEBSITE_URL: "https://cal.com", + }); + expect(getOrgFullOrigin(null)).toEqual("https://app.cal.eu"); }); it("should return the org origin if slug is set", () => { setupEnvs({ diff --git a/packages/features/ee/organizations/lib/orgDomains.ts b/packages/features/ee/organizations/lib/orgDomains.ts index 2ef2f32a93..7a354cd873 100644 --- a/packages/features/ee/organizations/lib/orgDomains.ts +++ b/packages/features/ee/organizations/lib/orgDomains.ts @@ -2,6 +2,7 @@ import type { IncomingMessage } from "node:http"; import { IS_PRODUCTION, WEBSITE_URL, SINGLE_ORG_SLUG } from "@calcom/lib/constants"; import { ALLOWED_HOSTNAMES, RESERVED_SUBDOMAINS, WEBAPP_URL } from "@calcom/lib/constants"; +import { getTldPlus1 } from "@calcom/lib/getTldPlus1"; import logger from "@calcom/lib/logger"; import slugify from "@calcom/lib/slugify"; import type { Prisma } from "@calcom/prisma/client"; @@ -145,8 +146,13 @@ export function subdomainSuffix() { } export function getOrgFullOrigin(slug: string | null, options: { protocol: boolean } = { protocol: true }) { - if (!slug) - return options.protocol ? WEBSITE_URL : WEBSITE_URL.replace("https://", "").replace("http://", ""); + if (!slug) { + // Use WEBAPP_URL if domains differ (e.g., EU: app.cal.eu vs cal.com) + const useWebappUrl = + getTldPlus1(new URL(WEBSITE_URL).hostname) !== getTldPlus1(new URL(WEBAPP_URL).hostname); + const baseUrl = useWebappUrl ? WEBAPP_URL : WEBSITE_URL; + return options.protocol ? baseUrl : baseUrl.replace("https://", "").replace("http://", ""); + } const orgFullOrigin = `${ options.protocol ? `${new URL(WEBSITE_URL).protocol}//` : "" diff --git a/packages/lib/getSafeRedirectUrl.ts b/packages/lib/getSafeRedirectUrl.ts index 13740577be..67332eb921 100644 --- a/packages/lib/getSafeRedirectUrl.ts +++ b/packages/lib/getSafeRedirectUrl.ts @@ -1,4 +1,5 @@ import { CONSOLE_URL, WEBAPP_URL, WEBSITE_URL, EMBED_LIB_URL } from "@calcom/lib/constants"; +import { getTldPlus1 } from "@calcom/lib/getTldPlus1"; // It ensures that redirection URL safe where it is accepted through a query params or other means where user can change it. export const getSafeRedirectUrl = (url = "") => { @@ -46,10 +47,5 @@ export function isSafeUrlToLoadResourceFrom(urlString: string) { } catch { return false; } - - function getTldPlus1(hostname: string) { - // Note: It doesn't support multipart tlds like .co.uk and thus makes only one part tld's safe like .com(and thus cal.com) - // If we want to use it elsewhere as well(apart from embed/preview.ts) we must consider Public Suffix List - return hostname.split(".").slice(-2).join("."); - } } + diff --git a/packages/lib/getTldPlus1.ts b/packages/lib/getTldPlus1.ts new file mode 100644 index 0000000000..eef88b1812 --- /dev/null +++ b/packages/lib/getTldPlus1.ts @@ -0,0 +1,7 @@ +/** + * Note: It doesn't support multipart tlds like .co.uk and thus makes only one part tld's safe like .com(and thus cal.com) + * If we want to use it elsewhere as well(apart from embed/preview.ts) we must consider Public Suffix List + */ +export function getTldPlus1(hostname: string): string { + return hostname.split(".").slice(-2).join("."); +}