@@ -28,6 +28,8 @@ import { IUpdateDiscord, updateDiscord } from "../functions/discord.js";
2828
2929const repeatOptions = [ "weekly" , "biweekly" ] as const ;
3030const EVENT_CACHE_SECONDS = 90 ;
31+ const CLIENT_HTTP_CACHE_POLICY =
32+ "public, max-age=180, stale-while-revalidate=420, stale-if-error=3600" ;
3133export type EventRepeatOptions = ( typeof repeatOptions ) [ number ] ;
3234
3335const baseSchema = z . object ( {
@@ -58,7 +60,7 @@ const postRequestSchema = requestSchema.refine(
5860export type EventPostRequest = z . infer < typeof postRequestSchema > ;
5961type EventGetRequest = {
6062 Params : { id : string } ;
61- Querystring : undefined ;
63+ Querystring : { ts ?: number } ;
6264 Body : undefined ;
6365} ;
6466
@@ -119,14 +121,15 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
119121 message : `${ userProvidedId } is not a valid event ID.` ,
120122 } ) ;
121123 }
124+ originalEvent = unmarshall ( originalEvent ) ;
122125 }
123126 const entry = {
124127 ...request . body ,
125128 id : entryUUID ,
126- createdBy : request . username ,
127- createdAt : originalEvent
128- ? originalEvent . createdAt || new Date ( ) . toISOString ( )
129- : new Date ( ) . toISOString ( ) ,
129+ createdAt :
130+ originalEvent && originalEvent . createdAt
131+ ? originalEvent . createdAt
132+ : new Date ( ) . toISOString ( ) ,
130133 updatedAt : new Date ( ) . toISOString ( ) ,
131134 } ;
132135 await fastify . dynamoClient . send (
@@ -173,6 +176,9 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
173176 }
174177 throw new DiscordEventError ( { } ) ;
175178 }
179+ fastify . nodeCache . del ( "events-upcoming_only=true" ) ;
180+ fastify . nodeCache . del ( "events-upcoming_only=false" ) ;
181+ fastify . nodeCache . del ( `event-${ entryUUID } ` ) ;
176182 reply . status ( 201 ) . send ( {
177183 id : entryUUID ,
178184 resource : `/api/v1/events/${ entryUUID } ` ,
@@ -198,36 +204,47 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
198204 "/:id" ,
199205 {
200206 schema : {
207+ querystring : {
208+ type : "object" ,
209+ properties : {
210+ ts : { type : "number" } ,
211+ } ,
212+ } ,
201213 response : { 200 : getEventJsonSchema } ,
202214 } ,
203215 } ,
204216 async ( request : FastifyRequest < EventGetRequest > , reply ) => {
205217 const id = request . params . id ;
218+ const ts = request . query ?. ts ;
219+ const cachedResponse = fastify . nodeCache . get ( `event-${ id } ` ) ;
220+ if ( cachedResponse ) {
221+ if ( ! ts ) {
222+ reply . header ( "Cache-Control" , CLIENT_HTTP_CACHE_POLICY ) ;
223+ }
224+ return reply . header ( "x-acm-cache-status" , "hit" ) . send ( cachedResponse ) ;
225+ }
226+
206227 try {
207228 const response = await fastify . dynamoClient . send (
208- new QueryCommand ( {
229+ new GetItemCommand ( {
209230 TableName : genericConfig . EventsDynamoTableName ,
210- KeyConditionExpression : "#id = :id" ,
211- ExpressionAttributeNames : {
212- "#id" : "id" ,
213- } ,
214- ExpressionAttributeValues : marshall ( { ":id" : id } ) ,
231+ Key : marshall ( { id } ) ,
215232 } ) ,
216233 ) ;
217- const items = response . Items ?. map ( ( item ) => unmarshall ( item ) ) ;
218- if ( items ?. length !== 1 ) {
219- throw new NotFoundError ( {
220- endpointName : request . url ,
221- } ) ;
234+ const item = response . Item ? unmarshall ( response . Item ) : null ;
235+ if ( ! item ) {
236+ throw new NotFoundError ( { endpointName : request . url } ) ;
222237 }
223- reply . send ( items [ 0 ] ) ;
224- } catch ( e : unknown ) {
238+ fastify . nodeCache . set ( `event-${ id } ` , item , EVENT_CACHE_SECONDS ) ;
239+
240+ if ( ! ts ) {
241+ reply . header ( "Cache-Control" , CLIENT_HTTP_CACHE_POLICY ) ;
242+ }
243+ return reply . header ( "x-acm-cache-status" , "miss" ) . send ( item ) ;
244+ } catch ( e ) {
225245 if ( e instanceof BaseError ) {
226246 throw e ;
227247 }
228- if ( e instanceof Error ) {
229- request . log . error ( "Failed to get from DynamoDB: " + e . toString ( ) ) ;
230- }
231248 throw new DatabaseFetchError ( {
232249 message : "Failed to get event from Dynamo table." ,
233250 } ) ;
@@ -268,6 +285,9 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
268285 id,
269286 resource : `/api/v1/events/${ id } ` ,
270287 } ) ;
288+ fastify . nodeCache . del ( "events-upcoming_only=true" ) ;
289+ fastify . nodeCache . del ( "events-upcoming_only=false" ) ;
290+ fastify . nodeCache . del ( `event-${ id } ` ) ;
271291 } catch ( e : unknown ) {
272292 if ( e instanceof Error ) {
273293 request . log . error ( "Failed to delete from DynamoDB: " + e . toString ( ) ) ;
@@ -306,18 +326,17 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
306326 const host = request . query ?. host ;
307327 const ts = request . query ?. ts ; // we only use this to disable cache control
308328 const cachedResponse = fastify . nodeCache . get (
309- `events-upcoming_only=${ upcomingOnly } |host= ${ host } ` ,
310- ) ;
329+ `events-upcoming_only=${ upcomingOnly } ` ,
330+ ) as EventsGetResponse ;
311331 if ( cachedResponse ) {
312- return reply
313- . header (
314- "cache-control" ,
315- ts
316- ? "no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate"
317- : "public, max-age=7200, stale-while-revalidate=900, stale-if-error=86400" ,
318- )
319- . header ( "x-acm-cache-status" , "hit" )
320- . send ( cachedResponse ) ;
332+ if ( ! ts ) {
333+ reply . header ( "Cache-Control" , CLIENT_HTTP_CACHE_POLICY ) ;
334+ }
335+ let filteredResponse = cachedResponse ;
336+ if ( host ) {
337+ filteredResponse = cachedResponse . filter ( ( x ) => x [ "host" ] == host ) ;
338+ }
339+ return reply . header ( "x-acm-cache-status" , "hit" ) . send ( filteredResponse ) ;
321340 }
322341 try {
323342 let command ;
@@ -371,20 +390,17 @@ const eventsPlugin: FastifyPluginAsync = async (fastify, _options) => {
371390 }
372391 } ) ;
373392 }
374- fastify . nodeCache . set (
375- `events-upcoming_only=${ upcomingOnly } ` ,
376- parsedItems ,
377- EVENT_CACHE_SECONDS ,
378- ) ;
379- reply
380- . header (
381- "cache-control" ,
382- ts
383- ? "no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate"
384- : "public, max-age=7200, stale-while-revalidate=900, stale-if-error=86400" ,
385- )
386- . header ( "x-acm-cache-status" , "miss" )
387- . send ( parsedItems ) ;
393+ if ( ! host ) {
394+ fastify . nodeCache . set (
395+ `events-upcoming_only=${ upcomingOnly } ` ,
396+ parsedItems ,
397+ EVENT_CACHE_SECONDS ,
398+ ) ;
399+ }
400+ if ( ! ts ) {
401+ reply . header ( "Cache-Control" , CLIENT_HTTP_CACHE_POLICY ) ;
402+ }
403+ return reply . header ( "x-acm-cache-status" , "hit" ) . send ( parsedItems ) ;
388404 } catch ( e : unknown ) {
389405 if ( e instanceof Error ) {
390406 request . log . error ( "Failed to get from DynamoDB: " + e . toString ( ) ) ;
0 commit comments