@@ -5,7 +5,12 @@ import { createLogger } from '@/lib/logs/console/logger'
55import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
66import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
77import { generateRequestId } from '@/lib/utils'
8- import { base64UrlEncode , buildMimeMessage } from '@/tools/gmail/utils'
8+ import {
9+ base64UrlEncode ,
10+ buildMimeMessage ,
11+ buildSimpleEmailMessage ,
12+ fetchThreadingHeaders ,
13+ } from '@/tools/gmail/utils'
914
1015export const dynamic = 'force-dynamic'
1116
@@ -16,8 +21,10 @@ const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me'
1621const GmailSendSchema = z . object ( {
1722 accessToken : z . string ( ) . min ( 1 , 'Access token is required' ) ,
1823 to : z . string ( ) . min ( 1 , 'Recipient email is required' ) ,
19- subject : z . string ( ) . min ( 1 , 'Subject is required' ) ,
24+ subject : z . string ( ) . optional ( ) . nullable ( ) ,
2025 body : z . string ( ) . min ( 1 , 'Email body is required' ) ,
26+ threadId : z . string ( ) . optional ( ) . nullable ( ) ,
27+ replyToMessageId : z . string ( ) . optional ( ) . nullable ( ) ,
2128 cc : z . string ( ) . optional ( ) . nullable ( ) ,
2229 bcc : z . string ( ) . optional ( ) . nullable ( ) ,
2330 attachments : z . array ( z . any ( ) ) . optional ( ) . nullable ( ) ,
@@ -49,11 +56,19 @@ export async function POST(request: NextRequest) {
4956
5057 logger . info ( `[${ requestId } ] Sending Gmail email` , {
5158 to : validatedData . to ,
52- subject : validatedData . subject ,
59+ subject : validatedData . subject || '' ,
5360 hasAttachments : ! ! ( validatedData . attachments && validatedData . attachments . length > 0 ) ,
5461 attachmentCount : validatedData . attachments ?. length || 0 ,
5562 } )
5663
64+ const threadingHeaders = validatedData . replyToMessageId
65+ ? await fetchThreadingHeaders ( validatedData . replyToMessageId , validatedData . accessToken )
66+ : { }
67+
68+ const originalMessageId = threadingHeaders . messageId
69+ const originalReferences = threadingHeaders . references
70+ const originalSubject = threadingHeaders . subject
71+
5772 let rawMessage : string | undefined
5873
5974 if ( validatedData . attachments && validatedData . attachments . length > 0 ) {
@@ -106,8 +121,10 @@ export async function POST(request: NextRequest) {
106121 to : validatedData . to ,
107122 cc : validatedData . cc ?? undefined ,
108123 bcc : validatedData . bcc ?? undefined ,
109- subject : validatedData . subject ,
124+ subject : validatedData . subject || originalSubject || '' ,
110125 body : validatedData . body ,
126+ inReplyTo : originalMessageId ,
127+ references : originalReferences ,
111128 attachments : attachmentBuffers ,
112129 } )
113130
@@ -117,22 +134,21 @@ export async function POST(request: NextRequest) {
117134 }
118135
119136 if ( ! rawMessage ) {
120- const emailHeaders = [
121- 'Content-Type: text/plain; charset="UTF-8"' ,
122- 'MIME-Version: 1.0' ,
123- `To: ${ validatedData . to } ` ,
124- ]
125-
126- if ( validatedData . cc ) {
127- emailHeaders . push ( `Cc: ${ validatedData . cc } ` )
128- }
129- if ( validatedData . bcc ) {
130- emailHeaders . push ( `Bcc: ${ validatedData . bcc } ` )
131- }
137+ rawMessage = buildSimpleEmailMessage ( {
138+ to : validatedData . to ,
139+ cc : validatedData . cc ,
140+ bcc : validatedData . bcc ,
141+ subject : validatedData . subject || originalSubject ,
142+ body : validatedData . body ,
143+ inReplyTo : originalMessageId ,
144+ references : originalReferences ,
145+ } )
146+ }
147+
148+ const requestBody : { raw : string ; threadId ?: string } = { raw : rawMessage }
132149
133- emailHeaders . push ( `Subject: ${ validatedData . subject } ` , '' , validatedData . body )
134- const email = emailHeaders . join ( '\n' )
135- rawMessage = Buffer . from ( email ) . toString ( 'base64url' )
150+ if ( validatedData . threadId ) {
151+ requestBody . threadId = validatedData . threadId
136152 }
137153
138154 const gmailResponse = await fetch ( `${ GMAIL_API_BASE } /messages/send` , {
@@ -141,7 +157,7 @@ export async function POST(request: NextRequest) {
141157 Authorization : `Bearer ${ validatedData . accessToken } ` ,
142158 'Content-Type' : 'application/json' ,
143159 } ,
144- body : JSON . stringify ( { raw : rawMessage } ) ,
160+ body : JSON . stringify ( requestBody ) ,
145161 } )
146162
147163 if ( ! gmailResponse . ok ) {
0 commit comments