File size: 2,433 Bytes
c09f67c |
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import { createClient } from "@api/services/supabase";
import type { Session } from "@api/utils/auth";
import { verifyAccessToken } from "@api/utils/auth";
import { getGeoContext } from "@api/utils/geo";
import type { Database } from "@midday/db/client";
import { db } from "@midday/db/client";
import type { SupabaseClient } from "@supabase/supabase-js";
import { initTRPC, TRPCError } from "@trpc/server";
import type { Context } from "hono";
import superjson from "superjson";
import { withPrimaryReadAfterWrite } from "./middleware/primary-read-after-write";
import { withTeamPermission } from "./middleware/team-permission";
type TRPCContext = {
session: Session | null;
supabase: SupabaseClient;
db: Database;
geo: ReturnType<typeof getGeoContext>;
teamId?: string;
forcePrimary?: boolean;
};
export const createTRPCContext = async (
_: unknown,
c: Context,
): Promise<TRPCContext> => {
const accessToken = c.req.header("Authorization")?.split(" ")[1];
const session = await verifyAccessToken(accessToken);
const supabase = await createClient(accessToken);
// Use the singleton database instance - no need for caching
const geo = getGeoContext(c.req);
// Check if client wants to force primary database reads (for replication lag handling)
const forcePrimary = c.req.header("x-force-primary") === "true";
return {
session,
supabase,
db,
geo,
forcePrimary,
};
};
const t = initTRPC.context<TRPCContext>().create({
transformer: superjson,
});
export const createTRPCRouter = t.router;
export const createCallerFactory = t.createCallerFactory;
const withPrimaryDbMiddleware = t.middleware(async (opts) => {
return withPrimaryReadAfterWrite({
ctx: opts.ctx,
type: opts.type,
next: opts.next,
});
});
const withTeamPermissionMiddleware = t.middleware(async (opts) => {
return withTeamPermission({
ctx: opts.ctx,
next: opts.next,
});
});
export const publicProcedure = t.procedure.use(withPrimaryDbMiddleware);
export const protectedProcedure = t.procedure
.use(withTeamPermissionMiddleware) // NOTE: This is needed to ensure that the teamId is set in the context
.use(withPrimaryDbMiddleware)
.use(async (opts) => {
const { teamId, session } = opts.ctx;
if (!session) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return opts.next({
ctx: {
teamId,
session,
},
});
});
|