nakama/backend/middleware/auth.js

568 lines
22 KiB
JavaScript
Raw Normal View History

2025-11-03 20:35:01 +00:00
const User = require('../models/User');
2025-11-04 21:51:05 +00:00
const { validateTelegramId } = require('./validator');
const { logSecurityEvent } = require('./logger');
2025-11-10 22:37:25 +00:00
const { validateAndParseInitData } = require('../utils/telegram');
2025-12-01 00:51:23 +00:00
const { fetchLatestAvatar } = require('../jobs/avatarUpdater');
2025-11-03 20:35:01 +00:00
2025-11-10 20:13:22 +00:00
const OFFICIAL_CLIENT_MESSAGE = 'Используйте официальный клиент. Сообщите об ошибке в https://t.me/NakamaReportbot';
const ALLOWED_SEARCH_PREFERENCES = ['furry', 'anime'];
const touchUserActivity = async (user) => {
if (!user) return;
const now = Date.now();
const shouldUpdate =
!user.lastActiveAt ||
Math.abs(now - new Date(user.lastActiveAt).getTime()) > 5 * 60 * 1000;
if (shouldUpdate) {
user.lastActiveAt = new Date(now);
await user.save();
}
};
const ensureUserSettings = async (user) => {
if (!user) return;
let updated = false;
if (!user.settings) {
user.settings = {};
updated = true;
}
if (!ALLOWED_SEARCH_PREFERENCES.includes(user.settings.searchPreference)) {
user.settings.searchPreference = 'furry';
updated = true;
}
if (!user.settings.whitelist) {
2025-12-01 14:26:18 +00:00
user.settings.whitelist = { noNSFW: true, noHomo: true };
2025-11-10 20:13:22 +00:00
updated = true;
2025-12-01 14:26:18 +00:00
} else {
if (user.settings.whitelist.noNSFW === undefined) {
2025-12-01 14:48:54 +00:00
user.settings.whitelist.noNSFW = true;
updated = true;
2025-12-01 14:26:18 +00:00
}
if (user.settings.whitelist.noHomo === undefined) {
user.settings.whitelist.noHomo = true;
updated = true;
}
2025-11-10 20:13:22 +00:00
}
if (updated) {
user.markModified('settings');
await user.save();
}
};
2025-12-01 01:02:46 +00:00
// Нормализовать данные пользователя из Telegram (поддержка camelCase и snake_case)
const normalizeTelegramUser = (telegramUser) => {
return {
id: telegramUser.id,
username: telegramUser.username || telegramUser.userName,
firstName: telegramUser.firstName || telegramUser.first_name || '',
lastName: telegramUser.lastName || telegramUser.last_name || '',
photoUrl: telegramUser.photoUrl || telegramUser.photo_url || null
};
};
2025-12-01 00:51:23 +00:00
// Подтянуть отсутствующие данные пользователя из Telegram
const ensureUserData = async (user, telegramUser) => {
if (!user || !telegramUser) return;
2025-12-01 01:02:46 +00:00
// Нормализовать данные (поддержка camelCase и snake_case)
const normalized = normalizeTelegramUser(telegramUser);
2025-12-01 00:51:23 +00:00
let updated = false;
// Обновить username, если отсутствует или пустой
if (!user.username || user.username.trim() === '') {
2025-12-01 01:02:46 +00:00
if (normalized.username) {
user.username = normalized.username;
2025-12-01 00:51:23 +00:00
updated = true;
2025-12-01 01:02:46 +00:00
} else if (normalized.firstName) {
user.username = normalized.firstName;
2025-12-01 00:51:23 +00:00
updated = true;
}
}
// Обновить firstName, если отсутствует
2025-12-01 01:02:46 +00:00
if (!user.firstName && normalized.firstName) {
user.firstName = normalized.firstName;
2025-12-01 00:51:23 +00:00
updated = true;
}
// Обновить lastName, если отсутствует
if (user.lastName === undefined || user.lastName === null) {
2025-12-01 01:02:46 +00:00
user.lastName = normalized.lastName;
2025-12-01 00:51:23 +00:00
updated = true;
}
// Обновить аватарку, если отсутствует
if (!user.photoUrl) {
2025-12-01 01:02:46 +00:00
// Сначала проверить photoUrl из initData
if (normalized.photoUrl) {
user.photoUrl = normalized.photoUrl;
2025-12-01 00:51:23 +00:00
updated = true;
} else {
// Если нет в initData, попробовать получить через Bot API
try {
const avatarUrl = await fetchLatestAvatar(user.telegramId);
if (avatarUrl) {
user.photoUrl = avatarUrl;
updated = true;
}
} catch (error) {
// Игнорируем ошибки получения аватарки
console.log('Не удалось получить аватарку через Bot API:', error.message);
}
}
}
if (updated) {
await user.save();
}
};
2025-11-03 20:35:01 +00:00
const authenticate = async (req, res, next) => {
try {
2025-11-10 22:37:25 +00:00
const authHeader = req.headers.authorization || '';
2025-11-10 22:48:18 +00:00
let initDataRaw = null;
2025-11-10 21:22:58 +00:00
2025-11-10 22:48:18 +00:00
if (authHeader.startsWith('tma ')) {
initDataRaw = authHeader.slice(4).trim();
}
if (!initDataRaw) {
const headerInitData = req.headers['x-telegram-init-data'];
if (headerInitData && typeof headerInitData === 'string') {
initDataRaw = headerInitData.trim();
}
}
if (!initDataRaw) {
2025-11-10 22:37:25 +00:00
logSecurityEvent('AUTH_TOKEN_MISSING', req);
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
2025-11-03 20:35:01 +00:00
}
2025-11-10 21:22:58 +00:00
2025-11-10 22:37:25 +00:00
if (!initDataRaw) {
logSecurityEvent('EMPTY_INITDATA', req);
2025-11-10 20:13:22 +00:00
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
2025-11-03 22:41:34 +00:00
}
2025-11-10 21:22:58 +00:00
2025-11-10 22:37:25 +00:00
let payload;
try {
payload = validateAndParseInitData(initDataRaw);
} catch (error) {
logSecurityEvent('INVALID_INITDATA', req, { reason: error.message });
return res.status(401).json({ error: `${error.message}. ${OFFICIAL_CLIENT_MESSAGE}` });
}
const telegramUser = payload.user;
2025-12-04 17:44:05 +00:00
const startParam = payload.start_param || payload.startParam;
2025-11-10 22:37:25 +00:00
if (!validateTelegramId(telegramUser.id)) {
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: telegramUser.id });
2025-11-04 21:51:05 +00:00
return res.status(401).json({ error: 'Неверный ID пользователя' });
}
2025-11-10 21:56:36 +00:00
2025-12-01 01:02:46 +00:00
// Нормализовать данные пользователя (библиотека возвращает camelCase, но может быть и snake_case)
const normalizedUser = normalizeTelegramUser(telegramUser);
let user = await User.findOne({ telegramId: normalizedUser.id.toString() });
2025-11-10 22:37:25 +00:00
2025-11-03 20:35:01 +00:00
if (!user) {
2025-12-04 17:44:05 +00:00
// Обработка реферального кода из start_param
let referredBy = null;
2025-12-04 18:23:15 +00:00
if (startParam) {
2025-12-08 15:12:23 +00:00
const trimmedParam = startParam.trim();
2025-12-04 18:23:15 +00:00
// Проверяем регистронезависимо (может быть ref_ или REF_)
2025-12-08 15:12:23 +00:00
const normalizedStartParam = trimmedParam.toLowerCase();
2025-12-04 18:23:15 +00:00
if (normalizedStartParam.startsWith('ref_')) {
2025-12-08 20:11:50 +00:00
// Убираем все префиксы ref_/REF_ (может быть двойной префикс ref_REF_)
let codeToSearch = trimmedParam;
while (codeToSearch.toLowerCase().startsWith('ref_')) {
codeToSearch = codeToSearch.substring(4); // Убираем "ref_" или "REF_"
}
// referralCode в базе хранится с префиксом "REF_" (в верхнем регистре)
// Ищем по коду с префиксом REF_
const escapedCode = codeToSearch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2025-12-04 18:23:15 +00:00
const referrer = await User.findOne({
2025-12-08 20:11:50 +00:00
referralCode: { $regex: new RegExp(`^REF_${escapedCode}$`, 'i') }
2025-12-04 18:23:15 +00:00
});
2025-12-08 15:12:23 +00:00
2025-12-04 18:23:15 +00:00
if (referrer) {
referredBy = referrer._id;
2025-12-08 20:11:50 +00:00
console.log(`🔗 Реферальная ссылка: пользователь ${normalizedUser.username || normalizedUser.id} зарегистрирован по ссылке от ${referrer.username} (${referrer._id}), код: ${trimmedParam} -> REF_${codeToSearch}`);
2025-12-08 15:12:23 +00:00
} else {
// Дополнительная проверка: посмотрим все referralCode в базе для отладки
const allCodes = await User.find({ referralCode: { $exists: true } }, { referralCode: 1, username: 1 }).limit(5);
2025-12-08 20:11:50 +00:00
console.warn(`⚠️ Реферальный код не найден: ${trimmedParam} (искали: REF_${codeToSearch})`);
2025-12-08 15:12:23 +00:00
console.warn(` Примеры кодов в базе: ${allCodes.map(u => u.referralCode).join(', ')}`);
2025-12-04 18:23:15 +00:00
}
2025-12-08 15:12:23 +00:00
} else {
console.log(` startParam не содержит ref_: ${trimmedParam}`);
2025-12-04 17:44:05 +00:00
}
}
2025-11-10 22:37:25 +00:00
user = new User({
2025-12-01 01:02:46 +00:00
telegramId: normalizedUser.id.toString(),
username: normalizedUser.username || normalizedUser.firstName || 'user',
firstName: normalizedUser.firstName,
lastName: normalizedUser.lastName,
2025-12-04 17:44:05 +00:00
photoUrl: normalizedUser.photoUrl,
referredBy: referredBy
2025-11-10 22:37:25 +00:00
});
await user.save();
2025-12-08 15:12:23 +00:00
if (referredBy) {
console.log(`✅ Создан новый пользователь ${user.username} (${user._id}) с referredBy: ${referredBy}`);
} else {
console.log(`✅ Создан новый пользователь ${user.username} (${user._id}) без реферала`);
}
2025-12-04 17:44:05 +00:00
// Счетчик рефералов увеличивается только когда пользователь создаст первый пост
// (см. routes/posts.js)
2025-11-10 22:37:25 +00:00
} else {
2025-12-04 18:23:15 +00:00
// Для существующих пользователей тоже можно установить referredBy,
2025-12-07 02:20:45 +00:00
// если они еще не были засчитаны как реферал и пришли по реферальной ссылке
if (startParam && !user.referredBy && !user.referralCounted) {
2025-12-08 15:12:23 +00:00
const trimmedParam = startParam.trim();
const normalizedStartParam = trimmedParam.toLowerCase();
2025-12-04 18:23:15 +00:00
if (normalizedStartParam.startsWith('ref_')) {
2025-12-08 15:12:23 +00:00
// Ищем по полному коду (с префиксом ref_)
2025-12-04 18:23:15 +00:00
const referrer = await User.findOne({
2025-12-08 15:12:23 +00:00
referralCode: { $regex: new RegExp(`^${trimmedParam.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i') }
2025-12-04 18:23:15 +00:00
});
2025-12-08 15:12:23 +00:00
2025-12-04 18:23:15 +00:00
if (referrer) {
2025-12-07 02:20:45 +00:00
user.referredBy = referrer._id;
await user.save();
2025-12-08 15:12:23 +00:00
console.log(`🔗 Установлен referredBy для существующего пользователя ${user.username} от ${referrer.username} (код: ${trimmedParam})`);
} else {
// Попробуем альтернативный поиск
const codeWithoutPrefix = trimmedParam.substring(4);
const alternativeSearch = await User.findOne({
$or: [
{ referralCode: { $regex: new RegExp(`^ref_${codeWithoutPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i') } },
{ referralCode: { $regex: new RegExp(`^REF_${codeWithoutPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i') } }
]
});
if (alternativeSearch) {
user.referredBy = alternativeSearch._id;
await user.save();
console.log(`🔗 Установлен referredBy (альтернативный поиск) для существующего пользователя ${user.username} от ${alternativeSearch.username}`);
} else {
console.warn(`⚠️ Реферальный код не найден для существующего пользователя ${user.username}: ${trimmedParam}`);
}
2025-12-04 18:23:15 +00:00
}
}
}
2025-12-01 00:51:23 +00:00
// Обновлять только если есть новые данные, не перезаписывать существующие пустыми значениями
2025-12-01 01:02:46 +00:00
if (normalizedUser.username) {
user.username = normalizedUser.username;
} else if (!user.username && normalizedUser.firstName) {
// Если username пустой, использовать firstName как fallback
user.username = normalizedUser.firstName;
2025-12-01 00:51:23 +00:00
}
2025-12-01 01:02:46 +00:00
if (normalizedUser.firstName) {
user.firstName = normalizedUser.firstName;
2025-12-01 00:51:23 +00:00
}
2025-12-01 01:02:46 +00:00
if (normalizedUser.lastName !== undefined) {
user.lastName = normalizedUser.lastName;
2025-12-01 00:51:23 +00:00
}
// Обновлять аватарку только если есть новая
2025-12-01 01:02:46 +00:00
if (normalizedUser.photoUrl) {
user.photoUrl = normalizedUser.photoUrl;
2025-11-10 22:37:25 +00:00
}
2025-12-01 00:51:23 +00:00
2025-11-10 22:37:25 +00:00
await user.save();
2025-11-03 20:35:01 +00:00
}
2025-11-10 21:56:36 +00:00
if (user.banned) {
return res.status(403).json({ error: 'Пользователь заблокирован' });
}
2025-12-01 01:02:46 +00:00
// Подтянуть отсутствующие данные из Telegram (используем нормализованные данные)
await ensureUserData(user, normalizedUser);
2025-11-10 20:13:22 +00:00
await ensureUserSettings(user);
await touchUserActivity(user);
2025-11-10 22:37:25 +00:00
2025-12-07 02:20:45 +00:00
// Реферальная система: отслеживание входов в разные дни
// Останавливаем отслеживание после засчета реферала, чтобы не заполнять БД
if (user.referredBy && !user.referralCounted) {
// Инициализировать loginDates если его нет
if (!user.loginDates) {
user.loginDates = [];
}
2025-12-08 14:37:25 +00:00
const { getMoscowStartOfDay, getMoscowDate } = require('../utils/moscowTime');
const today = getMoscowDate();
2025-12-08 15:12:23 +00:00
const todayNormalized = getMoscowStartOfDay(today);
const todayTime = todayNormalized.getTime();
2025-12-08 14:37:25 +00:00
2025-12-07 02:20:45 +00:00
// Получить уникальные даты из существующего массива (только даты, без времени по московскому времени)
const uniqueDates = new Set();
2025-12-08 15:12:23 +00:00
if (user.loginDates && Array.isArray(user.loginDates)) {
user.loginDates.forEach(date => {
if (!date) return;
try {
// Нормализуем дату: получаем начало дня по московскому времени
const dateObj = new Date(date);
const normalizedDate = getMoscowStartOfDay(dateObj);
const normalizedTime = normalizedDate.getTime();
uniqueDates.add(normalizedTime);
} catch (error) {
console.error(`Ошибка обработки даты ${date}:`, error);
}
});
}
2025-12-07 02:20:45 +00:00
2025-12-08 14:37:25 +00:00
// Добавить сегодняшнюю дату, если её еще нет
const todayExists = uniqueDates.has(todayTime);
if (!todayExists) {
2025-12-08 15:12:23 +00:00
if (!user.loginDates) {
user.loginDates = [];
}
2025-12-08 14:37:25 +00:00
user.loginDates.push(today);
uniqueDates.add(todayTime);
2025-12-08 15:12:23 +00:00
console.log(`📅 Реферал ${user.username} (${user._id}): добавлена дата входа. Уникальных дат: ${uniqueDates.size}/2`);
2025-12-08 14:37:25 +00:00
}
// Если уже есть 2 или более уникальные даты, засчитать реферала
2025-12-07 02:20:45 +00:00
if (uniqueDates.size >= 2) {
const User = require('../models/User');
2025-12-08 14:37:25 +00:00
const referrer = await User.findById(user.referredBy);
2025-12-07 02:20:45 +00:00
2025-12-08 15:12:23 +00:00
if (!referrer) {
console.error(`❌ Реферер не найден для пользователя ${user.username} (${user._id}), referredBy: ${user.referredBy}`);
} else {
2025-12-08 14:37:25 +00:00
// Увеличить счетчик рефералов
2025-12-08 15:12:23 +00:00
const oldCount = referrer.referralsCount || 0;
referrer.referralsCount = oldCount + 1;
2025-12-08 14:37:25 +00:00
await referrer.save();
2025-12-08 15:12:23 +00:00
console.log(`✅ Реферал засчитан: пользователь ${user.username} (${user._id}) засчитан для ${referrer.username} (${referrer._id}). Счетчик: ${oldCount} -> ${referrer.referralsCount}`);
2025-12-08 14:37:25 +00:00
// Начислить баллы за реферала
2025-12-08 15:12:23 +00:00
try {
const { awardReferral } = require('../utils/tickets');
await awardReferral(user.referredBy);
console.log(` ✅ Баллы начислены рефереру ${referrer.username}`);
} catch (error) {
console.error(` ⚠️ Ошибка начисления баллов:`, error.message);
}
2025-12-08 14:37:25 +00:00
}
2025-12-07 02:20:45 +00:00
user.referralCounted = true;
// Очистить loginDates после засчета, чтобы не хранить лишние данные
user.loginDates = [];
await user.save();
} else {
2025-12-08 14:37:25 +00:00
// Если еще нет 2 уникальных дат, сохранить обновленный массив loginDates
await user.save();
2025-12-07 02:20:45 +00:00
}
}
2025-11-03 20:35:01 +00:00
req.user = user;
2025-12-01 01:02:46 +00:00
req.telegramUser = normalizedUser;
2025-11-03 20:35:01 +00:00
next();
} catch (error) {
2025-11-03 21:29:00 +00:00
console.error('❌ Ошибка авторизации:', error);
2025-11-10 21:22:58 +00:00
res.status(401).json({ error: `Ошибка авторизации. ${OFFICIAL_CLIENT_MESSAGE}` });
2025-11-03 20:35:01 +00:00
}
};
// Middleware для проверки роли модератора
const requireModerator = (req, res, next) => {
if (req.user.role !== 'moderator' && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Требуются права модератора' });
}
next();
};
// Middleware для проверки роли админа
const requireAdmin = (req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Требуются права администратора' });
}
next();
};
2025-11-10 23:11:33 +00:00
// Middleware для модерации (использует MODERATION_BOT_TOKEN)
const authenticateModeration = async (req, res, next) => {
const config = require('../config');
try {
const authHeader = req.headers.authorization || '';
let initDataRaw = null;
if (authHeader.startsWith('tma ')) {
initDataRaw = authHeader.slice(4).trim();
}
if (!initDataRaw) {
const headerInitData = req.headers['x-telegram-init-data'];
if (headerInitData && typeof headerInitData === 'string') {
initDataRaw = headerInitData.trim();
}
}
if (!initDataRaw) {
logSecurityEvent('AUTH_TOKEN_MISSING', req);
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
}
let payload;
try {
// Use MODERATION_BOT_TOKEN for validation
payload = validateAndParseInitData(initDataRaw, config.moderationBotToken);
} catch (error) {
logSecurityEvent('INVALID_INITDATA', req, { reason: error.message });
return res.status(401).json({ error: `${error.message}. ${OFFICIAL_CLIENT_MESSAGE}` });
}
const telegramUser = payload.user;
if (!validateTelegramId(telegramUser.id)) {
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: telegramUser.id });
return res.status(401).json({ error: 'Неверный ID пользователя' });
}
2025-12-01 01:02:46 +00:00
// Нормализовать данные пользователя (библиотека возвращает camelCase, но может быть и snake_case)
const normalizedUser = normalizeTelegramUser(telegramUser);
let user = await User.findOne({ telegramId: normalizedUser.id.toString() });
2025-11-10 23:11:33 +00:00
if (!user) {
user = new User({
2025-12-01 01:02:46 +00:00
telegramId: normalizedUser.id.toString(),
username: normalizedUser.username || normalizedUser.firstName || 'user',
firstName: normalizedUser.firstName,
lastName: normalizedUser.lastName,
photoUrl: normalizedUser.photoUrl
2025-11-10 23:11:33 +00:00
});
await user.save();
} else {
2025-12-01 00:51:23 +00:00
// Обновлять только если есть новые данные, не перезаписывать существующие пустыми значениями
2025-12-01 01:02:46 +00:00
if (normalizedUser.username) {
user.username = normalizedUser.username;
} else if (!user.username && normalizedUser.firstName) {
// Если username пустой, использовать firstName как fallback
user.username = normalizedUser.firstName;
2025-12-01 00:51:23 +00:00
}
2025-12-01 01:02:46 +00:00
if (normalizedUser.firstName) {
user.firstName = normalizedUser.firstName;
2025-12-01 00:51:23 +00:00
}
2025-12-01 01:02:46 +00:00
if (normalizedUser.lastName !== undefined) {
user.lastName = normalizedUser.lastName;
2025-12-01 00:51:23 +00:00
}
// Обновлять аватарку только если есть новая
2025-12-01 01:02:46 +00:00
if (normalizedUser.photoUrl) {
user.photoUrl = normalizedUser.photoUrl;
2025-11-10 23:11:33 +00:00
}
2025-12-01 00:51:23 +00:00
2025-11-10 23:11:33 +00:00
await user.save();
}
if (user.banned) {
return res.status(403).json({ error: 'Пользователь заблокирован' });
}
2025-12-01 01:02:46 +00:00
// Подтянуть отсутствующие данные из Telegram (используем нормализованные данные)
await ensureUserData(user, normalizedUser);
2025-11-10 23:11:33 +00:00
await ensureUserSettings(user);
await touchUserActivity(user);
req.user = user;
2025-12-01 01:02:46 +00:00
req.telegramUser = normalizedUser;
2025-11-10 23:11:33 +00:00
next();
} catch (error) {
console.error('❌ Ошибка авторизации модерации:', error);
res.status(401).json({ error: `Ошибка авторизации. ${OFFICIAL_CLIENT_MESSAGE}` });
}
};
2025-12-08 23:42:32 +00:00
// Middleware для проверки JWT токена (для модерации через логин/пароль)
const authenticateJWT = async (req, res, next) => {
try {
// Получить токен из заголовка или cookie
let token = null;
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
token = authHeader.slice(7);
} else if (req.cookies) {
const { verifyAccessToken, ACCESS_COOKIE } = require('../utils/tokens');
token = req.cookies[ACCESS_COOKIE];
}
if (!token) {
return res.status(401).json({ error: 'Требуется авторизация' });
}
// Проверить токен
const { verifyAccessToken } = require('../utils/tokens');
let payload;
try {
payload = verifyAccessToken(token);
} catch (error) {
logSecurityEvent('INVALID_JWT_TOKEN', req);
return res.status(401).json({ error: 'Неверный токен' });
}
// Найти пользователя
const user = await User.findById(payload.userId);
if (!user) {
return res.status(401).json({ error: 'Пользователь не найден' });
}
if (user.banned) {
return res.status(403).json({ error: 'Аккаунт заблокирован' });
}
req.user = user;
next();
} catch (error) {
console.error('Ошибка JWT авторизации:', error);
res.status(401).json({ error: 'Ошибка авторизации' });
}
};
// Комбинированный middleware: Telegram или JWT
const authenticateModerationFlexible = async (req, res, next) => {
// Попробовать Telegram авторизацию
const authHeader = req.headers.authorization || '';
const hasTelegramAuth = authHeader.startsWith('tma ') || req.headers['x-telegram-init-data'];
if (hasTelegramAuth) {
return authenticateModeration(req, res, next);
} else {
return authenticateJWT(req, res, next);
}
};
2025-11-03 20:35:01 +00:00
module.exports = {
authenticate,
2025-11-10 23:11:33 +00:00
authenticateModeration,
2025-12-08 23:42:32 +00:00
authenticateJWT,
authenticateModerationFlexible,
2025-11-03 20:35:01 +00:00
requireModerator,
2025-11-10 21:56:36 +00:00
requireAdmin,
touchUserActivity,
2025-12-01 00:51:23 +00:00
ensureUserSettings,
ensureUserData
2025-11-03 20:35:01 +00:00
};