feat: auto-accept team invitations for existing users#24091
feat: auto-accept team invitations for existing users#24091anikdhabal merged 28 commits intomainfrom
Conversation
- Change email button text from 'View Invitation' to 'Accept Invite' - Implement auto-accept flow when clicking email CTA - Update TeamService.inviteMemberByToken to support auto-acceptance - Add new autoAcceptInvite tRPC endpoint for handling auto-acceptance - Update invitation link generation to include autoAccept parameter - Handle both team and organization invitation scenarios - Maintain payment/billing flow integration with TeamBilling.updateQuantity - Preserve backward compatibility with existing manual flow - Update all locale files with new 'Accept Invite' button text Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
|
Warning Rate limit exceeded@anikdhabal has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 21 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughAdds propagation of an Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🧪 Early access (Sonnet 4.5): enabledWe are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience. Note:
Comment |
- Keep only English 'Accept Invite' translation - Revert all other locale files to original 'View Invitation' translations - Maintain core auto-accept invitation functionality Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
- Remove autoAccept parameter from TeamService.inviteMemberByToken - Always auto-accept invitations for existing users clicking email links - Remove autoAccept logic from teams server-page.tsx - Remove autoAccept=true from invitation URLs - Delete autoAcceptInvite handler and schema files - Remove autoAcceptInvite endpoint from tRPC router - Simplify invitation flow to match new user pattern Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
58d45f1 to
0712b27
Compare
- Change expectation from accepted: false to accepted: true - Update test description to reflect auto-accept behavior - Fix TypeScript type casting to use Pick<TeamRepository, 'deleteById'> - Aligns with new default auto-acceptance for team invitations Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
…://github.com/calcom/cal.com into devin/1758863155-auto-accept-team-invitations
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (7)
packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts (1)
29-29: Prefer named export; avoid default exports.Keeps imports consistent, improves tree‑shaking, and matches repo conventions.
As per coding guidelines
-export default acceptOrLeaveHandler; +// Prefer named exports; consumers can import { acceptOrLeaveHandler }packages/lib/server/service/teamService.test.ts (1)
229-390: Add edge‑case tests for token acceptance robustness.
- Missing membership: simulate prisma.membership.update throwing P2025 and assert we create a membership and accept it.
- Token invalidation: after a successful accept, assert the verification token is deleted/invalidated to prevent reuse.
- Expiry parity: add a case for tokens with
expiresInDays: nullto mirror inviteMemberByToken’s acceptance of non-expiring tokens.I can draft minimal Vitest cases against prismaMock to cover these.
packages/lib/server/service/teamService.ts (5)
244-253: Make parent/child acceptance atomic.Wrap the child and optional parent membership updates in a single
$transactionto avoid partial acceptance if the second update fails. Also consider handling a missing parent membership gracefully.Example approach (illustrative):
await prisma.$transaction(async (tx) => { const { team } = await tx.membership.update({ where: { userId_teamId: { userId, teamId } }, data: { accepted: true }, select: { team: { select: { id: true, parentId: true, isOrganization: true } } }, }); if (team.parentId) { // Optionally catch P2025 and no‑op or create parent membership await tx.membership.update({ where: { userId_teamId: { userId, teamId: team.parentId } }, data: { accepted: true }, }); } return team; });Also applies to: 230-241
296-298: Don’t swallow errors; use structured logging (and optionally rethrow).Replace
console.logwith the service logger and include context. Consider rethrowing a typed error for callers to handle.- } catch (e) { - console.log(e); - } + } catch (e) { + log.error("leaveTeamMembership failed", { userId, teamId, err: e }); + // Consider: throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); + }
344-350: Invalidate the verification token after successful acceptance.Prevents reuse and reduces confusion/noise in the token table.
await TeamService.acceptTeamMembership({ userId, teamId: verificationToken.teamId, userEmail: currentUser.email, username: currentUser.username, }); + await prisma.verificationToken.deleteMany({ where: { token: acceptanceToken } });
100-104: Invite link and auto‑accept — confirm whereautoAccept=trueis appended.PR summary mentions defaulting to
/teams?token=...&autoAccept=true.buildInviteLinkcurrently returns/teams?token=...without the flag. If appended elsewhere (email templating/resend path), all good; otherwise add here.- const teamInviteLink = `${WEBAPP_URL}/teams?token=${token}`; + const teamInviteLink = `${WEBAPP_URL}/teams?token=${token}&autoAccept=true`;
334-342: Username/email matching — case sensitivity check.If usernames/emails are treated case‑insensitively elsewhere, normalize both sides before comparison to avoid false negatives.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/lib/server/service/teamService.test.ts(5 hunks)packages/lib/server/service/teamService.ts(3 hunks)packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/lib/server/service/teamService.test.tspackages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.tspackages/lib/server/service/teamService.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
packages/lib/server/service/teamService.test.tspackages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.tspackages/lib/server/service/teamService.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/lib/server/service/teamService.test.tspackages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.tspackages/lib/server/service/teamService.ts
**/*Service.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Service files must include
Servicesuffix, use PascalCase matching exported class, and avoid generic names (e.g.,MembershipService.ts)
Files:
packages/lib/server/service/teamService.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-08-27T16:39:38.192Z
Learning: anglerfishlyy successfully implemented CAL-3076 email invitation feature for Cal.com team event-types in PR #23312. The feature allows inviting people via email directly from assignment flow, with automatic team invitation if email doesn't belong to existing team member. Implementation includes Host type modifications (userId?: number, email?: string, isPending?: boolean), CheckedTeamSelect component updates with CreatableSelect, TRPC schema validation with zod email validation, and integration with existing teamInvite system.
📚 Learning: 2025-08-27T16:39:38.192Z
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-08-27T16:39:38.192Z
Learning: anglerfishlyy successfully implemented CAL-3076 email invitation feature for Cal.com team event-types in PR #23312. The feature allows inviting people via email directly from assignment flow, with automatic team invitation if email doesn't belong to existing team member. Implementation includes Host type modifications (userId?: number, email?: string, isPending?: boolean), CheckedTeamSelect component updates with CreatableSelect, TRPC schema validation with zod email validation, and integration with existing teamInvite system.
Applied to files:
packages/lib/server/service/teamService.ts
📚 Learning: 2025-07-28T11:50:23.946Z
Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*.ts : For Prisma queries, only select data you need; never use `include`, always use `select`
Applied to files:
packages/lib/server/service/teamService.ts
📚 Learning: 2025-08-07T18:42:34.081Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/lib/server/repository/PrismaPhoneNumberRepository.ts:412-417
Timestamp: 2025-08-07T18:42:34.081Z
Learning: In Cal.com codebase, the coding guideline requiring explicit `select` clauses instead of `include` for Prisma queries applies to read operations but not to update operations. Update operations don't need explicit select clauses.
Applied to files:
packages/lib/server/service/teamService.ts
🧬 Code graph analysis (3)
packages/lib/server/service/teamService.test.ts (2)
packages/lib/server/service/teamService.ts (1)
TeamService(54-569)packages/lib/createAProfileForAnExistingUser.ts (1)
createAProfileForAnExistingUser(13-104)
packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts (2)
packages/lib/server/service/teamService.ts (1)
TeamService(54-569)packages/platform/libraries/index.ts (1)
TeamService(139-139)
packages/lib/server/service/teamService.ts (1)
packages/lib/createAProfileForAnExistingUser.ts (1)
createAProfileForAnExistingUser(13-104)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Tests / E2E API v2
- GitHub Check: Tests / E2E (3/4)
- GitHub Check: Tests / E2E (1/4)
🔇 Additional comments (4)
packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts (1)
15-25: Good move to centralize logic in TeamService.Cleaner responsibility boundaries and easier to evolve acceptance/leave rules in one place. Please confirm that the "leave" path here is only used to reject pending invitations; if it’s also used to leave accepted memberships, you’ll likely need the fuller cleanup flow (hosts/event types) used elsewhere.
packages/lib/server/service/teamService.ts (3)
237-239: Minimize Prisma selection for team shape.Select only fields you actually use (
id,parentId,isOrganization) rather than the full team object.As per coding guidelines
- select: { - team: true, - }, + select: { + team: { select: { id: true, parentId: true, isOrganization: true } }, + },
301-312: Align token expiry rule with inviteMemberByToken (optional).
inviteMemberByTokenaccepts tokens withexpiresInDays: nullor non‑expiredexpires. Consider mirroring that here unless acceptance tokens are guaranteed to useexpires.- expires: { gte: new Date() }, + OR: [{ expiresInDays: null }, { expires: { gte: new Date() } }],
219-271: Seat/billing quantity on acceptance — verify desired timing.You update billing on provisional invite creation. If billing should reflect accepted members only, consider updating quantity on acceptance as well; otherwise confirm that counting pending invites is intentional.
packages/trpc/server/routers/viewer/teams/resendInvitation.handler.ts
Outdated
Show resolved
Hide resolved
…://github.com/calcom/cal.com into devin/1758863155-auto-accept-team-invitations
What does this PR do?
This PR implements auto-accept functionality for team invitations sent to existing Cal.com users. When an existing user clicks the "Accept Invite" button in their invitation email, the invitation is automatically accepted and they are redirected to the
/teamspage, eliminating the need for manual acceptance.