import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir"; import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { z } from "zod"; import { generateGuestMeetingTokenFromOwnerMeetingToken, updateMeetingTokenIfExpired, } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter"; import { getHostsAndGuests } from "@calcom/features/bookings/lib/getHostsAndGuests"; import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository"; import { getCalVideoReference } from "@calcom/features/get-cal-video-reference"; import { VideoCallGuestRepository } from "@calcom/features/video-call-guest/repositories/VideoCallGuestRepository"; import prisma from "@calcom/prisma"; import { validateCsrfToken } from "@calcom/web/lib/validateCsrfToken"; const videoCallGuestWithCsrfSchema = z.object({ bookingUid: z.string(), email: z.string().email(), name: z.string().min(1), csrfToken: z.string().length(64, "Invalid CSRF token"), }); async function handler(req: NextRequest) { let appDirRequestBody; try { appDirRequestBody = await req.json(); } catch { return NextResponse.json({ success: false, message: "Invalid JSON" }, { status: 400 }); } const guestData = videoCallGuestWithCsrfSchema.parse(appDirRequestBody); const csrfError = await validateCsrfToken(guestData.csrfToken); if (csrfError) { return csrfError; } const bookingRepo = new BookingRepository(prisma); const booking = await bookingRepo.findBookingIncludeCalVideoSettingsAndReferences({ bookingUid: guestData.bookingUid, }); if (!booking) { return NextResponse.json({ error: "Booking not found" }, { status: 404 }); } const { hosts, guests } = getHostsAndGuests(booking); const isHost = hosts.some((host) => host.email.toLowerCase() === guestData.email.trim().toLowerCase()); const isGuest = guests.some((guest) => guest.email.toLowerCase() === guestData.email.trim().toLowerCase()); if (isHost) { return NextResponse.json({ error: "hosts_must_use_login" }, { status: 403 }); } else if (!isGuest) { return NextResponse.json({ error: "invalid_guest_email" }, { status: 403 }); } const videoCallGuestRepo = new VideoCallGuestRepository(prisma); const guestSession = await videoCallGuestRepo.upsertVideoCallGuest({ bookingUid: guestData.bookingUid, email: guestData.email, name: guestData.name, }); const videoReference = getCalVideoReference(booking.references); if (!videoReference) { return NextResponse.json({ error: "Video reference not found" }, { status: 404 }); } const endTime = new Date(booking.endTime); const fourteenDaysAfter = new Date(endTime.getTime() + 14 * 24 * 60 * 60 * 1000); const epochTimeFourteenDaysAfter = Math.floor(fourteenDaysAfter.getTime() / 1000); const videoReferencePassword = await updateMeetingTokenIfExpired({ bookingReferenceId: videoReference.id, roomName: videoReference.uid, meetingToken: videoReference.meetingPassword, exp: epochTimeFourteenDaysAfter, }); const guestMeetingPassword = await generateGuestMeetingTokenFromOwnerMeetingToken({ meetingToken: videoReferencePassword, userId: guestSession.id, }); return NextResponse.json({ guestSessionId: guestSession.id, meetingPassword: guestMeetingPassword, meetingUrl: videoReference.meetingUrl, }); } export const POST = defaultResponderForAppDir(handler);