Files
calendar/apps/web/lib/video/[uid]/getServerSideProps.ts
T
Eunjae LeeGitHubClaude Opus 4.5Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
81e4241f85 refactor: apply biome formatting to apps/web (#27692)
* refactor: apply biome formatting to apps/web (batch 1)

Formats apps/web non-modules directories:
- apps/web/app
- apps/web/lib
- apps/web/pages
- apps/web/styles
- apps/web/server
- apps/web/test
- Root-level .ts and .mjs files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/modules (batch 2)

Formats smaller apps/web/modules directories:
- onboarding, data-table, users, apps, auth
- integration-attribute-sync, shell, availability
- feature-flags, team, webhooks, getting-started
- feature-opt-in, troubleshooter, schedules, notifications
- signup-view.tsx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/modules (batch 3)

Formats medium apps/web/modules directories:
- bookings
- event-types
- insights
- embed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/modules/ee (batch 4)

Formats apps/web/modules/ee directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web (batch 5)

Formats remaining apps/web directories:
- apps/web/modules/settings
- apps/web/modules/booking-audit
- apps/web/playwright

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to apps/web/components (batch 6)

Formats remaining apps/web/components files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to new apps/web files from main

Formats newly added/modified files from main merge:
- WorkflowStepContainer.tsx (resolved merge conflicts)
- Newly moved files (blocklist, form-builder, schedules, etc.)
- Other files modified in main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: apply biome formatting to new apps/web files from main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-10 23:25:24 +05:30

315 lines
9.7 KiB
TypeScript

import MarkdownIt from "markdown-it";
import type { GetServerSidePropsContext } from "next";
import {
generateGuestMeetingTokenFromOwnerMeetingToken,
setEnableRecordingUIAndUserIdForOrganizer,
updateMeetingTokenIfExpired,
} from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
import { getOrganizationRepository } from "@calcom/features/ee/organizations/di/OrganizationRepository.container";
import { EventTypeRepository } from "@calcom/features/eventtypes/repositories/eventTypeRepository";
import { getCalVideoReference } from "@calcom/features/get-cal-video-reference";
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
import { CAL_VIDEO_MEETING_LINK_FOR_TESTING } from "@calcom/lib/constants";
import { isENVDev } from "@calcom/lib/env";
import prisma from "@calcom/prisma";
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
type CalVideoSettings = {
disableRecordingForGuests: boolean;
disableRecordingForOrganizer: boolean;
enableAutomaticTranscription: boolean;
enableAutomaticRecordingForOrganizer: boolean;
disableTranscriptionForGuests: boolean;
disableTranscriptionForOrganizer: boolean;
requireEmailForGuests: boolean;
};
const shouldEnableRecordButton = ({
hasTeamPlan,
calVideoSettings,
isOrganizer,
}: {
hasTeamPlan: boolean;
calVideoSettings?: CalVideoSettings | null;
isOrganizer: boolean;
}) => {
if (!hasTeamPlan) return false;
if (!calVideoSettings) return true;
if (isOrganizer) {
return !calVideoSettings.disableRecordingForOrganizer;
}
return !calVideoSettings.disableRecordingForGuests;
};
const shouldEnableAutomaticTranscription = ({
hasTeamPlan,
calVideoSettings,
}: {
hasTeamPlan: boolean;
calVideoSettings?: CalVideoSettings | null;
}) => {
if (!hasTeamPlan) return false;
if (!calVideoSettings) return false;
return !!calVideoSettings.enableAutomaticTranscription;
};
const shouldEnableAutomaticRecording = ({
hasTeamPlan,
calVideoSettings,
isOrganizer,
}: {
hasTeamPlan: boolean;
calVideoSettings?: CalVideoSettings | null;
isOrganizer: boolean;
}) => {
if (!hasTeamPlan || !isOrganizer) return false;
if (!calVideoSettings) return false;
return !!calVideoSettings.enableAutomaticRecordingForOrganizer;
};
const shouldEnableTranscriptionButton = ({
hasTeamPlan,
calVideoSettings,
isOrganizer,
}: {
hasTeamPlan: boolean;
calVideoSettings?: CalVideoSettings | null;
isOrganizer: boolean;
}) => {
if (!hasTeamPlan) return false;
if (!calVideoSettings) return true;
if (isOrganizer) {
return !calVideoSettings.disableTranscriptionForOrganizer;
}
return !calVideoSettings.disableTranscriptionForGuests;
};
const checkIfUserIsHost = async ({
booking,
sessionUserId,
}: {
booking: {
user: { id: number } | null;
eventTypeId: number | undefined;
};
sessionUserId?: number;
}) => {
if (!sessionUserId) return false;
if (!booking.eventTypeId) {
return booking.user?.id === sessionUserId;
}
const eventTypeRepo = new EventTypeRepository(prisma);
const eventType = await eventTypeRepo.findByIdWithUserAccess({
id: booking.eventTypeId,
userId: sessionUserId,
});
// If eventType exists, it means user is either owner, host or user
return !!eventType;
};
export async function getServerSideProps(context: GetServerSidePropsContext) {
const { req } = context;
const bookingRepo = new BookingRepository(prisma);
const booking = await bookingRepo.findBookingIncludeCalVideoSettingsAndReferences({
bookingUid: context.query.uid as string,
});
// Below if block is for local testing purposes only
// STARTS------------------------------------------------------------------------------
if (booking?.references[0] && isENVDev && CAL_VIDEO_MEETING_LINK_FOR_TESTING) {
// meetingUrl is `null` in dev env, so setting a dummy meetingUrl (it's a past but real meeting link in production env)
booking.references[0].meetingUrl = CAL_VIDEO_MEETING_LINK_FOR_TESTING;
}
// ENDS--------------------------------------------------------------------------------
if (!booking || booking.references.length === 0 || !booking.references[0].meetingUrl) {
return {
redirect: {
destination: "/video/no-meeting-found",
permanent: false,
},
};
}
const hasTeamPlan = booking.user?.id
? await prisma.membership.findFirst({
where: {
userId: booking.user.id,
team: {
slug: {
not: null,
},
},
},
})
: false;
const userRepo = new UserRepository(prisma);
const profile = booking.user
? (
await userRepo.enrichUserWithItsProfile({
user: booking.user,
})
).profile
: null;
const organizationRepository = getOrganizationRepository();
const calVideoLogo = profile?.organization
? await organizationRepository.findCalVideoLogoByOrgId({ id: profile.organization.id })
: null;
//daily.co calls have a 14 days exit buffer when a user enters a call when it's not available it will trigger the modals
const now = new Date();
const exitDate = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000);
//find out if the meeting is in the past
const isPast = booking?.endTime <= exitDate;
const testingUid =
CAL_VIDEO_MEETING_LINK_FOR_TESTING && CAL_VIDEO_MEETING_LINK_FOR_TESTING?.split("/").pop();
const isTestingLink = booking?.uid === testingUid;
if (isPast && !isTestingLink) {
return {
redirect: {
destination: `/video/meeting-ended/${booking?.uid}`,
permanent: false,
},
};
}
const bookingObj = Object.assign({}, booking, {
startTime: booking.startTime.toString(),
endTime: booking.endTime.toString(),
});
const session = await getServerSession({ req });
const oldVideoReference = getCalVideoReference(bookingObj.references);
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: oldVideoReference.id,
roomName: oldVideoReference.uid,
meetingToken: oldVideoReference.meetingPassword,
exp: epochTimeFourteenDaysAfter,
});
const sessionUserId = session?.user?.impersonatedBy ? session.user.impersonatedBy.id : session?.user.id;
const sessionUserEmail = session?.user?.email;
const isOrganizer = await checkIfUserIsHost({
booking: {
eventTypeId: bookingObj.eventType?.id,
user: bookingObj.user,
},
sessionUserId,
});
const isAttendee = sessionUserEmail
? (bookingObj.attendees?.some(
(attendee) => attendee.email.toLowerCase() === sessionUserEmail.toLowerCase()
) ?? false)
: false;
// set meetingPassword for guests
if (!isOrganizer) {
const userIdForToken = sessionUserId;
const guestMeetingPassword = await generateGuestMeetingTokenFromOwnerMeetingToken({
meetingToken: videoReferencePassword,
userId: userIdForToken,
});
bookingObj.references.forEach((bookRef) => {
bookRef.meetingPassword = guestMeetingPassword;
});
}
// Only for backward compatibility and setting user id in participants for organizer
else {
const meetingPassword = await setEnableRecordingUIAndUserIdForOrganizer(
oldVideoReference.id,
videoReferencePassword,
sessionUserId
);
if (meetingPassword) {
bookingObj.references.forEach((bookRef) => {
bookRef.meetingPassword = meetingPassword;
});
}
}
const videoReference = getCalVideoReference(bookingObj.references);
const showRecordingButton = shouldEnableRecordButton({
hasTeamPlan: !!hasTeamPlan,
calVideoSettings: bookingObj.eventType?.calVideoSettings,
isOrganizer,
});
const enableAutomaticTranscription = shouldEnableAutomaticTranscription({
hasTeamPlan: !!hasTeamPlan,
calVideoSettings: bookingObj.eventType?.calVideoSettings,
});
const enableAutomaticRecordingForOrganizer = shouldEnableAutomaticRecording({
hasTeamPlan: !!hasTeamPlan,
calVideoSettings: bookingObj.eventType?.calVideoSettings,
isOrganizer,
});
const showTranscriptionButton = shouldEnableTranscriptionButton({
hasTeamPlan: !!hasTeamPlan,
calVideoSettings: bookingObj.eventType?.calVideoSettings,
isOrganizer,
});
return {
props: {
meetingUrl: videoReference.meetingUrl ?? "",
...(typeof videoReference.meetingPassword === "string" && {
meetingPassword: videoReference.meetingPassword,
}),
booking: {
...bookingObj,
...(bookingObj.description && { description: md.render(bookingObj.description) }),
user: bookingObj.user
? {
...bookingObj.user,
organization: profile?.organization,
}
: bookingObj.user,
},
hasTeamPlan: !!hasTeamPlan,
calVideoLogo,
loggedInUserName: sessionUserId ? session?.user?.name : undefined,
showRecordingButton,
enableAutomaticTranscription,
enableAutomaticRecordingForOrganizer,
showTranscriptionButton,
rediectAttendeeToOnExit: isOrganizer
? undefined
: bookingObj.eventType?.calVideoSettings?.redirectUrlOnExit,
overrideName: Array.isArray(context.query.name) ? context.query.name[0] : context.query.name,
requireEmailForGuests: bookingObj.eventType?.calVideoSettings?.requireEmailForGuests ?? false,
isLoggedInUserPartOfMeeting: isAttendee || isOrganizer,
},
};
}