import { Lang } from "@scandic-hotels/common/constants/language" import { logger } from "@scandic-hotels/common/logger" import { getServiceToken } from "@scandic-hotels/common/tokenManager" import { env } from "../env/server" import { durationMiddleware } from "./middlewares/durationMiddleware" import { sentryMiddleware } from "./middlewares/sentryMiddleware" import { badRequestError, internalServerError, sessionExpiredError, unauthorizedError, } from "./errors" import { langInput } from "./utils" import { procedure } from "." export const baseProcedure = procedure .use(durationMiddleware) .use(sentryMiddleware) /** @alias */ export const publicProcedure = baseProcedure export const languageProcedure = baseProcedure.use(async function (opts) { if (!opts.ctx.lang) { // When fetching data client side with TRPC we don't pass through middlewares and therefore do not get the lang through headers // We can then pass lang as an input in the request and set it to the context in the procedure const input = await opts.getRawInput() const parsedInput = langInput.safeParse(input) if (process.env.NODE_ENV === "development" && !parsedInput.success) { throw badRequestError({ message: "Missing lang in tRPC context", errorDetails: { path: opts.path, type: opts.type, input: opts.input, }, }) } return opts.next({ ctx: { lang: parsedInput.success ? parsedInput.data.lang : Lang.en, }, }) } return opts.next({ ctx: { lang: opts.ctx.lang, }, }) }) /** @alias */ export const contentstackBaseProcedure = languageProcedure 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 = baseProcedure.use(async function (opts) { const authRequired = opts.meta?.authRequired ?? true const session = await opts.ctx.auth() if (!authRequired && env.NODE_ENV === "development") { logger.debug( `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` ) logger.debug(`path: ${opts.path} | type: ${opts.type}`) } if (!session) { throw unauthorizedError() } if (session?.error === "RefreshAccessTokenError") { throw sessionExpiredError() } return opts.next({ ctx: { session, }, }) }) export const safeProtectedProcedure = baseProcedure.use(async function (opts) { const authRequired = opts.meta?.authRequired ?? true let session = await opts.ctx.auth() if (!authRequired && env.NODE_ENV === "development") { logger.debug( `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` ) logger.debug(`path: ${opts.path} | type: ${opts.type}`) } if (!session || session.error === "RefreshAccessTokenError") { session = null } return opts.next({ ctx: { session, }, }) }) export const serviceProcedure = baseProcedure.use(async (opts) => { const token = await getServiceToken() const { access_token } = token if (!access_token) { throw internalServerError(`[serviceProcedure] No service token`) } return opts.next({ ctx: { serviceToken: access_token, }, }) }) export const contentStackUidWithServiceProcedure = contentstackExtendedProcedureUID.concat(serviceProcedure) export const contentStackBaseWithServiceProcedure = contentstackBaseProcedure.concat(serviceProcedure) export const contentStackBaseWithProtectedProcedure = contentstackBaseProcedure.concat(protectedProcedure) export const safeProtectedServiceProcedure = safeProtectedProcedure.concat(serviceProcedure) export const languageProtectedProcedure = protectedProcedure.concat(languageProcedure) type ExperimentalProcedureCaller = ReturnType< typeof baseProcedure.experimental_caller > export function getProtectedServerActionProcedure( serverActionProcedure: ExperimentalProcedureCaller ) { return 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, }, }) }) }