fly-node
fly-node copied to clipboard
Fastify
import cookie from "cookie";
import { FastifyError, FastifyReply, FastifyRequest } from "fastify";
export { regionalDatabaseUrl } from "./config";
enum RequestReplayTypes {
CapturedWrite = "captured_write",
HttpMethod = "http_method",
Threshold = "threshold",
}
function inSecondaryRegion() {
return (
process.env.FLY_REGION &&
process.env.PRIMARY_REGION &&
process.env.FLY_REGION !== process.env.PRIMARY_REGION
);
}
function replayInPrimaryRegion(reply: FastifyReply, state: RequestReplayTypes) {
reply.header(
"Fly-Replay",
`region=${process.env.PRIMARY_REGION}; state=${state}`
);
reply.code(409).send(`Replaying in ${process.env.PRIMARY_REGION}`);
}
export async function requestHandler(req: FastifyRequest, reply: FastifyReply) {
const replayableHttpMethods = ["POST", "PUT", "PATCH", "DELETE"];
if (process.env.FLY_REGION) {
reply.header("Fly-Region", process.env.FLY_REGION);
}
if (inSecondaryRegion()) {
if (replayableHttpMethods.includes(req.method as string)) {
return replayInPrimaryRegion(reply, RequestReplayTypes.HttpMethod);
}
const parsedCookies = cookie.parse(req.headers.cookie || "");
if (
parsedCookies &&
parsedCookies["fly-replay-threshold"] &&
parseInt(parsedCookies["fly-replay-threshold"]) - Date.now() > 0
) {
return replayInPrimaryRegion(reply, RequestReplayTypes.Threshold);
}
}
if (req.headers["fly-replay-src"]) {
const matches = req.headers["fly-replay-src"]
.toString()
.matchAll(/(.*?)=(.*?)($|;)/g);
if (
matches &&
!Array.from(matches).some((match) => match[1] === "threshold")
) {
const threshold = Date.now() + 60 * 5;
reply.header(
"Set-Cookie",
`fly-replay-threshold=${threshold}; Path=/; HttpOnly`
);
}
}
}
export function errorHandler(
error: FastifyError,
req: FastifyRequest,
reply: FastifyReply
) {
if (error.toString().includes('SqlState("25006")') && inSecondaryRegion()) {
replayInPrimaryRegion(reply, RequestReplayTypes.CapturedWrite);
} else {
reply.send(error);
}
}
Fastify start server instance:
server.addHook("onRequest", requestHandler);
server.setErrorHandler(errorHandler);