@@ -35,6 +35,49 @@ app.post("/api/logout", (_req: Request, res: Response) => {
3535 res . json ( { ok : true } ) ;
3636} ) ;
3737
38+ // Event Grid webhook for per-user webhook paths.
39+ // Example: POST /api/webhook/<user-unique-path>
40+ // If WEBHOOK_ALLOWED_KEYS is set (comma-separated), the :userPath must be present there.
41+ app . post ( "/api/webhook/:userPath" , ( req : Request , res : Response ) => {
42+ const userPath = req . params . userPath ;
43+
44+ // Optional allow-list: comma separated keys in env var WEBHOOK_ALLOWED_KEYS
45+ const allowed = process . env . WEBHOOK_ALLOWED_KEYS ;
46+ if ( allowed ) {
47+ const allowedSet = new Set ( allowed . split ( "," ) . map ( s => s . trim ( ) ) . filter ( Boolean ) ) ;
48+ if ( ! allowedSet . has ( userPath ) ) {
49+ console . warn ( `Rejected webhook for unknown key: ${ userPath } ` ) ;
50+ return res . status ( 404 ) . json ( { error : "unknown webhook path" } ) ;
51+ }
52+ }
53+
54+ // Event Grid sends an array of events in the body
55+ const events = Array . isArray ( req . body ) ? req . body : [ req . body ] ;
56+
57+ // Subscription validation event
58+ const validationEvent = events . find ( ( e : any ) => e && e . eventType === 'Microsoft.EventGrid.SubscriptionValidationEvent' ) ;
59+ if ( validationEvent ) {
60+ const data = validationEvent . data || { } ;
61+ console . log ( `EventGrid subscription validation for ${ userPath } :` , data ) ;
62+ // Reply with validationResponse per Event Grid requirement
63+ return res . json ( { validationResponse : data . validationCode } ) ;
64+ }
65+
66+ // Normal events: log and ack
67+ try {
68+ console . log ( `Received ${ events . length } event(s) for webhook ${ userPath } ` ) ;
69+ // For debugging include a small sample of events
70+ console . log ( JSON . stringify ( events . map ( ( e : any ) => ( { id : e . id , eventType : e . eventType } ) ) , null , 2 ) ) ;
71+ } catch ( err ) {
72+ console . warn ( 'Failed to log events' , ( err as Error ) . message ) ;
73+ }
74+
75+ // TODO: integrate with persistence / user properties: look up which user has this key
76+ // and forward/enqueue events appropriately.
77+
78+ return res . status ( 200 ) . json ( { received : events . length } ) ;
79+ } ) ;
80+
3881// Serve static frontend if present in the final image at '../editor-dist'
3982const staticPath = path . join ( __dirname , ".." , "editor-dist" ) ;
4083const FRONTEND_INDEX = path . join ( staticPath , "index.html" ) ;
0 commit comments