@@ -19,9 +19,12 @@ import { headers } from "next/headers"
1919import { getStripe } from "@/lib/stripe"
2020import { getUser } from "@/data/user" ;
2121import { Session } from "next-auth" ;
22- import { STRIPE_PRODUCT_ID , CONFIG_MAX_REPOS_NO_TOKEN } from "@/lib/environment" ;
22+ import { STRIPE_PRODUCT_ID , CONFIG_MAX_REPOS_NO_TOKEN , EMAIL_FROM , SMTP_CONNECTION_URL } from "@/lib/environment" ;
2323import Stripe from "stripe" ;
2424import { OnboardingSteps } from "./lib/constants" ;
25+ import { render } from "@react-email/components" ;
26+ import InviteUserEmail from "./emails/inviteUserEmail" ;
27+ import { createTransport } from "nodemailer" ;
2528
2629const ajv = new Ajv ( {
2730 validateFormats : false ,
@@ -553,18 +556,71 @@ export const createInvites = async (emails: string[], domain: string): Promise<{
553556 } satisfies ServiceError ;
554557 }
555558
556- await prisma . $transaction ( async ( tx ) => {
557- for ( const email of emails ) {
558- await tx . invite . create ( {
559- data : {
560- recipientEmail : email ,
561- hostUserId : session . user . id ,
562- orgId,
559+ await prisma . invite . createMany ( {
560+ data : emails . map ( ( email ) => ( {
561+ recipientEmail : email ,
562+ hostUserId : session . user . id ,
563+ orgId,
564+ } ) ) ,
565+ skipDuplicates : true ,
566+ } ) ;
567+
568+ // Send invites to recipients
569+ if ( SMTP_CONNECTION_URL && EMAIL_FROM ) {
570+ const origin = ( await headers ( ) ) . get ( 'origin' ) ! ;
571+ await Promise . all ( emails . map ( async ( email ) => {
572+ const invite = await prisma . invite . findUnique ( {
573+ where : {
574+ recipientEmail_orgId : {
575+ recipientEmail : email ,
576+ orgId,
577+ } ,
578+ } ,
579+ include : {
580+ org : true ,
563581 }
564582 } ) ;
565- }
566- } ) ;
567583
584+ if ( ! invite ) {
585+ return ;
586+ }
587+
588+ const recipient = await prisma . user . findUnique ( {
589+ where : {
590+ email,
591+ } ,
592+ } ) ;
593+ const inviteLink = `${ origin } /redeem?invite_id=${ invite . id } ` ;
594+ const transport = createTransport ( SMTP_CONNECTION_URL ) ;
595+ const html = await render ( InviteUserEmail ( {
596+ baseUrl : 'https://sourcebot.app' ,
597+ host : {
598+ name : session . user . name ?? undefined ,
599+ email : session . user . email ! ,
600+ avatarUrl : session . user . image ?? undefined ,
601+ } ,
602+ recipient : {
603+ name : recipient ?. name ?? undefined ,
604+ } ,
605+ orgName : invite . org . name ,
606+ orgImageUrl : invite . org . imageUrl ?? undefined ,
607+ inviteLink,
608+ } ) ) ;
609+
610+ const result = await transport . sendMail ( {
611+ to : email ,
612+ from : EMAIL_FROM ,
613+ subject : `Join ${ invite . org . name } on Sourcebot` ,
614+ html,
615+ text : `Join ${ invite . org . name } on Sourcebot by clicking here: ${ inviteLink } ` ,
616+ } ) ;
617+
618+ const failed = result . rejected . concat ( result . pending ) . filter ( Boolean ) ;
619+ if ( failed . length > 0 ) {
620+ console . error ( `Failed to send invite email to ${ email } : ${ failed } ` ) ;
621+ }
622+ } ) ) ;
623+ }
568624
569625 return {
570626 success : true ,
0 commit comments