Update files
This commit is contained in:
parent
b6036af3f1
commit
c3f2746723
|
|
@ -142,8 +142,87 @@ const requireAdmin = (req, res, next) => {
|
|||
next();
|
||||
};
|
||||
|
||||
// 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 пользователя' });
|
||||
}
|
||||
|
||||
let user = await User.findOne({ telegramId: telegramUser.id.toString() });
|
||||
|
||||
if (!user) {
|
||||
user = new User({
|
||||
telegramId: telegramUser.id.toString(),
|
||||
username: telegramUser.username || telegramUser.first_name,
|
||||
firstName: telegramUser.first_name,
|
||||
lastName: telegramUser.last_name,
|
||||
photoUrl: telegramUser.photo_url
|
||||
});
|
||||
await user.save();
|
||||
} else {
|
||||
user.username = telegramUser.username || telegramUser.first_name;
|
||||
user.firstName = telegramUser.first_name;
|
||||
user.lastName = telegramUser.last_name;
|
||||
if (telegramUser.photo_url) {
|
||||
user.photoUrl = telegramUser.photo_url;
|
||||
}
|
||||
await user.save();
|
||||
}
|
||||
|
||||
if (user.banned) {
|
||||
return res.status(403).json({ error: 'Пользователь заблокирован' });
|
||||
}
|
||||
|
||||
await ensureUserSettings(user);
|
||||
await touchUserActivity(user);
|
||||
|
||||
req.user = user;
|
||||
req.telegramUser = telegramUser;
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка авторизации модерации:', error);
|
||||
res.status(401).json({ error: `Ошибка авторизации. ${OFFICIAL_CLIENT_MESSAGE}` });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
authenticate,
|
||||
authenticateModeration,
|
||||
requireModerator,
|
||||
requireAdmin,
|
||||
touchUserActivity,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const router = express.Router();
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const multer = require('multer');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
const { authenticateModeration } = require('../middleware/auth');
|
||||
const { logSecurityEvent } = require('../middleware/logger');
|
||||
const User = require('../models/User');
|
||||
const Post = require('../models/Post');
|
||||
|
|
@ -68,7 +68,7 @@ const serializeUser = (user) => ({
|
|||
createdAt: user.createdAt
|
||||
});
|
||||
|
||||
router.post('/auth/verify', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.post('/auth/verify', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const admins = await listAdmins();
|
||||
|
||||
res.json({
|
||||
|
|
@ -85,7 +85,7 @@ router.post('/auth/verify', authenticate, requireModerationAccess, async (req, r
|
|||
});
|
||||
});
|
||||
|
||||
router.get('/users', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.get('/users', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { filter = 'active', page = 1, limit = 50 } = req.query;
|
||||
const pageNum = Math.max(parseInt(page, 10) || 1, 1);
|
||||
const limitNum = Math.min(Math.max(parseInt(limit, 10) || 50, 1), 200);
|
||||
|
|
@ -125,7 +125,7 @@ router.get('/users', authenticate, requireModerationAccess, async (req, res) =>
|
|||
});
|
||||
});
|
||||
|
||||
router.put('/users/:id/ban', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.put('/users/:id/ban', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { banned, days } = req.body;
|
||||
|
||||
const user = await User.findById(req.params.id);
|
||||
|
|
@ -145,7 +145,7 @@ router.put('/users/:id/ban', authenticate, requireModerationAccess, async (req,
|
|||
res.json({ user: serializeUser(user) });
|
||||
});
|
||||
|
||||
router.get('/posts', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.get('/posts', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { page = 1, limit = 20, author, tag } = req.query;
|
||||
const pageNum = Math.max(parseInt(page, 10) || 1, 1);
|
||||
const limitNum = Math.min(Math.max(parseInt(limit, 10) || 20, 1), 100);
|
||||
|
|
@ -190,7 +190,7 @@ router.get('/posts', authenticate, requireModerationAccess, async (req, res) =>
|
|||
});
|
||||
});
|
||||
|
||||
router.put('/posts/:id', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { content, hashtags, tags, isNSFW } = req.body;
|
||||
|
||||
const post = await Post.findById(req.params.id);
|
||||
|
|
@ -233,7 +233,7 @@ router.put('/posts/:id', authenticate, requireModerationAccess, async (req, res)
|
|||
});
|
||||
});
|
||||
|
||||
router.delete('/posts/:id', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.delete('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const post = await Post.findById(req.params.id);
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: 'Пост не найден' });
|
||||
|
|
@ -254,7 +254,7 @@ router.delete('/posts/:id', authenticate, requireModerationAccess, async (req, r
|
|||
res.json({ success: true });
|
||||
});
|
||||
|
||||
router.delete('/posts/:id/images/:index', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.delete('/posts/:id/images/:index', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { id, index } = req.params;
|
||||
const idx = parseInt(index, 10);
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ router.delete('/posts/:id/images/:index', authenticate, requireModerationAccess,
|
|||
res.json({ images: post.images });
|
||||
});
|
||||
|
||||
router.post('/posts/:id/ban', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.post('/posts/:id/ban', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { days = 7 } = req.body;
|
||||
|
||||
|
|
@ -298,7 +298,7 @@ router.post('/posts/:id/ban', authenticate, requireModerationAccess, async (req,
|
|||
res.json({ user: serializeUser(post.author) });
|
||||
});
|
||||
|
||||
router.get('/reports', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.get('/reports', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { page = 1, limit = 30, status = 'pending' } = req.query;
|
||||
const pageNum = Math.max(parseInt(page, 10) || 1, 1);
|
||||
const limitNum = Math.min(Math.max(parseInt(limit, 10) || 30, 1), 100);
|
||||
|
|
@ -345,7 +345,7 @@ router.get('/reports', authenticate, requireModerationAccess, async (req, res) =
|
|||
});
|
||||
});
|
||||
|
||||
router.put('/reports/:id', authenticate, requireModerationAccess, async (req, res) => {
|
||||
router.put('/reports/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||
const { status = 'reviewed' } = req.body;
|
||||
const report = await Report.findById(req.params.id);
|
||||
|
||||
|
|
@ -362,7 +362,7 @@ router.put('/reports/:id', authenticate, requireModerationAccess, async (req, re
|
|||
|
||||
router.post(
|
||||
'/channel/publish',
|
||||
authenticate,
|
||||
authenticateModeration,
|
||||
requireModerationAccess,
|
||||
upload.array('images', 10),
|
||||
async (req, res) => {
|
||||
|
|
|
|||
|
|
@ -42,16 +42,19 @@ function manualValidateInitData(initDataRaw, botToken) {
|
|||
return signature === hash;
|
||||
}
|
||||
|
||||
function validateAndParseInitData(initDataRaw) {
|
||||
function validateAndParseInitData(initDataRaw, botToken = null) {
|
||||
const tokenToUse = botToken || config.telegramBotToken;
|
||||
|
||||
console.log('[Telegram] validateAndParseInitData called:', {
|
||||
hasInitData: !!initDataRaw,
|
||||
type: typeof initDataRaw,
|
||||
length: initDataRaw?.length || 0,
|
||||
preview: initDataRaw?.substring(0, 100) + '...'
|
||||
preview: initDataRaw?.substring(0, 100) + '...',
|
||||
usingModerationToken: !!botToken
|
||||
});
|
||||
|
||||
if (!config.telegramBotToken) {
|
||||
throw new Error('TELEGRAM_BOT_TOKEN не настроен');
|
||||
if (!tokenToUse) {
|
||||
throw new Error('Bot token не настроен');
|
||||
}
|
||||
|
||||
if (!initDataRaw || typeof initDataRaw !== 'string') {
|
||||
|
|
@ -69,7 +72,7 @@ function validateAndParseInitData(initDataRaw) {
|
|||
// Try library validation first
|
||||
let valid = false;
|
||||
try {
|
||||
validate(trimmed, config.telegramBotToken);
|
||||
validate(trimmed, tokenToUse);
|
||||
valid = true;
|
||||
console.log('[Telegram] Library validation successful');
|
||||
} catch (libError) {
|
||||
|
|
@ -77,7 +80,7 @@ function validateAndParseInitData(initDataRaw) {
|
|||
|
||||
// Fallback to manual validation with base64 padding fix
|
||||
try {
|
||||
valid = manualValidateInitData(trimmed, config.telegramBotToken);
|
||||
valid = manualValidateInitData(trimmed, tokenToUse);
|
||||
if (valid) {
|
||||
console.log('[Telegram] Manual validation successful');
|
||||
}
|
||||
|
|
@ -98,16 +101,19 @@ function validateAndParseInitData(initDataRaw) {
|
|||
console.log('[Telegram] Parsed payload:', {
|
||||
hasUser: !!payload?.user,
|
||||
userId: payload?.user?.id,
|
||||
authDate: payload?.auth_date
|
||||
authDate: payload?.auth_date,
|
||||
allKeys: Object.keys(payload)
|
||||
});
|
||||
|
||||
if (!payload || !payload.user) {
|
||||
throw new Error('Отсутствует пользователь в initData');
|
||||
}
|
||||
|
||||
const authDate = Number(payload.auth_date);
|
||||
// Check for auth_date - it might be authDate in parsed payload
|
||||
const authDate = Number(payload.auth_date || payload.authDate);
|
||||
|
||||
if (!authDate) {
|
||||
console.error('[Telegram] Missing auth_date in payload:', payload);
|
||||
throw new Error('Отсутствует auth_date в initData');
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue