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
This commit is contained in:
Dhairyashil Shinde
2026-01-20 12:48:30 +00:00
committed by GitHub
parent e7a69d68df
commit bea542b310
4 changed files with 28 additions and 12 deletions
@@ -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({
@@ -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}//` : ""
+2 -6
View File
@@ -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(".");
}
}
+7
View File
@@ -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(".");
}