diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/repositories/bookings.repository.ts b/apps/api/v2/src/ee/bookings/2024-08-13/repositories/bookings.repository.ts index ed468a40f750e8..31e888c220df6c 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/repositories/bookings.repository.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/repositories/bookings.repository.ts @@ -30,6 +30,7 @@ export class BookingsRepository_2024_08_13 { attendees: true, user: true, eventType: true, + references: true, }, }); } @@ -49,6 +50,7 @@ export class BookingsRepository_2024_08_13 { }, user: true, eventType: true, + references: true, }, }); } @@ -125,6 +127,7 @@ export class BookingsRepository_2024_08_13 { attendees: true, user: true, eventType: true, + references: true, }, }); } @@ -142,6 +145,7 @@ export class BookingsRepository_2024_08_13 { }, user: true, eventType: true, + references: true, }, }); } @@ -155,6 +159,7 @@ export class BookingsRepository_2024_08_13 { attendees: true, user: true, eventType: true, + references: true, }, }); if (!booking) { @@ -181,6 +186,7 @@ export class BookingsRepository_2024_08_13 { }, user: true, eventType: true, + references: true, }, }); } @@ -216,6 +222,7 @@ export class BookingsRepository_2024_08_13 { attendees: true, user: true, eventType: true, + references: true, }, }); } diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts index 532120f76aaa43..faf796ad869c0a 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts @@ -622,17 +622,17 @@ export class BookingsService_2024_08_13 { const isSeated = !!booking.eventType?.seatsPerTimeSlot; if (isRecurring && !isSeated) { - return this.outputService.getOutputRecurringBooking(booking); + return this.outputService.getOutputRecurringBooking(booking, userIsEventTypeAdminOrOwner); } if (isRecurring && isSeated) { const showAttendees = userIsEventTypeAdminOrOwner || !!booking.eventType?.seatsShowAttendees; - return this.outputService.getOutputRecurringSeatedBooking(booking, showAttendees); + return this.outputService.getOutputRecurringSeatedBooking(booking, showAttendees, userIsEventTypeAdminOrOwner); } if (isSeated) { const showAttendees = userIsEventTypeAdminOrOwner || !!booking.eventType?.seatsShowAttendees; - return this.outputService.getOutputSeatedBooking(booking, showAttendees); + return this.outputService.getOutputSeatedBooking(booking, showAttendees, userIsEventTypeAdminOrOwner); } - return this.outputService.getOutputBooking(booking); + return this.outputService.getOutputBooking(booking, userIsEventTypeAdminOrOwner); } const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUserAndEvent(uid); @@ -644,10 +644,10 @@ export class BookingsService_2024_08_13 { if (isRecurringSeated) { const showAttendees = userIsEventTypeAdminOrOwner || !!recurringBooking[0].eventType?.seatsShowAttendees; - return this.outputService.getOutputRecurringSeatedBookings(ids, showAttendees); + return this.outputService.getOutputRecurringSeatedBookings(ids, showAttendees, userIsEventTypeAdminOrOwner); } - return this.outputService.getOutputRecurringBookings(ids); + return this.outputService.getOutputRecurringBookings(ids, userIsEventTypeAdminOrOwner); } async getBookingBySeatUid(seatUid: string, authUser: AuthOptionalUser) { @@ -662,7 +662,7 @@ export class BookingsService_2024_08_13 { const userIsEventTypeAdminOrOwner = authUser && booking.eventType ? await this.eventTypeAccessService.userIsEventTypeAdminOrOwner( - authUser, + { ...authUser, isSystemAdmin: authUser.isSystemAdmin ?? false }, booking.eventType as EventType ) : false; @@ -682,16 +682,16 @@ export class BookingsService_2024_08_13 { }; if (isRecurring) { - return this.outputService.getOutputRecurringSeatedBooking(bookingWithFilteredAttendees, true); + return this.outputService.getOutputRecurringSeatedBooking(bookingWithFilteredAttendees, true, userIsEventTypeAdminOrOwner); } - return this.outputService.getOutputSeatedBooking(bookingWithFilteredAttendees, true); + return this.outputService.getOutputSeatedBooking(bookingWithFilteredAttendees, true, userIsEventTypeAdminOrOwner); } if (isRecurring) { - return this.outputService.getOutputRecurringSeatedBooking(booking, true); + return this.outputService.getOutputRecurringSeatedBooking(booking, true, userIsEventTypeAdminOrOwner); } - return this.outputService.getOutputSeatedBooking(booking, true); + return this.outputService.getOutputSeatedBooking(booking, true, userIsEventTypeAdminOrOwner); } async getBookings( @@ -749,16 +749,21 @@ export class BookingsService_2024_08_13 { absentHost: !!booking.noShowHost, }; + const userIsEventTypeAdminOrOwner = + user && formatted.eventType + ? await this.eventTypeAccessService.userIsEventTypeAdminOrOwner({ ...user, isSystemAdmin: false } as ApiAuthGuardUser, formatted.eventType) + : false; + const isRecurring = !!formatted.recurringEventId; const isSeated = !!formatted.eventType?.seatsPerTimeSlot; if (isRecurring && !isSeated) { - formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted)); + formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted, userIsEventTypeAdminOrOwner)); } else if (isRecurring && isSeated) { - formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted, true)); + formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted, true, userIsEventTypeAdminOrOwner)); } else if (isSeated) { - formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted, true)); + formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted, true, userIsEventTypeAdminOrOwner)); } else { - formattedBookings.push(await this.outputService.getOutputBooking(formatted)); + formattedBookings.push(await this.outputService.getOutputBooking(formatted, userIsEventTypeAdminOrOwner)); } } @@ -855,7 +860,7 @@ export class BookingsService_2024_08_13 { const isPlatformManagedUserBooking = !!(booking.userId && booking.user?.isPlatformManaged); if (isRecurring && !isSeated) { - const outputBooking = await this.outputService.getOutputRecurringBooking(databaseBooking); + const outputBooking = await this.outputService.getOutputRecurringBooking(databaseBooking, userIsEventTypeAdminOrOwner); return Object.assign(outputBooking, { isPlatformManagedUserBooking }); } if (isRecurring && isSeated) { @@ -874,7 +879,7 @@ export class BookingsService_2024_08_13 { ); return Object.assign(outputBooking, { isPlatformManagedUserBooking }); } - const outputBooking = await this.outputService.getOutputBooking(databaseBooking); + const outputBooking = await this.outputService.getOutputBooking(databaseBooking, userIsEventTypeAdminOrOwner); return Object.assign(outputBooking, { isPlatformManagedUserBooking }); } catch (error) { this.errorsBookingsService.handleBookingError(error, false); diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts index 1843da15df911d..c6fb814f4defab 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts @@ -83,6 +83,7 @@ type DatabaseBooking = Booking & { }[]; user: DatabaseUser | null; createdAt: Date; + references: { type: string; meetingPassword: string | null }[]; }; type BookingWithUser = Booking & { user: DatabaseUser | null }; @@ -97,7 +98,7 @@ export class OutputBookingsService_2024_08_13 { return email.replace(/\+[a-zA-Z0-9]{25}/, ""); } - async getOutputBooking(databaseBooking: DatabaseBooking) { + async getOutputBooking(databaseBooking: DatabaseBooking, isHostOrAdmin: boolean = false) { const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const duration = dateEnd.diff(dateStart, "minutes").minutes; @@ -111,6 +112,8 @@ export class OutputBookingsService_2024_08_13 { const rescheduledToUid = await this.getRescheduledToUid(databaseBooking); const rescheduledByEmail = await this.getRescheduledByEmail(databaseBooking); + const hostToken = isHostOrAdmin ? databaseBooking.references?.find(ref => ref.type === "daily_video")?.meetingPassword : undefined; + const booking = { id: databaseBooking.id, uid: databaseBooking.uid, @@ -139,6 +142,7 @@ export class OutputBookingsService_2024_08_13 { })), guests: bookingResponses.guests, location, + hostToken: hostToken || undefined, // note(Lauris): meetingUrl is deprecated meetingUrl: location, absentHost: !!databaseBooking.noShowHost, @@ -228,7 +232,7 @@ export class OutputBookingsService_2024_08_13 { }; } - async getOutputRecurringBookings(bookingsIds: number[]) { + async getOutputRecurringBookings(bookingsIds: number[], isHostOrAdmin: boolean = false) { const databaseBookings = await this.bookingsRepository.getByIdsWithAttendeesAndUserAndEvent(bookingsIds); const bookingsMap = new Map(databaseBookings.map(booking => [booking.id, booking])); @@ -238,12 +242,12 @@ export class OutputBookingsService_2024_08_13 { if (!databaseBooking) { throw new Error(`Booking with id=${bookingId} was not found in the database`); } - return this.getOutputRecurringBooking(databaseBooking); + return this.getOutputRecurringBooking(databaseBooking, isHostOrAdmin); }); return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()); } - getOutputRecurringBooking(databaseBooking: DatabaseBooking) { + getOutputRecurringBooking(databaseBooking: DatabaseBooking, isHostOrAdmin: boolean = false) { const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const duration = dateEnd.diff(dateStart, "minutes").minutes; @@ -255,6 +259,8 @@ export class OutputBookingsService_2024_08_13 { const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata); const location = metadata?.videoCallUrl || databaseBooking.location; + const hostToken = isHostOrAdmin ? databaseBooking.references?.find(ref => ref.type === "daily_video")?.meetingPassword : undefined; + const booking = { id: databaseBooking.id, uid: databaseBooking.uid, @@ -282,6 +288,7 @@ export class OutputBookingsService_2024_08_13 { })), guests: bookingResponses.guests, location, + hostToken: hostToken || undefined, // note(Lauris): meetingUrl is deprecated meetingUrl: location, recurringBookingUid: databaseBooking.recurringEventId, @@ -327,11 +334,11 @@ export class OutputBookingsService_2024_08_13 { userIsEventTypeAdminOrOwner: boolean ): Promise { const showAttendees = userIsEventTypeAdminOrOwner || !!databaseBooking.eventType?.seatsShowAttendees; - const getSeatedBookingOutput = await this.getOutputSeatedBooking(databaseBooking, showAttendees); + const getSeatedBookingOutput = await this.getOutputSeatedBooking(databaseBooking, showAttendees, userIsEventTypeAdminOrOwner); return { ...getSeatedBookingOutput, seatUid }; } - async getOutputSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) { + async getOutputSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean, isHostOrAdmin: boolean = false) { const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const duration = dateEnd.diff(dateStart, "minutes").minutes; @@ -340,6 +347,8 @@ export class OutputBookingsService_2024_08_13 { const rescheduledToUid = await this.getRescheduledToUid(databaseBooking); const rescheduledByEmail = await this.getRescheduledByEmail(databaseBooking); + const hostToken = isHostOrAdmin ? databaseBooking.references?.find(ref => ref.type === "daily_video")?.meetingPassword : undefined; + const booking = { id: databaseBooking.id, uid: databaseBooking.uid, @@ -357,6 +366,7 @@ export class OutputBookingsService_2024_08_13 { eventTypeId: databaseBooking.eventTypeId, attendees: [], location, + hostToken: hostToken || undefined, // note(Lauris): meetingUrl is deprecated meetingUrl: location, absentHost: !!databaseBooking.noShowHost, @@ -408,7 +418,7 @@ export class OutputBookingsService_2024_08_13 { return parsed; } - async getOutputRecurringSeatedBookings(bookingsIds: number[], showAttendees: boolean) { + async getOutputRecurringSeatedBookings(bookingsIds: number[], showAttendees: boolean, isHostOrAdmin: boolean = false) { const databaseBookings = await this.bookingsRepository.getByIdsWithAttendeesWithBookingSeatAndUserAndEvent(bookingsIds); const bookingsMap = new Map(databaseBookings.map(booking => [booking.id, booking])); @@ -418,7 +428,7 @@ export class OutputBookingsService_2024_08_13 { if (!databaseBooking) { throw new Error(`Booking with id=${bookingId} was not found in the database`); } - return this.getOutputRecurringSeatedBooking(databaseBooking, showAttendees); + return this.getOutputRecurringSeatedBooking(databaseBooking, showAttendees, isHostOrAdmin); }); return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()); @@ -456,18 +466,21 @@ export class OutputBookingsService_2024_08_13 { const showAttendees = userIsEventTypeAdminOrOwner || !!databaseBooking.eventType?.seatsShowAttendees; const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking( databaseBooking, - showAttendees + showAttendees, + userIsEventTypeAdminOrOwner ); return { ...getRecurringSeatedBookingOutput, seatUid }; } - getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) { + getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean, isHostOrAdmin: boolean = false) { const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const duration = dateEnd.diff(dateStart, "minutes").minutes; const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata); const location = metadata?.videoCallUrl || databaseBooking.location; + const hostToken = isHostOrAdmin ? databaseBooking.references?.find(ref => ref.type === "daily_video")?.meetingPassword : undefined; + const booking = { id: databaseBooking.id, uid: databaseBooking.uid, @@ -485,6 +498,7 @@ export class OutputBookingsService_2024_08_13 { eventTypeId: databaseBooking.eventTypeId, attendees: [], location, + hostToken: hostToken || undefined, // note(Lauris): meetingUrl is deprecated meetingUrl: location, recurringBookingUid: databaseBooking.recurringEventId, diff --git a/apps/api/v2/src/modules/booking-seat/booking-seat.repository.ts b/apps/api/v2/src/modules/booking-seat/booking-seat.repository.ts index df850c743e9cd2..e8d692886504fa 100644 --- a/apps/api/v2/src/modules/booking-seat/booking-seat.repository.ts +++ b/apps/api/v2/src/modules/booking-seat/booking-seat.repository.ts @@ -111,6 +111,7 @@ export class BookingSeatRepository { userId: true, }, }, + references: true, }, }, }, diff --git a/apps/web/modules/videos/views/videos-single-view.tsx b/apps/web/modules/videos/views/videos-single-view.tsx index 5197393359c25c..e99f5865fcf68e 100644 --- a/apps/web/modules/videos/views/videos-single-view.tsx +++ b/apps/web/modules/videos/views/videos-single-view.tsx @@ -22,6 +22,7 @@ import type { DailyCall } from "@daily-co/daily-js"; import DailyIframe from "@daily-co/daily-js"; import { DailyProvider, useDailyEvent } from "@daily-co/daily-react"; import type { getServerSideProps } from "@lib/video/[uid]/getServerSideProps"; +import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useRef, useState } from "react"; import { CalVideoPremiumFeatures } from "../cal-video-premium-features"; @@ -57,7 +58,10 @@ export default function JoinCall(props: PageProps) { !!userNameForCall && (requireEmailForGuests ? !!loggedInUserName && isLoggedInUserPartOfMeeting : true); const [isCallFrameReady, setIsCallFrameReady] = useState(false); - const activeMeetingPassword = guestCredentials?.meetingPassword ?? meetingPassword; + const searchParams = useSearchParams(); + const tokenParam = searchParams?.get("token") || undefined; + + const activeMeetingPassword = tokenParam ?? guestCredentials?.meetingPassword ?? meetingPassword; const activeMeetingUrl = guestCredentials?.meetingUrl ?? meetingUrl; const activeUserName = guestCredentials?.userName ?? userNameForCall; diff --git a/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts b/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts index ac9e81705ca676..061d37bcc3d06e 100644 --- a/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts +++ b/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts @@ -288,6 +288,16 @@ class BaseBookingOutput_2024_08_13 { @IsOptional() @Expose() icsUid?: string; + + @ApiPropertyOptional({ + type: String, + example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + description: "Daily.co host token to join with administrative privileges.", + }) + @IsString() + @IsOptional() + @Expose() + hostToken?: string; } export class BookingOutput_2024_08_13 extends BaseBookingOutput_2024_08_13 {