11import { NextRequest , NextResponse } from 'next/server'
22import { nanoid } from 'nanoid'
33import { Logger } from '@/lib/logs/console-logger'
4+ import { acquireLock , releaseLock } from '@/lib/redis'
45import { pollGmailWebhooks } from '@/lib/webhooks/gmail-polling-service'
56
67const logger = new Logger ( 'GmailPollingAPI' )
78
89export const dynamic = 'force-dynamic'
9- export const maxDuration = 300 // Allow up to 5 minutes for polling to complete
10+ export const maxDuration = 180 // Allow up to 3 minutes for polling to complete
1011
11- interface PollingTask {
12- promise : Promise < any >
13- startedAt : number
14- }
15-
16- const activePollingTasks = new Map < string , PollingTask > ( )
17- const STALE_TASK_THRESHOLD_MS = 10 * 60 * 1000 // 10 minutes
18-
19- function cleanupStaleTasks ( ) {
20- const now = Date . now ( )
21- let removedCount = 0
22-
23- for ( const [ requestId , task ] of activePollingTasks . entries ( ) ) {
24- if ( now - task . startedAt > STALE_TASK_THRESHOLD_MS ) {
25- activePollingTasks . delete ( requestId )
26- removedCount ++
27- }
28- }
29-
30- if ( removedCount > 0 ) {
31- logger . info ( `Cleaned up ${ removedCount } stale polling tasks` )
32- }
33-
34- return removedCount
35- }
12+ const LOCK_KEY = 'gmail-polling-lock'
13+ const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min)
3614
3715export async function GET ( request : NextRequest ) {
3816 const requestId = nanoid ( )
3917 logger . info ( `Gmail webhook polling triggered (${ requestId } )` )
4018
19+ let lockValue : string | undefined
20+
4121 try {
4222 const authHeader = request . headers . get ( 'authorization' )
4323 const webhookSecret = process . env . CRON_SECRET || process . env . WEBHOOK_POLLING_SECRET
@@ -51,49 +31,42 @@ export async function GET(request: NextRequest) {
5131 return new NextResponse ( 'Unauthorized' , { status : 401 } )
5232 }
5333
54- cleanupStaleTasks ( )
55-
56- const pollingTask : PollingTask = {
57- promise : null as any ,
58- startedAt : Date . now ( ) ,
34+ lockValue = requestId // unique value to identify the holder
35+ const locked = await acquireLock ( LOCK_KEY , lockValue , LOCK_TTL_SECONDS )
36+
37+ if ( ! locked ) {
38+ return NextResponse . json (
39+ {
40+ success : true ,
41+ message : 'Polling already in progress – skipped' ,
42+ requestId,
43+ status : 'skip' ,
44+ } ,
45+ { status : 202 }
46+ )
5947 }
6048
61- pollingTask . promise = pollGmailWebhooks ( )
62- . then ( ( results ) => {
63- logger . info ( `Gmail polling completed successfully (${ requestId } )` , {
64- userCount : results ?. total || 0 ,
65- successful : results ?. successful || 0 ,
66- failed : results ?. failed || 0 ,
67- } )
68- activePollingTasks . delete ( requestId )
69- return results
70- } )
71- . catch ( ( error ) => {
72- logger . error ( `Error in background Gmail polling task (${ requestId } ):` , error )
73- activePollingTasks . delete ( requestId )
74- throw error
75- } )
76-
77- activePollingTasks . set ( requestId , pollingTask )
49+ const results = await pollGmailWebhooks ( )
7850
7951 return NextResponse . json ( {
8052 success : true ,
81- message : 'Gmail webhook polling started successfully ' ,
53+ message : 'Gmail polling completed ' ,
8254 requestId,
83- status : 'polling_started ' ,
84- activeTasksCount : activePollingTasks . size ,
55+ status : 'completed ' ,
56+ ... results ,
8557 } )
8658 } catch ( error ) {
87- logger . error ( `Error initiating Gmail webhook polling (${ requestId } ):` , error )
88-
59+ logger . error ( `Error during Gmail polling (${ requestId } ):` , error )
8960 return NextResponse . json (
9061 {
9162 success : false ,
92- message : 'Failed to start Gmail webhook polling' ,
63+ message : 'Gmail polling failed ' ,
9364 error : error instanceof Error ? error . message : 'Unknown error' ,
9465 requestId,
9566 } ,
9667 { status : 500 }
9768 )
69+ } finally {
70+ await releaseLock ( LOCK_KEY ) . catch ( ( ) => { } )
9871 }
9972}
0 commit comments