netlify/functions/ping_data.ts (58 lines of code) (raw):
import type { Config, Context } from "@netlify/functions";
import { getStore } from "@netlify/blobs";
import { DATA_VERSION } from "app/data/format.ts";
const CACHE_MAX_AGE: number = 60 * 60 * 24; // 1 day
export default async (req: Request, context: Context): Promise<Response> => {
const { date } = context.params;
const store = getStore("ping-data");
let etag: string | undefined;
{
// Only get the first If-None-Match entry, if any
const if_none_match = req.headers.get("If-None-Match");
let etag_match: RegExpMatchArray | null | undefined;
if (if_none_match && (etag_match = if_none_match.match(/"(.*?)"/))) {
etag = etag_match[1];
}
}
const result = await store.getWithMetadata(date, { type: "stream", consistency: "strong", etag });
const missing = !result;
const oldVersion = result && result.metadata["version"] !== DATA_VERSION;
const retry = result && result.metadata["retry"] &&
Date.now() >= (result.metadata["retry"] as number);
if (missing || oldVersion || retry) {
// The background function will set a date key in ping-data-request;
// check it to avoid launching the background function unnecessarily.
const requestStore = getStore("ping-data-request");
if (await requestStore.get(date) !== null) {
return new Response(null, { status: 202 });
}
console.log(`initializing background fetch: ${missing ? "missing" : oldVersion ? "old data version" : "retry"}`);
return await fetch(`${context.site.url}/.netlify/functions/store-ping-data-background`, {
method: "POST",
headers: { "Authorization": `PSK ${process.env["NETLIFY_LOCAL_PSK"]}` },
body: JSON.stringify({ date })
});
}
const headers: Record<string, string> = {
"Cache-Control": `public, max-age=${CACHE_MAX_AGE}`,
};
// Add cache-related headers
const modified_date: string | undefined = result.metadata["date"] as any;
if (modified_date) {
headers["Last-Modified"] = new Date(modified_date).toUTCString();
}
if (result.etag) {
headers["ETag"] = result.etag;
}
// Check whether we can omit the response
if (etag && result.etag === etag) {
return new Response(null, { status: 304, headers });
}
const if_modified_since = req.headers.get("If-Modified-Since");
if (modified_date && if_modified_since) {
if (new Date(modified_date).getTime() <= new Date(if_modified_since).getTime()) {
return new Response(null, { status: 304, headers });
}
}
// We're sending the data payload, so add content headers
headers["Content-Encoding"] = "gzip";
headers["Content-Type"] = "application/json";
return new Response(result.data, { headers });
};
export const config: Config = {
path: "/ping_data/:date"
};