/
DME
/
Notes
Обзор
Документация
Войти
/
DME
/
Notes
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/server/api/trpc.ts
145 строк
3 KB
Karasique333
Commit
19 июн 2025, 15:09
19 июн 2025, 15:09
d96c829
Код
Авторство
О чём код?
import { betterFetch } from '@better-fetch/fetch' import { TRPCError, initTRPC } from '@trpc/server' import superjson from 'superjson' import { ZodError } from 'zod' import { rateLimiter } from '@/lib/redis' import { getBaseUrl } from '@/lib/utils' import { db } from '@/server/db' export type Session = { id: string userId: string createdAt: string updatedAt: string expiresAt: string token: string ipAddress?: string | null userAgent?: string | null } export type User = { id: string name: string email: string emailVerified: boolean image: string | null createdAt: string updatedAt: string } export type ContextSession = { session: Session user: User } export const createTRPCContext = async (opts: { headers: Headers }) => { const { data: session } = await betterFetch<ContextSession>( '/api/auth/get-session', { baseURL: getBaseUrl(), headers: { cookie: opts.headers.get('cookie') ?? '', }, } ) return { db, session: session ?? null, ...opts, } } const t = initTRPC.context<typeof createTRPCContext>().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, } }, }) export const createCallerFactory = t.createCallerFactory export const createTRPCRouter = t.router const timingMiddleware = t.middleware(async ({ next, path }) => { const start = Date.now() if (t._config.isDev) { // artificial delay in dev const waitMs = Math.floor(Math.random() * 400) + 100 await new Promise((resolve) => setTimeout(resolve, waitMs)) } const result = await next() const end = Date.now() console.log(`[TRPC] ${path} took ${end - start}ms to execute`) return result }) const rateLimitMiddleware = t.middleware(async ({ ctx, next, path }) => { // Extract client IP from headers (use a fallback if not available) const clientIp = ctx.headers.get('x-forwarded-for') ?? 'unknown_client' // Check rate limit const { success, remaining } = await rateLimiter.limit(clientIp) if (!success) { throw new TRPCError({ code: 'TOO_MANY_REQUESTS', message: `Rate limit exceeded for ${path}. Please try again later.`, }) } const updatedCtx = { ...ctx, remainingRequests: remaining, } return next({ ctx: updatedCtx, }) }) const authMiddleware = t.middleware(async ({ next, ctx }) => { if (!ctx.session) { throw new TRPCError({ code: 'UNAUTHORIZED', message: 'You must be logged in to access this resource.', }) } const res = await next({ ctx: { session: ctx.session.session, }, }) return res }) export const publicProcedure = t.procedure.use(timingMiddleware) export const publicProcedureWithRateLimit = publicProcedure .use(timingMiddleware) .use(rateLimitMiddleware) export const authProcedure = publicProcedure.use(authMiddleware)