feat: implement guest invite confirmation#24684
feat: implement guest invite confirmation#24684husniabad wants to merge 8 commits intocalcom:mainfrom
Conversation
|
@husniabad is attempting to deploy a commit to the cal Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
7 issues found across 14 files
Prompt for AI agents (all 7 issues)
Understand the root cause of the following 7 issues and fix them.
<file name="apps/web/pages/guest-verification/error.tsx">
<violation number="1" location="apps/web/pages/guest-verification/error.tsx:10">
Please wrap these user-facing strings with the t() translation helper instead of hardcoding English text so the new guest verification error view remains localized.</violation>
</file>
<file name="packages/prisma/migrations/20251025001217_implement_guest_confirmation_flow/migration.sql">
<violation number="1" location="packages/prisma/migrations/20251025001217_implement_guest_confirmation_flow/migration.sql:24">
The non-unique index on token is redundant because the preceding unique index already provides the same B-tree structure; this wastes storage and slows writes without improving reads.</violation>
</file>
<file name="apps/web/pages/guest-verification/success.tsx">
<violation number="1" location="apps/web/pages/guest-verification/success.tsx:8">
Please wrap the success-page copy in the localization helper instead of hardcoding strings so translations can be applied consistently across the UI.</violation>
</file>
<file name="packages/features/bookings/lib/handlePendingGuests.ts">
<violation number="1" location="packages/features/bookings/lib/handlePendingGuests.ts:24">
Recreating a pending guest unconditionally will throw once a pending record already exists because PendingGuest has a unique (email, bookingId) constraint. Handle the existing record (e.g., update or upsert it) instead of always calling create.</violation>
</file>
<file name="apps/web/pages/api/guest-verification/confirm.ts">
<violation number="1" location="apps/web/pages/api/guest-verification/confirm.ts:35">
Switch this Prisma lookup to use `select` instead of `include`; we only need the booking metadata (status/userId/bookingId), but the current include pulls every attendee record unnecessarily, increasing query cost and widening data exposure.</violation>
</file>
<file name="packages/prisma/schema.prisma">
<violation number="1" location="packages/prisma/schema.prisma:2754">
Rule violated: **Prevent Direct NOW() Usage in Database Queries**
Replace now() with a timezone-aware default to comply with the Prevent Direct NOW() Usage in Database Queries rule.</violation>
</file>
<file name="packages/features/bookings/lib/service/RegularBookingService.ts">
<violation number="1" location="packages/features/bookings/lib/service/RegularBookingService.ts:2607">
Rule violated: **Avoid Logging Sensitive Information**
Please avoid logging guest email addresses; logging the guestsToVerify array exposes PII and violates the Avoid Logging Sensitive Information policy.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| const getErrorMessage = () => { | ||
| switch (reason) { | ||
| case "expired": | ||
| return "The verification link has expired. Please contact the meeting organizer."; |
There was a problem hiding this comment.
Please wrap these user-facing strings with the t() translation helper instead of hardcoding English text so the new guest verification error view remains localized.
Prompt for AI agents
Address the following comment on apps/web/pages/guest-verification/error.tsx at line 10:
<comment>Please wrap these user-facing strings with the t() translation helper instead of hardcoding English text so the new guest verification error view remains localized.</comment>
<file context>
@@ -0,0 +1,34 @@
+ const getErrorMessage = () => {
+ switch (reason) {
+ case "expired":
+ return "The verification link has expired. Please contact the meeting organizer.";
+ case "invalid":
+ return "The verification link is invalid. Please check your email for the correct link.";
</file context>
| CREATE INDEX "PendingGuest_bookingId_idx" ON "public"."PendingGuest"("bookingId"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "PendingGuest_token_idx" ON "public"."PendingGuest"("token"); |
There was a problem hiding this comment.
The non-unique index on token is redundant because the preceding unique index already provides the same B-tree structure; this wastes storage and slows writes without improving reads.
Prompt for AI agents
Address the following comment on packages/prisma/migrations/20251025001217_implement_guest_confirmation_flow/migration.sql at line 24:
<comment>The non-unique index on token is redundant because the preceding unique index already provides the same B-tree structure; this wastes storage and slows writes without improving reads.</comment>
<file context>
@@ -0,0 +1,33 @@
+CREATE INDEX "PendingGuest_bookingId_idx" ON "public"."PendingGuest"("bookingId");
+
+-- CreateIndex
+CREATE INDEX "PendingGuest_token_idx" ON "public"."PendingGuest"("token");
+
+-- CreateIndex
</file context>
✅ Addressed in c22181a
| <svg className="mx-auto mb-4 h-16 w-16 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | ||
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||
| </svg> | ||
| <h1 className="mb-2 text-2xl font-bold text-gray-900">Email Verified!</h1> |
There was a problem hiding this comment.
Please wrap the success-page copy in the localization helper instead of hardcoding strings so translations can be applied consistently across the UI.
Prompt for AI agents
Address the following comment on apps/web/pages/guest-verification/success.tsx at line 8:
<comment>Please wrap the success-page copy in the localization helper instead of hardcoding strings so translations can be applied consistently across the UI.</comment>
<file context>
@@ -0,0 +1,15 @@
+ <svg className="mx-auto mb-4 h-16 w-16 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
+ </svg>
+ <h1 className="mb-2 text-2xl font-bold text-gray-900">Email Verified!</h1>
+ <p className="text-gray-600">
+ You&apos;ve been successfully added to the meeting. Check your email for the meeting details.
</file context>
✅ Addressed in c22181a
| const token = generateToken(); | ||
| const expiresAt = dayjs().add(48, "hours").toDate(); | ||
|
|
||
| await prisma.pendingGuest.create({ |
There was a problem hiding this comment.
Recreating a pending guest unconditionally will throw once a pending record already exists because PendingGuest has a unique (email, bookingId) constraint. Handle the existing record (e.g., update or upsert it) instead of always calling create.
Prompt for AI agents
Address the following comment on packages/features/bookings/lib/handlePendingGuests.ts at line 24:
<comment>Recreating a pending guest unconditionally will throw once a pending record already exists because PendingGuest has a unique (email, bookingId) constraint. Handle the existing record (e.g., update or upsert it) instead of always calling create.</comment>
<file context>
@@ -0,0 +1,51 @@
+ const token = generateToken();
+ const expiresAt = dayjs().add(48, "hours").toDate();
+
+ await prisma.pendingGuest.create({
+ data: {
+ email: guestEmail,
</file context>
✅ Addressed in c22181a
| expiresAt: { gte: new Date() }, | ||
| }, | ||
| include: { | ||
| booking: { |
There was a problem hiding this comment.
Switch this Prisma lookup to use select instead of include; we only need the booking metadata (status/userId/bookingId), but the current include pulls every attendee record unnecessarily, increasing query cost and widening data exposure.
Prompt for AI agents
Address the following comment on apps/web/pages/api/guest-verification/confirm.ts at line 35:
<comment>Switch this Prisma lookup to use `select` instead of `include`; we only need the booking metadata (status/userId/bookingId), but the current include pulls every attendee record unnecessarily, increasing query cost and widening data exposure.</comment>
<file context>
@@ -0,0 +1,163 @@
+ expiresAt: { gte: new Date() },
+ },
+ include: {
+ booking: {
+ include: {
+ attendees: true,
</file context>
✅ Addressed in c22181a
| token String @unique | ||
| verified Boolean @default(false) | ||
| expiresAt DateTime | ||
| createdAt DateTime @default(now()) |
There was a problem hiding this comment.
Rule violated: Prevent Direct NOW() Usage in Database Queries
Replace now() with a timezone-aware default to comply with the Prevent Direct NOW() Usage in Database Queries rule.
Prompt for AI agents
Address the following comment on packages/prisma/schema.prisma at line 2754:
<comment>Replace now() with a timezone-aware default to comply with the Prevent Direct NOW() Usage in Database Queries rule.</comment>
<file context>
@@ -2741,3 +2742,20 @@ model CalendarCacheEvent {
+ token String @unique
+ verified Boolean @default(false)
+ expiresAt DateTime
+ createdAt DateTime @default(now())
+
+ @@unique([email, bookingId])
</file context>
| createdAt DateTime @default(now()) | |
| createdAt DateTime @default(dbgenerated("now() AT TIME ZONE 'UTC'")) |
| }, | ||
| bookerUrl, | ||
| }); | ||
| log.info("Sent verification emails to pending guests", guestsToVerify); |
There was a problem hiding this comment.
Rule violated: Avoid Logging Sensitive Information
Please avoid logging guest email addresses; logging the guestsToVerify array exposes PII and violates the Avoid Logging Sensitive Information policy.
Prompt for AI agents
Address the following comment on packages/features/bookings/lib/service/RegularBookingService.ts at line 2607:
<comment>Please avoid logging guest email addresses; logging the guestsToVerify array exposes PII and violates the Avoid Logging Sensitive Information policy.</comment>
<file context>
@@ -2588,6 +2590,25 @@ async function handler(
+ },
+ bookerUrl,
+ });
+ log.info("Sent verification emails to pending guests", guestsToVerify);
+ } catch (error) {
+ log.error("Error sending verification emails to pending guests", error);
</file context>
| log.info("Sent verification emails to pending guests", guestsToVerify); | |
| log.info("Sent verification emails to pending guests", { count: guestsToVerify.length }); |
✅ Addressed in c22181a
|
@husniabad This issue is assigned to somebody. Have you got confirmation from them before working on it? |
|
This PR has been marked as stale due to inactivity. If you're still working on it or need any help, please let us know or update the PR to keep it active. |
Devin AI is resolving merge conflictsThis PR has merge conflicts with the Devin will:
If you prefer to resolve conflicts manually, you can close the Devin session and handle it yourself. |
Devin AI is resolving merge conflictsThis PR has merge conflicts with the Devin will:
If you prefer to resolve conflicts manually, you can close the Devin session and handle it yourself. |
Merge Conflict Resolution - Push Authentication FailedI resolved all merge conflicts between Conflicts Resolved
Additional Type Fixes
Validation
Pre-existing Type Error (not from merge)
Authentication ErrorThe PR author will need to pull these changes or resolve the conflicts manually. |
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… handling duplicate attendee creation atomically
…ing booking dates with time zone support
…nclude specific fields
What does this PR do?
This PR implements a complete guest email verification flow for the impersonation prevention feature introduced in PR #24298. Previously, when a user with
requiresBookerEmailVerificationenabled was added as a guest to a booking, they were silently filtered out with no notification. This PR changes that behavior by:PendingGuestdatabase model to track guests awaiting verificationrequiresBookerEmailVerification=trueis silently filtered out when added to bookingVisual Demo (For contributors especially)
A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).
Video Demo (if applicable):
Image Demo (if applicable):
Create booking and add guest:

Created booking without the guest:

Receive guest invite request:

Confirm the invitation:

Receive guest attending email notification:


Booking updated:

Link expired or invalid token:

Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
Happy Path - Email Verification:
Error Cases:
Cleanup Job:
curl http://localhost:3000/api/cron/cleanup-pending-guestsChecklist
Summary by cubic
Implements a guest email confirmation flow (CAL-6586). Guests verify via a link before being added; calendars and notifications update after confirmation, with safe locale fallback and time zone–aware dates.
New Features
Migration
Written for commit 7708723. Summary will update on new commits.