diff --git a/apps/api/v2/src/app.ts b/apps/api/v2/src/app.ts index b452d0ddca..cbcff9b57b 100644 --- a/apps/api/v2/src/app.ts +++ b/apps/api/v2/src/app.ts @@ -18,6 +18,7 @@ import { CAL_API_VERSION_HEADER, X_CAL_CLIENT_ID, X_CAL_SECRET_KEY, + X_CAL_PLATFORM_EMBED, } from "@calcom/platform-constants"; import { TRPCExceptionFilter } from "./filters/trpc-exception.filter"; @@ -45,6 +46,7 @@ export const bootstrap = (app: NestExpressApplication): NestExpressApplication = allowedHeaders: [ X_CAL_CLIENT_ID, X_CAL_SECRET_KEY, + X_CAL_PLATFORM_EMBED, CAL_API_VERSION_HEADER, "Accept", "Authorization", diff --git a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts index 251824bf5f..af70edb760 100644 --- a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts +++ b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts @@ -37,7 +37,7 @@ import { Request } from "express"; import { NextApiRequest } from "next/types"; import { v4 as uuidv4 } from "uuid"; -import { X_CAL_CLIENT_ID } from "@calcom/platform-constants"; +import { X_CAL_CLIENT_ID, X_CAL_PLATFORM_EMBED } from "@calcom/platform-constants"; import { BOOKING_READ, SUCCESS_STATUS, BOOKING_WRITE } from "@calcom/platform-constants"; import { handleNewRecurringBooking, @@ -161,14 +161,15 @@ export class BookingsController_2024_04_15 { async createBooking( @Req() req: BookingRequest, @Body() body: CreateBookingInput_2024_04_15, - @Headers(X_CAL_CLIENT_ID) clientId?: string + @Headers(X_CAL_CLIENT_ID) clientId?: string, + @Headers(X_CAL_PLATFORM_EMBED) isEmbed?: string ): Promise>> { const oAuthClientId = clientId?.toString(); const { orgSlug, locationUrl } = body; req.headers["x-cal-force-slug"] = orgSlug; try { const booking = await handleNewBooking( - await this.createNextApiBookingRequest(req, oAuthClientId, locationUrl) + await this.createNextApiBookingRequest(req, oAuthClientId, locationUrl, isEmbed) ); if (booking.userId && booking.uid && booking.startTime) { void (await this.billingService.increaseUsageByUserId(booking.userId, { @@ -192,13 +193,16 @@ export class BookingsController_2024_04_15 { @Req() req: BookingRequest, @Param("bookingId") bookingId: string, @Body() _: CancelBookingInput_2024_04_15, - @Headers(X_CAL_CLIENT_ID) clientId?: string + @Headers(X_CAL_CLIENT_ID) clientId?: string, + @Headers(X_CAL_PLATFORM_EMBED) isEmbed?: string ): Promise> { const oAuthClientId = clientId?.toString(); if (bookingId) { try { req.body.id = parseInt(bookingId); - const res = await handleCancelBooking(await this.createNextApiBookingRequest(req, oAuthClientId)); + const res = await handleCancelBooking( + await this.createNextApiBookingRequest(req, oAuthClientId, undefined, isEmbed) + ); if (!res.onlyRemovedAttendee) { void (await this.billingService.cancelUsageByBookingUid(res.bookingUid)); } @@ -246,7 +250,8 @@ export class BookingsController_2024_04_15 { async createRecurringBooking( @Req() req: BookingRequest, @Body() _: CreateRecurringBookingInput_2024_04_15[], - @Headers(X_CAL_CLIENT_ID) clientId?: string + @Headers(X_CAL_CLIENT_ID) clientId?: string, + @Headers(X_CAL_PLATFORM_EMBED) isEmbed?: string ): Promise> { const oAuthClientId = clientId?.toString(); try { @@ -258,7 +263,7 @@ export class BookingsController_2024_04_15 { } const createdBookings: BookingResponse[] = await handleNewRecurringBooking( - await this.createNextApiRecurringBookingRequest(req, oAuthClientId) + await this.createNextApiRecurringBookingRequest(req, oAuthClientId, undefined, isEmbed) ); createdBookings.forEach(async (booking) => { @@ -284,13 +289,14 @@ export class BookingsController_2024_04_15 { async createInstantBooking( @Req() req: BookingRequest, @Body() _: CreateBookingInput_2024_04_15, - @Headers(X_CAL_CLIENT_ID) clientId?: string + @Headers(X_CAL_CLIENT_ID) clientId?: string, + @Headers(X_CAL_PLATFORM_EMBED) isEmbed?: string ): Promise>>> { const oAuthClientId = clientId?.toString(); req.userId = (await this.getOwnerId(req)) ?? -1; try { const instantMeeting = await handleInstantMeeting( - await this.createNextApiBookingRequest(req, oAuthClientId) + await this.createNextApiBookingRequest(req, oAuthClientId, undefined, isEmbed) ); if (instantMeeting.userId && instantMeeting.bookingUid) { @@ -332,8 +338,14 @@ export class BookingsController_2024_04_15 { } } - private async getOAuthClientsParams(clientId: string): Promise { + private async getOAuthClientsParams(clientId: string, isEmbed = false): Promise { const res = { ...DEFAULT_PLATFORM_PARAMS }; + + if (isEmbed) { + // embed should ignore oauth client settings and enable emails by default + return { ...res, arePlatformEmailsEnabled: true }; + } + try { const client = await this.oAuthClientRepository.getOAuthClient(clientId); // fetch oAuthClient from db and use data stored in db to set these values @@ -354,13 +366,14 @@ export class BookingsController_2024_04_15 { private async createNextApiBookingRequest( req: BookingRequest, oAuthClientId?: string, - platformBookingLocation?: string + platformBookingLocation?: string, + isEmbed?: string ): Promise { const requestId = req.get("X-Request-Id"); const clone = { ...req }; const userId = (await this.getOwnerId(req)) ?? -1; const oAuthParams = oAuthClientId - ? await this.getOAuthClientsParams(oAuthClientId) + ? await this.getOAuthClientsParams(oAuthClientId, this.transformToBoolean(isEmbed)) : DEFAULT_PLATFORM_PARAMS; this.logger.log(`createNextApiBookingRequest_2024_04_15`, { requestId, @@ -377,12 +390,13 @@ export class BookingsController_2024_04_15 { private async createNextApiRecurringBookingRequest( req: BookingRequest, oAuthClientId?: string, - platformBookingLocation?: string + platformBookingLocation?: string, + isEmbed?: string ): Promise { const clone = { ...req }; const userId = (await this.getOwnerId(req)) ?? -1; const oAuthParams = oAuthClientId - ? await this.getOAuthClientsParams(oAuthClientId) + ? await this.getOAuthClientsParams(oAuthClientId, this.transformToBoolean(isEmbed)) : DEFAULT_PLATFORM_PARAMS; const requestId = req.get("X-Request-Id"); this.logger.log(`createNextApiRecurringBookingRequest_2024_04_15`, { @@ -424,4 +438,8 @@ export class BookingsController_2024_04_15 { throw new InternalServerErrorException(errMsg); } + + private transformToBoolean(v?: string): boolean { + return v && typeof v === "string" ? v.toLowerCase() === "true" : false; + } } diff --git a/packages/platform/atoms/cal-provider/CalProvider.tsx b/packages/platform/atoms/cal-provider/CalProvider.tsx index 784f03f373..416e409077 100644 --- a/packages/platform/atoms/cal-provider/CalProvider.tsx +++ b/packages/platform/atoms/cal-provider/CalProvider.tsx @@ -112,6 +112,10 @@ export function CalProvider({ } }, [accessToken]); + useEffect(() => { + http.setPlatformEmbedHeader(isEmbed); + }, [isEmbed]); + return ( { return instance.defaults.headers.common?.[X_CAL_CLIENT_ID]?.toString() ?? ""; }, + setPlatformEmbedHeader: (isEmbed: boolean) => { + instance.defaults.headers.common[X_CAL_PLATFORM_EMBED] = isEmbed.toString(); + }, + getPlatformEmbedHeader: () => { + return instance.defaults.headers.common?.[X_CAL_PLATFORM_EMBED]?.toString() ?? ""; + }, setVersionHeader: (clientId: string) => { instance.defaults.headers.common[CAL_API_VERSION_HEADER] = clientId; }, diff --git a/packages/platform/constants/api.ts b/packages/platform/constants/api.ts index 7ad01d5387..d6386f047a 100644 --- a/packages/platform/constants/api.ts +++ b/packages/platform/constants/api.ts @@ -48,6 +48,7 @@ export const API_ERROR_CODES = [ // Request headers export const X_CAL_SECRET_KEY = "x-cal-secret-key"; export const X_CAL_CLIENT_ID = "x-cal-client-id"; +export const X_CAL_PLATFORM_EMBED = "x-cal-platform-embed"; // HTTP status codes export const HTTP_CODE_TOKEN_EXPIRED = 498;