155 lines
4.6 KiB
JavaScript
155 lines
4.6 KiB
JavaScript
const User = require('../models/User');
|
||
const { validateTelegramId } = require('./validator');
|
||
const { logSecurityEvent } = require('./logger');
|
||
const config = require('../config');
|
||
const {
|
||
ACCESS_COOKIE,
|
||
REFRESH_COOKIE,
|
||
signAuthTokens,
|
||
setAuthCookies,
|
||
clearAuthCookies,
|
||
verifyAccessToken,
|
||
verifyRefreshToken
|
||
} = require('../utils/tokens');
|
||
|
||
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) {
|
||
user.settings.whitelist = { noNSFW: true };
|
||
updated = true;
|
||
} else if (user.settings.whitelist.noNSFW === undefined) {
|
||
user.settings.whitelist.noNSFW = true;
|
||
updated = true;
|
||
}
|
||
|
||
if (updated) {
|
||
user.markModified('settings');
|
||
await user.save();
|
||
}
|
||
};
|
||
|
||
// Middleware для проверки авторизации
|
||
const authenticate = async (req, res, next) => {
|
||
try {
|
||
const accessToken = req.cookies[ACCESS_COOKIE];
|
||
const refreshToken = req.cookies[REFRESH_COOKIE];
|
||
|
||
let tokenPayload = null;
|
||
|
||
if (accessToken) {
|
||
try {
|
||
tokenPayload = verifyAccessToken(accessToken);
|
||
} catch (error) {
|
||
if (error.name !== 'TokenExpiredError') {
|
||
logSecurityEvent('INVALID_ACCESS_TOKEN', req, { error: error.message });
|
||
clearAuthCookies(res);
|
||
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!tokenPayload && refreshToken) {
|
||
try {
|
||
const refreshPayload = verifyRefreshToken(refreshToken);
|
||
const userForRefresh = await User.findById(refreshPayload.userId);
|
||
|
||
if (!userForRefresh) {
|
||
clearAuthCookies(res);
|
||
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
|
||
}
|
||
|
||
const tokens = signAuthTokens(userForRefresh);
|
||
setAuthCookies(res, tokens);
|
||
tokenPayload = verifyAccessToken(tokens.accessToken);
|
||
} catch (error) {
|
||
logSecurityEvent('INVALID_REFRESH_TOKEN', req, { error: error.message });
|
||
clearAuthCookies(res);
|
||
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
|
||
}
|
||
}
|
||
|
||
if (!tokenPayload) {
|
||
logSecurityEvent('AUTH_TOKEN_MISSING', req);
|
||
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
|
||
}
|
||
|
||
if (!validateTelegramId(tokenPayload.telegramId)) {
|
||
logSecurityEvent('INVALID_TELEGRAM_ID', req, { telegramId: tokenPayload.telegramId });
|
||
clearAuthCookies(res);
|
||
return res.status(401).json({ error: 'Неверный ID пользователя' });
|
||
}
|
||
|
||
let user = await User.findOne({ telegramId: tokenPayload.telegramId.toString() });
|
||
if (!user) {
|
||
clearAuthCookies(res);
|
||
return res.status(401).json({ error: OFFICIAL_CLIENT_MESSAGE });
|
||
}
|
||
|
||
if (user.banned) {
|
||
clearAuthCookies(res);
|
||
return res.status(403).json({ error: 'Пользователь заблокирован' });
|
||
}
|
||
|
||
await ensureUserSettings(user);
|
||
await touchUserActivity(user);
|
||
req.user = user;
|
||
req.telegramUser = { id: user.telegramId };
|
||
next();
|
||
} catch (error) {
|
||
console.error('❌ Ошибка авторизации:', error);
|
||
res.status(401).json({ error: `Ошибка авторизации. ${OFFICIAL_CLIENT_MESSAGE}` });
|
||
}
|
||
};
|
||
|
||
// 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();
|
||
};
|
||
|
||
module.exports = {
|
||
authenticate,
|
||
requireModerator,
|
||
requireAdmin,
|
||
touchUserActivity,
|
||
ensureUserSettings
|
||
};
|