fly-node icon indicating copy to clipboard operation
fly-node copied to clipboard

Fastify

Open semoal opened this issue 2 years ago • 0 comments

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);

semoal avatar Jun 20 '23 12:06 semoal