Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit36b5791

Browse files
author
Yankai Zhu
committed
polished code
1 parent93b261c commit36b5791

File tree

6 files changed

+173
-94
lines changed

6 files changed

+173
-94
lines changed

‎backend/package-lock.json‎

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎backend/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@types/cors":"^2.8.17",
1010
"@types/express":"^4.17.21",
1111
"@types/node":"^20.12.13",
12+
"async-mutex":"^0.5.0",
1213
"cors":"^2.8.5",
1314
"dotenv":"^16.4.5",
1415
"express":"^4.19.2",
Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
1-
importcryptofrom'crypto';
1+
importcryptofrom"crypto";
22
import{RequestHandler}from"express";
3-
import{eventInfo,fetchEvent,filterInPlace,replaceInPlace}from'../data/eventData';
4-
5-
interfaceChangesEntry{
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-
interfaceFacebookWebhookNotificationEntry{
15-
id:string;
16-
changes:ChangesEntry[];
17-
}
18-
19-
interfaceFacebookWebhookNotification{
20-
entry:FacebookWebhookNotificationEntry[];
6+
interfaceFacebookWebhookPayload{
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-
constverifySignature=(rawBody:Buffer,signatureHeader?:string):boolean=>{
21+
constverifySignature=(
22+
rawBody:Buffer,
23+
signatureHeader?:string
24+
):boolean=>{
2525
if(!signatureHeader)returnfalse;
26-
const[algo,signature]=signatureHeader.split('=');
27-
if(algo!=='sha256')returnfalse;
26+
const[algo,signature]=signatureHeader.split("=");
27+
if(algo!=="sha256")returnfalse;
2828

2929
constexpected=crypto
30-
.createHmac('sha256',process.env.FB_APP_SECRETasstring)
30+
.createHmac("sha256",process.env.FB_APP_SECRETasstring)
3131
.update(rawBody)
32-
.digest('hex');
32+
.digest("hex");
3333

3434
returncrypto.timingSafeEqual(Buffer.from(signature),Buffer.from(expected));
35-
}
35+
};
3636

3737
exportconstEventsWebhookVerifier:RequestHandler=(req,res)=>{
3838
constmode=req.query["hub.mode"];
@@ -73,13 +73,22 @@ https://developers.facebook.com/docs/graph-api/webhooks/reference/page/#feed --
7373
*/
7474

7575
exportconstEventsWebhookUpdate:RequestHandler=async(req,res)=>{
76-
constsignature=req.headers['x-hub-signature-256'];
77-
if(!req.rawBody||typeofsignature!=="string"||!verifySignature(req.rawBody,signature)){
76+
constsignature=req.headers["x-hub-signature-256"];
77+
if(
78+
!req.rawBody||
79+
typeofsignature!=="string"||
80+
!verifySignature(req.rawBody,signature)
81+
){
7882
returnres.sendStatus(401);
7983
}
8084

81-
constnotif:FacebookWebhookNotification=req.body;
82-
if(!notif||!notif.entry||notif.object!=="page"||notif.entry.length===0){
85+
constnotif:FacebookWebhookPayload=req.body;
86+
if(
87+
!notif||
88+
!notif.entry||
89+
notif.object!=="page"||
90+
notif.entry.length===0
91+
){
8392
returnres.sendStatus(400);
8493
}
8594

@@ -89,19 +98,40 @@ export const EventsWebhookUpdate: RequestHandler = async (req, res) => {
8998
for(constchangeofentry.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+
awaiteventInfoMutex.runExclusive(()=>
104+
filterInPlace(eventInfo,(val)=>val.id!==change.value.event_id)
105+
);
106+
console.log(`Deleted event:${change.value.event_id}`);
107+
}elseif(change.value.verb==="edit"){
108+
constnewEvent=awaitfetchEvent(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+
}elseif(change.value.verb==="add"){
97119
constnewEvent=awaitfetchEvent(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+
awaiteventInfoMutex.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+
returnres.sendStatus(500);
102132
}
103133
}
104134
}
105135

106136
res.sendStatus(200);
107-
}
137+
};

‎backend/src/data/eventData.ts‎

Lines changed: 31 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import{Mutex}from"async-mutex";
2+
import{inspect}from"util";
3+
import{FacebookError,Result,ResultType}from"../util";
4+
15
classEventInfo{
26
// god forbid a class have public members
37
publicid:string;
@@ -28,39 +32,6 @@ class EventInfo {
2832
}
2933
}
3034

31-
// We are altering the array in place, pray we do not alter it from another thread
32-
// I don't even know if concurrent modification exception is a thing in JS
33-
// Maybe this is a single threaded moment :icant:
34-
exportfunctionfilterInPlace<T>(
35-
arr:T[],
36-
predicate:(value:T,index:number,array:T[])=>boolean
37-
):T[]{
38-
letwrite=0;
39-
for(letread=0;read<arr.length;read++){
40-
constval=arr[read];
41-
if(predicate(val,read,arr)){
42-
arr[write++]=val;
43-
}
44-
}
45-
arr.length=write;
46-
returnarr;
47-
}
48-
49-
// This one is definitely not thread safe lmao
50-
// TODO fix with a mutex probably
51-
exportfunctionreplaceInPlace<T>(
52-
arr:T[],
53-
predicate:(value:T,index:number,array:T[])=>boolean,
54-
replacement:T
55-
):number{
56-
constidx=arr.findIndex(predicate);
57-
if(idx!==-1)arr[idx]=replacement;
58-
returnidx;
59-
}
60-
61-
// we LOVE global variables
62-
exportleteventInfo:EventInfo[]=[];
63-
6435
interfaceFacebookEvent{
6536
id:string;
6637
name:string;
@@ -76,51 +47,60 @@ interface FacebookEventsResponse {
7647

7748
// this isn't in .env for different module compatiblity
7849
constFB_API_VERSION="v23.0";
50+
constDEFAULT_EVENT_LOCATION="Everything everywhere all at once!!!";
51+
constDEFAULT_EVENT_IMAGE="/images/events/default_event.jpg";
52+
53+
// we LOVE global variables
54+
exportconsteventInfoMutex=newMutex();
55+
exportconsteventInfo:EventInfo[]=[];
7956

8057
exportasyncfunctionfetchEvents(){
8158
constresponse=awaitfetch(
8259
`https://graph.facebook.com/${FB_API_VERSION}/${process.env.FB_EVENT_PAGE_ID}/events?access_token=${process.env.FB_ACCESS_TOKEN}&fields=id,name,cover,place,start_time,end_time`
8360
);
8461

85-
constres:FacebookEventsResponse=awaitresponse.json();
86-
87-
if(!res||!res.data){
88-
console.log("No events found...");
89-
return;
62+
constres:Result<FacebookEventsResponse,FacebookError>=awaitresponse.json();
63+
if(!res||res.type===ResultType.Err){
64+
console.log(`No events found...\n${res}`);
65+
return[];
9066
}
9167

92-
constprocessed=res.data.map(
68+
constprocessed=res.value.data.map(
9369
(e)=>
9470
newEventInfo(
9571
e.id,
9672
e.name,
9773
e.start_time,
9874
e.end_time,
99-
e.place?.name??"Everything everywhere all at once!!!",
100-
e.cover?.source||"/images/events/default_event.jpg"
75+
e.place?.name??DEFAULT_EVENT_LOCATION,
76+
e.cover?.source??DEFAULT_EVENT_IMAGE
10177
)
10278
);
10379

104-
eventInfo=processed;
80+
returnprocessed;
10581
}
10682

10783
exportasyncfunctionfetchEvent(id:string){
10884
constresponse=awaitfetch(
10985
`https://graph.facebook.com/${FB_API_VERSION}/${id}?access_token=${process.env.FB_ACCESS_TOKEN}&fields=id,name,cover,place,start_time,end_time`
11086
);
11187

112-
constres:FacebookEvent=awaitresponse.json();
88+
constres:Result<FacebookEvent,FacebookError>=awaitresponse.json();
11389

114-
if(!res){
115-
thrownewError(`Couldn't get details for event${id}`);
90+
if(!res||res.type===ResultType.Err){
91+
thrownewError(
92+
`Couldn't fetch details for event${id}\n${inspect(
93+
Object.getOwnPropertyDescriptor(res,"error")?.value
94+
)}`
95+
);
11696
}
11797

11898
returnnewEventInfo(
119-
res.id,
120-
res.name,
121-
res.start_time,
122-
res.end_time,
123-
res.place?.name??"Everything everywhere all at once!!!",
124-
res.cover?.source||"/images/events/default_event.jpg"
99+
res.value.id,
100+
res.value.name,
101+
res.value.start_time,
102+
res.value.end_time,
103+
res.value.place?.name??DEFAULT_EVENT_LOCATION,
104+
res.value.cover?.source??DEFAULT_EVENT_IMAGE
125105
);
126106
}

‎backend/src/index.ts‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,24 @@ import dotenv from "dotenv";
44
importpingRoutefrom"./routes/ping";
55
importeventsRoutefrom"./routes/events";
66
importeventsWebhookRoutefrom"./routes/eventsWebhook";
7-
import{fetchEvents}from"./data/eventData";
7+
import{eventInfo,eventInfoMutex,fetchEvents}from"./data/eventData";
88

99
dotenv.config();
1010

1111
(async()=>{
1212
try{
13-
awaitfetchEvents();
13+
constevents=awaitfetchEvents();
14+
eventInfoMutex.runExclusive(()=>eventInfo.concat(events));
1415
console.log("Events fetched successfully");
1516
}catch(error){
1617
// do we ungracefully bail out here???
17-
console.error("Error fetching events:",error);
18+
// could just load from a backup file instead
19+
console.error("Error fetching events on startup:",error);
1820
}
1921

2022
constapp:Express=express();
2123
constport=process.env.PORT||9000;
22-
24+
2325
// Middleware
2426
app.use(
2527
express.json({
@@ -29,11 +31,11 @@ dotenv.config();
2931
})
3032
);
3133
app.use(cors());
32-
34+
3335
app.use(pingRoute);
3436
app.use(eventsWebhookRoute);
3537
app.use(eventsRoute);
36-
38+
3739
app.listen(port,()=>{
3840
console.log(`Server successfully started on port${port}`);
3941
});

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp