1- import { getOrganizations } from '@/data/getOrganizations' ;
1+ import { OnboardingLayout } from '@/components/onboarding/OnboardingLayout' ;
2+ import { SignOut } from '@/components/sign-out' ;
23import { auth } from '@/utils/auth' ;
3- import type { Organization } from '@db ' ;
4+ import { Icons } from '@comp/ui/icons ' ;
45import { db } from '@db' ;
56import { headers } from 'next/headers' ;
67import { notFound , redirect } from 'next/navigation' ;
@@ -10,6 +11,21 @@ interface InvitePageProps {
1011 params : Promise < { code : string } > ;
1112}
1213
14+ const maskEmail = ( email : string ) => {
15+ const [ local , domain ] = email . split ( '@' ) ;
16+ if ( ! domain ) return email ;
17+ const maskedLocal =
18+ local . length <= 2 ? `${ local [ 0 ] ?? '' } ***` : `${ local [ 0 ] } ***${ local . slice ( - 1 ) } ` ;
19+
20+ const domainParts = domain . split ( '.' ) ;
21+ if ( domainParts . length === 0 ) return `${ maskedLocal } @***` ;
22+ const tld = domainParts [ domainParts . length - 1 ] ;
23+ const secondLevel = domainParts . length >= 2 ? domainParts [ domainParts . length - 2 ] : '' ;
24+ const maskedSecondLevel = secondLevel ? `${ secondLevel [ 0 ] } ***` : '***' ;
25+
26+ return `${ maskedLocal } @${ maskedSecondLevel } .${ tld } ` ;
27+ } ;
28+
1329export default async function InvitePage ( { params } : InvitePageProps ) {
1430 const { code } = await params ;
1531 const session = await auth . api . getSession ( {
@@ -21,21 +37,10 @@ export default async function InvitePage({ params }: InvitePageProps) {
2137 return redirect ( `/auth?inviteCode=${ code } ` ) ;
2238 }
2339
24- // Fetch existing organizations
25- let organizations : Organization [ ] = [ ] ;
26- try {
27- const result = await getOrganizations ( ) ;
28- organizations = result . organizations ;
29- } catch ( error ) {
30- // If user has no organizations, continue with empty array
31- console . error ( 'Failed to fetch organizations:' , error ) ;
32- }
33-
34- // Check if this invitation exists and is valid for this user
40+ // Load invite by code, then verify email after
3541 const invitation = await db . invitation . findFirst ( {
3642 where : {
3743 id : code ,
38- email : session . user . email ,
3944 status : 'pending' ,
4045 } ,
4146 include : {
@@ -48,16 +53,53 @@ export default async function InvitePage({ params }: InvitePageProps) {
4853 } ) ;
4954
5055 if ( ! invitation ) {
51- // Either invitation doesn't exist, already accepted, or not for this user
5256 notFound ( ) ;
5357 }
5458
59+ // If signed-in user email doesn't match the invited email, prompt to switch accounts
60+ if ( invitation . email !== session . user . email ) {
61+ return (
62+ < OnboardingLayout variant = "setup" currentOrganization = { null } >
63+ < div className = "flex min-h-[calc(100dvh-80px)] w-full items-center justify-center p-4" >
64+ < div className = "bg-card relative w-full max-w-[480px] rounded-sm border p-10 shadow-lg" >
65+ < div className = "flex flex-col items-center gap-6 text-center" >
66+ < Icons . Logo />
67+ < h1 className = "text-2xl font-semibold tracking-tight" > Wrong account</ h1 >
68+ < div className = "mx-auto max-w-[42ch] text-muted-foreground leading-relaxed flex flex-col gap-4" >
69+ < div className = "space-y-2 text-sm" >
70+ < p >
71+ You are signed in as
72+ < span className = "mx-1 inline-flex items-center rounded-xs border border-muted bg-muted/40 px-2 py-0.5 text-sm" >
73+ { session . user . email }
74+ </ span >
75+ </ p >
76+ < p >
77+ This invite is for
78+ < span className = "mx-1 inline-flex items-center rounded-xs border border-muted bg-muted/40 px-2 py-0.5 text-sm" >
79+ { maskEmail ( invitation . email ) }
80+ </ span >
81+ </ p >
82+ </ div >
83+ < p className = "text-base font-medium" >
84+ To accept, sign out and sign back in with the invited email.
85+ </ p >
86+ </ div >
87+ < SignOut asButton className = "w-full" />
88+ </ div >
89+ </ div >
90+ </ div >
91+ </ OnboardingLayout >
92+ ) ;
93+ }
94+
5595 return (
56- < div className = "flex flex-1 items-center justify-center p-4" >
57- < AcceptInvite
58- inviteCode = { invitation . id }
59- organizationName = { invitation . organization . name || '' }
60- />
61- </ div >
96+ < OnboardingLayout variant = "setup" currentOrganization = { null } >
97+ < div className = "flex min-h-[calc(100dvh-80px)] w-full items-center justify-center p-4" >
98+ < AcceptInvite
99+ inviteCode = { invitation . id }
100+ organizationName = { invitation . organization . name || '' }
101+ />
102+ </ div >
103+ </ OnboardingLayout >
62104 ) ;
63105}
0 commit comments