import { initTRPC } from "@trpc/server" import { experimental_nextAppDirCaller } from "@trpc/server/adapters/next-app-dir" import { ZodError } from "zod" import { env } from "@/env/server" import { badRequestError, internalServerError, sessionExpiredError, unauthorizedError, } from "./errors/trpc" import { fetchServiceToken } from "./tokenManager" import { type Context, createContext } from "./context" import { transformer } from "./transformer" import { langInput } from "./utils" import type { Session } from "next-auth" import type { Meta } from "@/types/trpc/meta" const t = initTRPC .context() .meta() .create({ transformer, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, } }, }) export const { createCallerFactory, mergeRouters, router } = t export const publicProcedure = t.procedure export const contentstackBaseProcedure = t.procedure.use(async function (opts) { if (!opts.ctx.lang) { throw badRequestError("Missing Lang in tRPC context") } return opts.next({ ctx: { lang: opts.ctx.lang, }, }) }) export const contentstackExtendedProcedureUID = contentstackBaseProcedure.use( async function (opts) { if (!opts.ctx.uid) { throw badRequestError("Missing UID in tRPC context") } return opts.next({ ctx: { uid: opts.ctx.uid, }, }) } ) export const protectedProcedure = t.procedure.use(async function (opts) { const authRequired = opts.meta?.authRequired ?? true const session = await opts.ctx.auth() if (!authRequired && env.NODE_ENV === "development") { console.info( `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` ) console.info(`path: ${opts.path} | type: ${opts.type}`) } if (!session) { throw unauthorizedError() } if (session?.error === "RefreshAccessTokenError") { throw sessionExpiredError() } return opts.next({ ctx: { session, }, }) }) export const safeProtectedProcedure = t.procedure.use(async function (opts) { const authRequired = opts.meta?.authRequired ?? true let session: Session | null = await opts.ctx.auth() if (!authRequired && env.NODE_ENV === "development") { console.info( `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` ) console.info(`path: ${opts.path} | type: ${opts.type}`) } if (!session || session.error === "RefreshAccessTokenError") { session = null } return opts.next({ ctx: { session, }, }) }) export const serviceProcedure = t.procedure.use(async (opts) => { const { access_token } = await fetchServiceToken() if (!access_token) { throw internalServerError("Failed to obtain service token") } return opts.next({ ctx: { serviceToken: access_token, }, }) }) export const serverActionProcedure = t.procedure.experimental_caller( experimental_nextAppDirCaller({ createContext, normalizeFormData: true, }) ) export const protectedServerActionProcedure = serverActionProcedure.use( async (opts) => { const session = await opts.ctx.auth() if (!session) { throw unauthorizedError() } if (session && session.error === "RefreshAccessTokenError") { throw sessionExpiredError() } return opts.next({ ctx: { ...opts.ctx, session, }, }) } )