diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/booking-guests.controller.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/booking-guests.controller.ts index 3bb01bc87d..015851bbe7 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/booking-guests.controller.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/booking-guests.controller.ts @@ -3,6 +3,7 @@ import { AddGuestsOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/add import { BookingGuestsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/booking-guests.service"; import { VERSION_2024_08_13_VALUE, VERSION_2024_08_13 } from "@/lib/api-versions"; import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers"; +import { Throttle } from "@/lib/endpoint-throttler-decorator"; import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator"; import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator"; import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard"; @@ -35,10 +36,19 @@ export class BookingGuestsController_2024_08_13 { @HttpCode(HttpStatus.OK) @Permissions([BOOKING_WRITE]) @UseGuards(ApiAuthGuard, BookingUidGuard) + @Throttle({ + limit: 5, + ttl: 60000, + blockDuration: 60000, + name: "booking_guests_add", + }) @ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER) @ApiOperation({ summary: "Add guests to an existing booking", - description: `Add one or more guests to an existing booking. + description: `Add one or more guests to an existing booking. Maximum 10 guests per request, with a limit of 30 total guests per booking. + + **Rate Limiting:** + This endpoint is rate limited to 5 requests per minute to prevent abuse. **Email Notifications:** When guests are added, the following notifications are sent (unless disabled by event type settings): diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/booking-guests.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/booking-guests.service.ts index 7fca9186c7..c434cfae1b 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/booking-guests.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/booking-guests.service.ts @@ -2,11 +2,13 @@ import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/reposito import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service"; import { PlatformBookingsService } from "@/ee/bookings/shared/platform-bookings.service"; import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy"; -import { Injectable, Logger, HttpException, NotFoundException } from "@nestjs/common"; +import { Injectable, Logger, HttpException, NotFoundException, BadRequestException } from "@nestjs/common"; import { addGuestsHandler } from "@calcom/platform-libraries/bookings"; import type { AddGuestsInput_2024_08_13 } from "@calcom/platform-types"; +const MAX_TOTAL_GUESTS_PER_BOOKING = 30; + @Injectable() export class BookingGuestsService_2024_08_13 { private readonly logger = new Logger("BookingGuestsService_2024_08_13"); @@ -23,6 +25,18 @@ export class BookingGuestsService_2024_08_13 { throw new NotFoundException(`Booking with uid ${bookingUid} not found`); } + const currentGuestCount = booking.attendees.length; + const newGuestCount = input.guests.length; + const totalGuestCount = currentGuestCount + newGuestCount; + + if (totalGuestCount > MAX_TOTAL_GUESTS_PER_BOOKING) { + const remainingSlots = Math.max(0, MAX_TOTAL_GUESTS_PER_BOOKING - currentGuestCount); + throw new BadRequestException( + `Cannot add ${newGuestCount} guests. This booking already has ${currentGuestCount} attendees. ` + + `Maximum total guests allowed is ${MAX_TOTAL_GUESTS_PER_BOOKING}. You can add up to ${remainingSlots} more guests.` + ); + } + const platformClientParams = booking.eventTypeId ? await this.platformBookingsService.getOAuthClientParams(booking.eventTypeId) : undefined; diff --git a/docs/api-reference/v2/openapi.json b/docs/api-reference/v2/openapi.json index 76ce00679b..f731e3cddd 100644 --- a/docs/api-reference/v2/openapi.json +++ b/docs/api-reference/v2/openapi.json @@ -10170,7 +10170,7 @@ "post": { "operationId": "BookingGuestsController_2024_08_13_addGuests", "summary": "Add guests to an existing booking", - "description": "Add one or more guests to an existing booking.\n \n **Email Notifications:**\n When guests are added, the following notifications are sent (unless disabled by event type settings):\n \n - **Organizer & Team Members:** Receive an \"Add Guests\" notification email informing them that new guests have been added to the booking.\n \n - **New Guests:** Receive a \"Scheduled Event\" email with full booking details and calendar invite. If they have a phone number, they also receive an SMS notification.\n \n - **Existing Guests:** Receive an \"Add Guests\" notification email informing them that additional guests have been added to the booking.\n \n The cal-api-version header is required for this endpoint. Without it, the request will fail with a 404 error.\n ", + "description": "Add one or more guests to an existing booking. Maximum 10 guests per request, with a limit of 30 total guests per booking.\n \n **Rate Limiting:**\n This endpoint is rate limited to 5 requests per minute to prevent abuse.\n \n **Email Notifications:**\n When guests are added, the following notifications are sent (unless disabled by event type settings):\n \n - **Organizer & Team Members:** Receive an \"Add Guests\" notification email informing them that new guests have been added to the booking.\n \n - **New Guests:** Receive a \"Scheduled Event\" email with full booking details and calendar invite. If they have a phone number, they also receive an SMS notification.\n \n - **Existing Guests:** Receive an \"Add Guests\" notification email informing them that additional guests have been added to the booking.\n \n The cal-api-version header is required for this endpoint. Without it, the request will fail with a 404 error.\n ", "parameters": [ { "name": "cal-api-version", @@ -31182,7 +31182,7 @@ "type": "object", "properties": { "guests": { - "description": "Array of guests to add to the booking", + "description": "Array of guests to add to the booking. Maximum 10 guests per request.", "example": [ { "email": "john.doe@example.com", @@ -31195,6 +31195,8 @@ } ], "type": "array", + "minItems": 1, + "maxItems": 10, "items": { "$ref": "#/components/schemas/Guest" } diff --git a/packages/platform/types/bookings/2024-08-13/inputs/add-guests.input.ts b/packages/platform/types/bookings/2024-08-13/inputs/add-guests.input.ts index 34f4e324cd..241240660f 100644 --- a/packages/platform/types/bookings/2024-08-13/inputs/add-guests.input.ts +++ b/packages/platform/types/bookings/2024-08-13/inputs/add-guests.input.ts @@ -4,6 +4,7 @@ import { IsArray, IsEmail, ArrayMinSize, + ArrayMaxSize, IsString, IsOptional, IsTimeZone, @@ -67,12 +68,13 @@ class Guest { export class AddGuestsInput_2024_08_13 { @ArrayMinSize(1) + @ArrayMaxSize(10, { message: "Cannot add more than 10 guests at a time" }) @IsArray() @ValidateNested({ each: true }) @Type(() => Guest) @ApiProperty({ type: [Guest], - description: "Array of guests to add to the booking", + description: "Array of guests to add to the booking. Maximum 10 guests per request.", example: [ { email: "john.doe@example.com",