All files / src/plugins account-context.ts

100% Statements 12/12
100% Branches 6/6
100% Functions 2/2
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45                                  7x 7x   7x 3x 3x 1x     2x         2x 1x     1x 1x       7x        
import fp from "fastify-plugin";
import type { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
import { eq, and } from "drizzle-orm";
import { db } from "../db/index.js";
import { memberships } from "../db/schema.js";
 
declare module "fastify" {
  interface FastifyInstance {
    requireAccount: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
  }
  interface FastifyRequest {
    accountId: string;
    membership: { role: string };
  }
}
 
async function accountContextPlugin(app: FastifyInstance) {
  app.decorateRequest("accountId", "");
  app.decorateRequest("membership", null as unknown as { role: string });
 
  app.decorate("requireAccount", async (request: FastifyRequest, reply: FastifyReply) => {
    const accountId = request.headers["x-account-id"];
    if (!accountId || typeof accountId !== "string") {
      return reply.status(400).send({ error: "X-Account-Id header is required" });
    }
 
    const [membership] = await db
      .select()
      .from(memberships)
      .where(and(eq(memberships.userId, request.user.sub), eq(memberships.accountId, accountId)));
 
    if (!membership) {
      return reply.status(403).send({ error: "You are not a member of this account" });
    }
 
    request.accountId = accountId;
    request.membership = { role: membership.role };
  });
}
 
export const accountContext = fp(accountContextPlugin, {
  name: "account-context",
  dependencies: ["authenticate"],
});