@@ -3,6 +3,7 @@ const AV = require('leanengine')
33const { Router } = require ( 'express' )
44const { check, query } = require ( 'express-validator' )
55const Redis = require ( 'ioredis' )
6+ const crypto = require ( 'crypto' )
67
78const { captureException } = require ( '../errorHandler' )
89const { checkPermission } = require ( '../../oauth/lc' )
@@ -127,6 +128,7 @@ router.post(
127128 }
128129 // === Rate Limiting End ===
129130
131+ // === Duplicate Check Start ===
130132 const {
131133 title,
132134 category_id,
@@ -138,6 +140,28 @@ router.post(
138140 } = req . body
139141 const author = req . user
140142
143+ if ( redisClient ) {
144+ try {
145+ const titleHash = crypto . createHash ( 'sha1' ) . update ( title ) . digest ( 'hex' )
146+ const duplicateCheckKey = `duplicate_check:ticket:${ author . id } :${ titleHash } `
147+ const existingTicketId = await redisClient . get ( duplicateCheckKey )
148+
149+ if ( existingTicketId ) {
150+ console . log (
151+ `Duplicate ticket creation detected for user ${ author . id } with title hash ${ titleHash } . Returning existing ticket ID: ${ existingTicketId } `
152+ )
153+ return res . json ( { id : existingTicketId } )
154+ }
155+ } catch ( error ) {
156+ console . error ( `Redis duplicate check failed for user ${ author . id } :` , error )
157+ captureException ( error , {
158+ extra : { component : 'TicketAPI' , msg : 'Duplicate check failed' , userId : author . id } ,
159+ } )
160+ // Fail open: If Redis fails, allow creation.
161+ }
162+ }
163+ // === Duplicate Check End ===
164+
141165 const ticket = await Ticket . create ( {
142166 title,
143167 category_id,
@@ -151,6 +175,23 @@ router.post(
151175 await ticket . saveFormValues ( form_values )
152176 }
153177
178+ // === Store in Redis After Creation ===
179+ if ( redisClient ) {
180+ try {
181+ const titleHash = crypto . createHash ( 'sha1' ) . update ( title ) . digest ( 'hex' )
182+ const duplicateCheckKey = `duplicate_check:ticket:${ author . id } :${ titleHash } `
183+ // Set the key with the new ticket ID and 2-minute expiry
184+ await redisClient . set ( duplicateCheckKey , ticket . id , 'EX' , 120 )
185+ } catch ( error ) {
186+ console . error ( `Redis set after creation failed for ticket ${ ticket . id } :` , error )
187+ captureException ( error , {
188+ extra : { component : 'TicketAPI' , msg : 'Set duplicate key failed' , ticketId : ticket . id } ,
189+ } )
190+ // Log error but don't block response
191+ }
192+ }
193+ // === Store in Redis End ===
194+
154195 res . json ( { id : ticket . id } )
155196 } )
156197)
0 commit comments