1- import crypto from ' crypto' ;
1+ import crypto from " crypto" ;
22import { RequestHandler } from "express" ;
3- import { eventInfo , fetchEvent , filterInPlace , replaceInPlace } from '../data/eventData' ;
4-
5- interface ChangesEntry {
6- field : string ;
7- value : {
8- event_id : string ;
9- item : string ;
10- verb : string ;
11- }
12- }
3+ import { eventInfo , eventInfoMutex , fetchEvent } from "../data/eventData" ;
4+ import { filterInPlace , replaceInPlace } from "../util" ;
135
14- interface FacebookWebhookNotificationEntry {
15- id : string ;
16- changes : ChangesEntry [ ] ;
17- }
18-
19- interface FacebookWebhookNotification {
20- entry : FacebookWebhookNotificationEntry [ ] ;
6+ interface FacebookWebhookPayload {
217 object : string ;
8+ entry : Array < {
9+ id : string ;
10+ changes : Array < {
11+ field : string ;
12+ value : {
13+ event_id : string ;
14+ item : string ;
15+ verb : string ;
16+ } ;
17+ } > ;
18+ } > ;
2219}
2320
24- const verifySignature = ( rawBody : Buffer , signatureHeader ?: string ) : boolean => {
21+ const verifySignature = (
22+ rawBody : Buffer ,
23+ signatureHeader ?: string
24+ ) : boolean => {
2525 if ( ! signatureHeader ) return false ;
26- const [ algo , signature ] = signatureHeader . split ( '=' ) ;
27- if ( algo !== ' sha256' ) return false ;
26+ const [ algo , signature ] = signatureHeader . split ( "=" ) ;
27+ if ( algo !== " sha256" ) return false ;
2828
2929 const expected = crypto
30- . createHmac ( ' sha256' , process . env . FB_APP_SECRET as string )
30+ . createHmac ( " sha256" , process . env . FB_APP_SECRET as string )
3131 . update ( rawBody )
32- . digest ( ' hex' ) ;
32+ . digest ( " hex" ) ;
3333
3434 return crypto . timingSafeEqual ( Buffer . from ( signature ) , Buffer . from ( expected ) ) ;
35- }
35+ } ;
3636
3737export const EventsWebhookVerifier : RequestHandler = ( req , res ) => {
3838 const mode = req . query [ "hub.mode" ] ;
@@ -73,13 +73,22 @@ https://developers.facebook.com/docs/graph-api/webhooks/reference/page/#feed --
7373*/
7474
7575export const EventsWebhookUpdate : RequestHandler = async ( req , res ) => {
76- const signature = req . headers [ 'x-hub-signature-256' ] ;
77- if ( ! req . rawBody || typeof signature !== "string" || ! verifySignature ( req . rawBody , signature ) ) {
76+ const signature = req . headers [ "x-hub-signature-256" ] ;
77+ if (
78+ ! req . rawBody ||
79+ typeof signature !== "string" ||
80+ ! verifySignature ( req . rawBody , signature )
81+ ) {
7882 return res . sendStatus ( 401 ) ;
7983 }
8084
81- const notif : FacebookWebhookNotification = req . body ;
82- if ( ! notif || ! notif . entry || notif . object !== "page" || notif . entry . length === 0 ) {
85+ const notif : FacebookWebhookPayload = req . body ;
86+ if (
87+ ! notif ||
88+ ! notif . entry ||
89+ notif . object !== "page" ||
90+ notif . entry . length === 0
91+ ) {
8392 return res . sendStatus ( 400 ) ;
8493 }
8594
@@ -89,19 +98,40 @@ export const EventsWebhookUpdate: RequestHandler = async (req, res) => {
8998 for ( const change of entry . changes ) {
9099 if ( change . field !== "feed" || change . value . item !== "event" ) continue ;
91100
92- if ( change . value . verb === "delete" ) {
93- // we need filter *in place* because all imports are immutable (the REAL const)
94- filterInPlace ( eventInfo , ( val , index , arr ) => val . id !== change . value . event_id ) ;
95- } else {
96- try {
101+ try {
102+ if ( change . value . verb === "delete" ) {
103+ await eventInfoMutex . runExclusive ( ( ) =>
104+ filterInPlace ( eventInfo , ( val ) => val . id !== change . value . event_id )
105+ ) ;
106+ console . log ( `Deleted event: ${ change . value . event_id } ` ) ;
107+ } else if ( change . value . verb === "edit" ) {
108+ const newEvent = await fetchEvent ( change . value . event_id ) ;
109+
110+ eventInfoMutex . runExclusive ( ( ) =>
111+ replaceInPlace (
112+ eventInfo ,
113+ ( val ) => val . id === change . value . event_id ,
114+ newEvent
115+ )
116+ ) ;
117+ console . log ( `Edited event: ${ change . value . event_id } ` ) ;
118+ } else if ( change . value . verb === "add" ) {
97119 const newEvent = await fetchEvent ( change . value . event_id ) ;
98- replaceInPlace ( eventInfo , ( val , index , arr ) => val . id === change . value . event_id , newEvent ) ;
99- } catch ( err ) {
100- console . log ( `Wasn't able to update event for some reason: ${ err } ` ) ;
120+ await eventInfoMutex . runExclusive ( ( ) => eventInfo . push ( newEvent ) ) ;
121+ console . log ( `Added event: ${ change . value . event_id } ` ) ;
122+ } else {
123+ console . warn (
124+ `Unknown verb "${ change . value . verb } " for event ${ change . value . event_id } `
125+ ) ;
101126 }
127+ } catch ( err ) {
128+ console . error (
129+ `Error processing event: ${ change . value . event_id } :\n${ err } `
130+ ) ;
131+ return res . sendStatus ( 500 ) ;
102132 }
103133 }
104134 }
105135
106136 res . sendStatus ( 200 ) ;
107- }
137+ } ;
0 commit comments