Каждый tRPC-запрос получает контекст — объект с информацией о текущем пользователе, доступными сервисами и функциями для работы с БД.
Структура контекста
{
user: User | null, // Текущий пользователь (или null)
userAccess: {
privileges: string[], // Группы привилегий
permissions: string[], // Права доступа
},
sessionToken: string | null, // Токен текущей сессии (или null)
clientIp: string | null, // IP клиента (cf-connecting-ip / x-real-ip / x-forwarded-for / remoteAddress)
withDatabase, // Функция для запросов к БД
withTransaction, // Функция для транзакций
avatarService, // Генерация аватаров
storageService, // S3-хранилище
oauthStateStorage, // Хранилище OAuth-состояний
verificationService, // Верификация пользователей
paymentsService, // Платёжная система
analyticsService, // Аналитика
notificationService, // Уведомления и WebSocket
rconService, // RCON-подключение к серверам
permissionSyncService, // Синхронизация прав с игровыми серверами
punishmentsService, // Наказания (баны, муты, варны)
res, // Express Response (для cookies)
}
Пользователь
Если запрос содержит валидный токен авторизации (cookie или заголовок Authorization), в ctx.user будет объект пользователя из БД. Иначе — null.
// В protectedProcedure ctx.user гарантированно не null
const userId = ctx.user.id;
const username = ctx.user.username;
Работа с БД
withDatabase
Выполняет запрос к базе данных. Принимает функцию с аргументами db (Drizzle-клиент), schema (все таблицы) и dialect (тип БД).
const users = await ctx.withDatabase(({ db, schema }) =>
db.select().from(schema.users).limit(10)
);
withTransaction
То же самое, но в рамках транзакции. Если функция выбросит ошибку — все изменения откатятся.
await ctx.withTransaction(async ({ db, schema }) => {
await db.insert(schema.payments).values({ ... });
await db.update(schema.users).set({ balance: newBalance }).where(...);
});
Обе функции автоматически подмешивают схемы модулей в объект schema. Это значит, что таблицы модулей доступны наравне с основными.
Сервисы
Сервисы прокидываются из NestJS через dependency injection. Каждый сервис — это интерфейс, описанный в packages/trpc/src/context.ts.
Динамические import() запрещены. Все зависимости должны прокидываться через контекст.
avatarService
Генерация аватаров по умолчанию.
if (ctx.avatarService) {
const url = await ctx.avatarService.generateDefaultAvatar(username);
}
storageService
Загрузка файлов в S3-совместимое хранилище.
await ctx.storageService.uploadObject({
key: 'avatars/user-1.png',
body: buffer,
contentType: 'image/png',
});
const url = ctx.storageService.getObjectUrl('avatars/user-1.png');
paymentsService
Создание платёжных ссылок и получение информации о платежах.
const { link } = await ctx.paymentsService.createPaymentLink(
'yookassa', userId, 500, email, username
);
const providers = await ctx.paymentsService.getEnabledProviders();
const limits = await ctx.paymentsService.getPaymentLimits();
notificationService
Отправка уведомлений пользователям и broadcast.
await ctx.notificationService.send({
userId: 1,
type: 'payment',
title: 'Оплата получена',
message: 'Ваш баланс пополнен на 500 руб.',
channels: ['push', 'websocket'],
});
// Отправка события через WebSocket
ctx.notificationService.emitToUser(userId, 'balance_update', { balance: 1500 });
verificationService
Управление верификацией пользователей.
const result = await ctx.verificationService.checkOnLogin(userId, {
ip, userAgent, fingerprint, deviceInfo,
});
if (result.requiresVerification) {
// Требуется верификация
}
rconService
Отправка команд на Minecraft серверы через RCON.
const response = await ctx.rconService.sendCommand(serverId, 'say Hello!');
const isOnline = ctx.rconService.isConnected(serverId);
analyticsService
Сбор и получение аналитики.
if (ctx.analyticsService?.isEnabled()) {
ctx.analyticsService.trackPageView({ sessionId, path: '/shop', userId });
const summary = await ctx.analyticsService.getSummary(30); // за 30 дней
const realtime = await ctx.analyticsService.getRealTimeVisitors();
}
permissionSyncService
Синхронизация привилегий и прав с игровыми серверами (например, LuckPerms) при выдаче, отзыве и изменении.
if (ctx.permissionSyncService) {
await ctx.permissionSyncService.onPrivilegeGranted(
username, privilegeName, privilegeServerId, durationSeconds
);
await ctx.permissionSyncService.onUserPermissionAdded(username, node);
}
punishmentsService
Управление наказаниями (баны, муты, варны, HWID-баны) и покупками разбана.
const { punishments, total } = await ctx.punishmentsService.getPunishments({
userId, isActive: true,
});
const price = await ctx.punishmentsService.calculateUnbanPrice(punishmentId, userId);
const stats = await ctx.punishmentsService.getStats();
Создание контекста
Контекст создаётся в packages/trpc/src/context.ts через функцию createContext. На сервере (NestJS) используется createContextFactory, которая принимает сервисы и возвращает фабрику контекста для каждого запроса.
const contextFactory = createContextFactory({
avatarService,
storageService,
paymentsService,
// ...
});
// Для каждого запроса:
const ctx = await contextFactory({ req, res });