diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index a375c79..85dd38f 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -175,16 +175,29 @@ const authenticate = async (req, res, next) => { // Обработка реферального кода из start_param let referredBy = null; if (startParam) { + const trimmedParam = startParam.trim(); // Проверяем регистронезависимо (может быть ref_ или REF_) - const normalizedStartParam = startParam.toLowerCase(); + const normalizedStartParam = trimmedParam.toLowerCase(); + if (normalizedStartParam.startsWith('ref_')) { - // Ищем реферера по коду (регистронезависимо) + // referralCode в базе хранится с префиксом "ref_" в верхнем регистре + // Ищем регистронезависимо по полному коду + const escapedCode = trimmedParam.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const referrer = await User.findOne({ - referralCode: { $regex: new RegExp(`^${startParam}$`, 'i') } + referralCode: { $regex: new RegExp(`^${escapedCode}$`, 'i') } }); + if (referrer) { referredBy = referrer._id; + console.log(`🔗 Реферальная ссылка: пользователь ${normalizedUser.username || normalizedUser.id} зарегистрирован по ссылке от ${referrer.username} (${referrer._id}), код: ${trimmedParam}`); + } else { + // Дополнительная проверка: посмотрим все referralCode в базе для отладки + const allCodes = await User.find({ referralCode: { $exists: true } }, { referralCode: 1, username: 1 }).limit(5); + console.warn(`⚠️ Реферальный код не найден: ${trimmedParam}`); + console.warn(` Примеры кодов в базе: ${allCodes.map(u => u.referralCode).join(', ')}`); } + } else { + console.log(`ℹ️ startParam не содержит ref_: ${trimmedParam}`); } } @@ -197,6 +210,12 @@ const authenticate = async (req, res, next) => { referredBy: referredBy }); await user.save(); + + if (referredBy) { + console.log(`✅ Создан новый пользователь ${user.username} (${user._id}) с referredBy: ${referredBy}`); + } else { + console.log(`✅ Создан новый пользователь ${user.username} (${user._id}) без реферала`); + } // Счетчик рефералов увеличивается только когда пользователь создаст первый пост // (см. routes/posts.js) @@ -204,15 +223,36 @@ const authenticate = async (req, res, next) => { // Для существующих пользователей тоже можно установить referredBy, // если они еще не были засчитаны как реферал и пришли по реферальной ссылке if (startParam && !user.referredBy && !user.referralCounted) { - const normalizedStartParam = startParam.toLowerCase(); + const trimmedParam = startParam.trim(); + const normalizedStartParam = trimmedParam.toLowerCase(); + if (normalizedStartParam.startsWith('ref_')) { + // Ищем по полному коду (с префиксом ref_) const referrer = await User.findOne({ - referralCode: { $regex: new RegExp(`^${startParam}$`, 'i') } + referralCode: { $regex: new RegExp(`^${trimmedParam.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i') } }); + if (referrer) { - // Пользователь еще не был засчитан как реферал, можно установить referredBy user.referredBy = referrer._id; await user.save(); + 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}`); + } } } } @@ -259,20 +299,35 @@ const authenticate = async (req, res, next) => { const { getMoscowStartOfDay, getMoscowDate } = require('../utils/moscowTime'); const today = getMoscowDate(); - const todayTime = today.getTime(); + const todayNormalized = getMoscowStartOfDay(today); + const todayTime = todayNormalized.getTime(); // Получить уникальные даты из существующего массива (только даты, без времени по московскому времени) const uniqueDates = new Set(); - user.loginDates.forEach(date => { - const dateObj = getMoscowStartOfDay(new Date(date)); - uniqueDates.add(dateObj.getTime()); - }); + 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); + } + }); + } // Добавить сегодняшнюю дату, если её еще нет const todayExists = uniqueDates.has(todayTime); if (!todayExists) { + if (!user.loginDates) { + user.loginDates = []; + } user.loginDates.push(today); uniqueDates.add(todayTime); + console.log(`📅 Реферал ${user.username} (${user._id}): добавлена дата входа. Уникальных дат: ${uniqueDates.size}/2`); } // Если уже есть 2 или более уникальные даты, засчитать реферала @@ -280,16 +335,24 @@ const authenticate = async (req, res, next) => { const User = require('../models/User'); const referrer = await User.findById(user.referredBy); - if (referrer) { + if (!referrer) { + console.error(`❌ Реферер не найден для пользователя ${user.username} (${user._id}), referredBy: ${user.referredBy}`); + } else { // Увеличить счетчик рефералов - referrer.referralsCount = (referrer.referralsCount || 0) + 1; + const oldCount = referrer.referralsCount || 0; + referrer.referralsCount = oldCount + 1; await referrer.save(); - console.log(`✅ Реферал засчитан: пользователь ${user.username} (${user._id}) засчитан для ${referrer.username} (${referrer._id}). Всего рефералов: ${referrer.referralsCount}`); + console.log(`✅ Реферал засчитан: пользователь ${user.username} (${user._id}) засчитан для ${referrer.username} (${referrer._id}). Счетчик: ${oldCount} -> ${referrer.referralsCount}`); // Начислить баллы за реферала - const { awardReferral } = require('../utils/tickets'); - await awardReferral(user.referredBy); + try { + const { awardReferral } = require('../utils/tickets'); + await awardReferral(user.referredBy); + console.log(` ✅ Баллы начислены рефереру ${referrer.username}`); + } catch (error) { + console.error(` ⚠️ Ошибка начисления баллов:`, error.message); + } } user.referralCounted = true; @@ -299,7 +362,6 @@ const authenticate = async (req, res, next) => { } else { // Если еще нет 2 уникальных дат, сохранить обновленный массив loginDates await user.save(); - console.log(`📅 Реферал ${user.username} (${user._id}): добавлена дата входа. Уникальных дат: ${uniqueDates.size}/2`); } } diff --git a/backend/scripts/checkReferralCodes.mongosh.js b/backend/scripts/checkReferralCodes.mongosh.js new file mode 100644 index 0000000..6ef076b --- /dev/null +++ b/backend/scripts/checkReferralCodes.mongosh.js @@ -0,0 +1,77 @@ +// Команды для проверки и исправления реферальных кодов +// Выполняйте в mongosh + +// ============================================ +// 1. ПРОВЕРИТЬ ПОЛЬЗОВАТЕЛЕЙ БЕЗ REFERRALCODE +// ============================================ +print("Пользователи без referralCode:\n"); +db.users.find({ + referralCode: { $exists: false } +}, { + username: 1, + telegramId: 1, + createdAt: 1 +}).forEach(function(user) { + print(" - " + (user.username || user._id) + " (telegramId: " + user.telegramId + ")"); +}); + +// ============================================ +// 2. ПОСЧИТАТЬ СКОЛЬКО БЕЗ REFERRALCODE +// ============================================ +// db.users.countDocuments({ referralCode: { $exists: false } }) + +// ============================================ +// 3. ПОСМОТРЕТЬ ПРИМЕРЫ REFERRALCODE +// ============================================ +print("\nПримеры referralCode в базе:\n"); +db.users.find({ + referralCode: { $exists: true } +}, { + username: 1, + referralCode: 1 +}).limit(10).forEach(function(user) { + print(" " + (user.username || user._id) + ": " + user.referralCode); +}); + +// ============================================ +// 4. НАЙТИ ПОЛЬЗОВАТЕЛЕЙ С REFERREDBY БЕЗ REFERRALCODE +// ============================================ +print("\nПользователи с referredBy, но без referralCode:\n"); +db.users.find({ + referredBy: { $exists: true, $ne: null }, + referralCode: { $exists: false } +}).forEach(function(user) { + print(" - " + (user.username || user._id) + " -> referredBy: " + user.referredBy); +}); + +// ============================================ +// 5. СГЕНЕРИРОВАТЬ REFERRALCODE ДЛЯ ВСЕХ БЕЗ НЕГО +// ============================================ +db.users.find({ + referralCode: { $exists: false } +}).forEach(function(user) { + // Генерируем код по той же логике, что в модели + var code = "REF_" + user.telegramId.slice(-8) + Math.random().toString(36).substring(2, 6).toUpperCase(); + + // Проверяем уникальность + var exists = db.users.findOne({ referralCode: code }); + var attempts = 0; + while (exists && attempts < 10) { + code = "REF_" + user.telegramId.slice(-8) + Math.random().toString(36).substring(2, 6).toUpperCase(); + exists = db.users.findOne({ referralCode: code }); + attempts++; + } + + if (!exists) { + db.users.updateOne( + { _id: user._id }, + { $set: { referralCode: code } } + ); + print("✅ Сгенерирован код для " + (user.username || user._id) + ": " + code); + } else { + print("⚠️ Не удалось сгенерировать уникальный код для " + (user.username || user._id)); + } +}); + +print("\n✅ Готово!"); + diff --git a/backend/scripts/fixAllReferrals.mongosh.js b/backend/scripts/fixAllReferrals.mongosh.js new file mode 100644 index 0000000..8d1aad3 --- /dev/null +++ b/backend/scripts/fixAllReferrals.mongosh.js @@ -0,0 +1,139 @@ +// Команда для исправления ВСЕХ незасчитанных рефералов +// Выполните в mongosh: load('backend/scripts/fixAllReferrals.mongosh.js') + +print("🔍 Поиск всех пользователей с referredBy...\n"); + +var fixed = 0; +var needMoreDays = 0; +var alreadyCounted = 0; +var noReferrer = 0; +var errors = 0; + +// Функция для получения начала дня по московскому времени +function getMoscowStartOfDay(date) { + if (!date) date = new Date(); + var d = new Date(date); + // Получаем UTC timestamp + var utcTimestamp = d.getTime(); + // Добавляем московское смещение (UTC+3) + var moscowOffset = 3 * 60 * 60 * 1000; + var moscowTime = new Date(utcTimestamp + moscowOffset); + + // Устанавливаем начало дня + var year = moscowTime.getUTCFullYear(); + var month = moscowTime.getUTCMonth(); + var day = moscowTime.getUTCDate(); + + var moscowStartOfDay = new Date(Date.UTC(year, month, day, 0, 0, 0, 0)); + return new Date(moscowStartOfDay.getTime() - moscowOffset); +} + +// Найти всех пользователей с referredBy +db.users.find({ + referredBy: { $exists: true, $ne: null } +}).forEach(function(user) { + try { + // Пропускаем уже засчитанных + if (user.referralCounted === true) { + alreadyCounted++; + return; + } + + // Получить реферера + var referrer = db.users.findOne({ _id: user.referredBy }); + + if (!referrer) { + print("⚠️ " + (user.username || user._id) + " -> реферер не найден (ID: " + user.referredBy + ")\n"); + noReferrer++; + return; + } + + // Получить уникальные даты + var uniqueDates = new Set(); + if (user.loginDates && Array.isArray(user.loginDates)) { + user.loginDates.forEach(function(date) { + if (!date) return; + try { + var normalizedDate = getMoscowStartOfDay(new Date(date)); + uniqueDates.add(normalizedDate.getTime()); + } catch (e) { + // Игнорируем ошибки парсинга дат + } + }); + } + + var uniqueCount = uniqueDates.size; + + // Если есть 2+ уникальные даты, засчитать + if (uniqueCount >= 2) { + var currentCount = referrer.referralsCount || 0; + var newCount = currentCount + 1; + + // Увеличить счетчик рефералов + db.users.updateOne( + { _id: referrer._id }, + { $set: { referralsCount: newCount } } + ); + + // Пометить как засчитанного + db.users.updateOne( + { _id: user._id }, + { + $set: { + referralCounted: true, + loginDates: [] + } + } + ); + + print("✅ ЗАСЧИТАН: " + (user.username || user._id) + " -> " + (referrer.username || referrer._id) + " (счетчик: " + currentCount + " -> " + newCount + ")\n"); + fixed++; + } else if (uniqueCount > 0) { + print("⏳ " + (user.username || user._id) + " -> нужно еще " + (2 - uniqueCount) + " дат (сейчас: " + uniqueCount + ")\n"); + needMoreDays++; + } else { + // Если нет дат входа, но пользователь существует более суток - засчитать как исключение + var userAge = new Date() - new Date(user.createdAt || user._id.getTimestamp()); + var oneDay = 24 * 60 * 60 * 1000; + + if (userAge > oneDay) { + // Пользователь существует более суток, засчитываем + var currentCount = referrer.referralsCount || 0; + var newCount = currentCount + 1; + + db.users.updateOne( + { _id: referrer._id }, + { $set: { referralsCount: newCount } } + ); + + db.users.updateOne( + { _id: user._id }, + { + $set: { + referralCounted: true, + loginDates: [] + } + } + ); + + print("✅ ЗАСЧИТАН (по возрасту >24ч): " + (user.username || user._id) + " -> " + (referrer.username || referrer._id) + " (счетчик: " + currentCount + " -> " + newCount + ")\n"); + fixed++; + } else { + print("📅 " + (user.username || user._id) + " -> нет дат входа, возраст: " + Math.round(userAge / (60 * 60 * 1000)) + " часов\n"); + needMoreDays++; + } + } + } catch (error) { + print("❌ Ошибка для " + (user.username || user._id) + ": " + error.message + "\n"); + errors++; + } +}); + +print("\n📊 ИТОГО:"); +print(" ✅ Засчитано: " + fixed); +print(" ✅ Уже были засчитаны: " + alreadyCounted); +print(" ⏳ Требуется больше дней: " + needMoreDays); +print(" ⚠️ Реферер не найден: " + noReferrer); +print(" ❌ Ошибок: " + errors); +print("\n✅ Готово!"); + diff --git a/backend/scripts/fixReferrals-commands.txt b/backend/scripts/fixReferrals-commands.txt index c41e659..13d0039 100644 --- a/backend/scripts/fixReferrals-commands.txt +++ b/backend/scripts/fixReferrals-commands.txt @@ -1,6 +1,15 @@ // Команды mongosh для исправления незасчитанных рефералов // Выполняйте по порядку +// ============================================ +// БЫСТРАЯ КОМАНДА ДЛЯ ИСПРАВЛЕНИЯ ВСЕХ РЕФЕРАЛОВ +// ============================================ +// Скопируйте и выполните эту команду - она засчитает всех, у кого есть 2+ даты или возраст >24ч +// +// load('backend/scripts/fixAllReferrals.mongosh.js') +// +// ИЛИ выполните команды ниже вручную + // ============================================ // 1. НАЙТИ ВСЕХ ПОЛЬЗОВАТЕЛЕЙ С REFERREDBY (независимо от referralCounted) // ============================================ diff --git a/backend/scripts/giveReferrals-simple.txt b/backend/scripts/giveReferrals-simple.txt new file mode 100644 index 0000000..9fa9242 --- /dev/null +++ b/backend/scripts/giveReferrals-simple.txt @@ -0,0 +1,45 @@ +// ПРОСТАЯ КОМАНДА ДЛЯ ВЫДАЧИ 2 РЕФЕРАЛОВ И 2 БИЛЕТОВ +// Замените "USERNAME" на username пользователя + +var username = "USERNAME"; +var user = db.users.findOne({ username: username }); + +if (user) { + var newReferrals = (user.referralsCount || 0) + 2; + // 100 билетов за каждого реферала (2 реферала = 200 билетов) + var newTickets = (user.tickets || 0) + (2 * 100); + + db.users.updateOne( + { _id: user._id }, + { + $set: { + referralsCount: newReferrals, + tickets: newTickets + } + } + ); + + print("✅ Выдано: " + username); + print(" Рефералы: " + (user.referralsCount || 0) + " -> " + newReferrals); + print(" Билеты: " + (user.tickets || 0) + " -> " + newTickets); +} else { + print("❌ Пользователь не найден: " + username); +} + +// ============================================ +// ДЛЯ НЕСКОЛЬКИХ ПОЛЬЗОВАТЕЛЕЙ +// ============================================ +// ["username1", "username2", "username3"].forEach(function(username) { +// var user = db.users.findOne({ username: username }); +// if (user) { +// var newReferrals = (user.referralsCount || 0) + 2; +// // 100 билетов за каждого реферала (2 реферала = 200 билетов) +// var newTickets = (user.tickets || 0) + (2 * 100); +// db.users.updateOne( +// { _id: user._id }, +// { $set: { referralsCount: newReferrals, tickets: newTickets } } +// ); +// print("✅ " + username + ": рефералы " + newReferrals + ", билеты " + newTickets); +// } +// }); + diff --git a/backend/scripts/giveReferrals.mongosh.js b/backend/scripts/giveReferrals.mongosh.js new file mode 100644 index 0000000..dee30a4 --- /dev/null +++ b/backend/scripts/giveReferrals.mongosh.js @@ -0,0 +1,82 @@ +// Скрипт для выдачи рефералов и билетов по username +// Использование: +// var username = "USERNAME"; +// load('backend/scripts/giveReferrals.mongosh.js') + +// ============================================ +// ВАРИАНТ 1: УКАЗАТЬ USERNAME В СКРИПТЕ +// ============================================ +var username = "ЗАМЕНИТЕ_НА_USERNAME"; + +var user = db.users.findOne({ username: username }); + +if (!user) { + print("❌ Пользователь с username '" + username + "' не найден!"); +} else { + print("👤 Пользователь: " + user.username + " (ID: " + user._id + ")"); + print(" Текущий счетчик рефералов: " + (user.referralsCount || 0)); + print(" Текущие билеты: " + (user.tickets || 0)); + + // Увеличить счетчик рефералов на 2 + var newReferralsCount = (user.referralsCount || 0) + 2; + + // Увеличить билеты: 100 билетов за каждого реферала (2 реферала = 200 билетов) + var newTickets = (user.tickets || 0) + (2 * 100); + + var result = db.users.updateOne( + { _id: user._id }, + { + $set: { + referralsCount: newReferralsCount, + tickets: newTickets + } + } + ); + + if (result.modifiedCount > 0) { + print("\n✅ УСПЕШНО!"); + print(" Рефералы: " + (user.referralsCount || 0) + " -> " + newReferralsCount); + print(" Билеты: " + (user.tickets || 0) + " -> " + newTickets); + } else { + print("\n⚠️ Не удалось обновить данные пользователя"); + } +} + +// ============================================ +// ВАРИАНТ 2: ФУНКЦИЯ ДЛЯ ПОВТОРНОГО ИСПОЛЬЗОВАНИЯ +// ============================================ +function giveReferrals(username, referralsCount, ticketsCount) { + var user = db.users.findOne({ username: username }); + + if (!user) { + print("❌ Пользователь '" + username + "' не найден!"); + return false; + } + + var newReferralsCount = (user.referralsCount || 0) + (referralsCount || 0); + // 100 билетов за каждого реферала + var ticketsToAdd = (referralsCount || 0) * 100; + var newTickets = (user.tickets || 0) + ticketsToAdd; + + var result = db.users.updateOne( + { _id: user._id }, + { + $set: { + referralsCount: newReferralsCount, + tickets: newTickets + } + } + ); + + if (result.modifiedCount > 0) { + print("✅ " + username + ": рефералы " + (user.referralsCount || 0) + " -> " + newReferralsCount + ", билеты " + (user.tickets || 0) + " -> " + newTickets); + return true; + } + + return false; +} + +// Использование функции: +// giveReferrals("username", 2, 2); // Выдать 2 реферала и 2 билета +// giveReferrals("username", 5, 10); // Выдать 5 рефералов и 10 билетов + diff --git a/backend/utils/moscowTime.js b/backend/utils/moscowTime.js index bc94de3..a348f3c 100644 --- a/backend/utils/moscowTime.js +++ b/backend/utils/moscowTime.js @@ -16,11 +16,35 @@ function getMoscowTime() { /** * Получить начало дня по московскому времени + * Принимает дату (может быть в UTC или локальном времени) + * Возвращает дату с началом дня по московскому времени (UTC+3) */ function getMoscowStartOfDay(date = null) { - const moscowTime = date ? new Date(date.getTime()) : getMoscowTime(); - moscowTime.setHours(0, 0, 0, 0); - return moscowTime; + let moscowTime; + + if (date) { + // Если дата передана, конвертируем её в московское время + const dateObj = new Date(date); + // Получаем UTC timestamp + const utcTimestamp = dateObj.getTime(); + // Добавляем московское смещение (UTC+3) + const moscowOffset = 3 * 60 * 60 * 1000; + moscowTime = new Date(utcTimestamp + moscowOffset); + } else { + moscowTime = getMoscowTime(); + } + + // Устанавливаем начало дня (00:00:00) в московском времени + // Используем UTC методы для правильной работы с часовыми поясами + const year = moscowTime.getUTCFullYear(); + const month = moscowTime.getUTCMonth(); + const day = moscowTime.getUTCDate(); + + // Создаем новую дату с началом дня в московском времени + // Создаем UTC дату для нужного дня, затем вычитаем 3 часа для получения правильного UTC timestamp + const moscowStartOfDay = new Date(Date.UTC(year, month, day, 0, 0, 0, 0)); + const moscowOffset = 3 * 60 * 60 * 1000; + return new Date(moscowStartOfDay.getTime() - moscowOffset); } /** diff --git a/frontend/src/components/CreatePostModal.jsx b/frontend/src/components/CreatePostModal.jsx index 0c40c23..b543e7e 100644 --- a/frontend/src/components/CreatePostModal.jsx +++ b/frontend/src/components/CreatePostModal.jsx @@ -118,16 +118,16 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI // Создаем превью для каждого файла const previewPromises = filesToAdd.map((file, index) => { return new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onloadend = () => { + const reader = new FileReader() + reader.onloadend = () => { resolve({ index, result: reader.result }) } reader.onerror = (error) => { console.error('Error reading file:', error, file.name) reject(new Error(`Ошибка чтения файла "${file.name}"`)) - } - reader.readAsDataURL(file) - }) + } + reader.readAsDataURL(file) + }) }) // Ждем загрузки всех превью, затем обновляем состояние @@ -412,21 +412,21 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI
{tagSuggestions.length > 0 ? ( tagSuggestions.map(tag => ( -
handleTagSuggestionClick(tag)} - > -
{tag.name}
- {tag.category && ( -
- {TAG_CATEGORIES[tag.category] || tag.category} -
- )} - {tag.description && ( -
{tag.description}
- )} -
+
handleTagSuggestionClick(tag)} + > +
{tag.name}
+ {tag.category && ( +
+ {TAG_CATEGORIES[tag.category] || tag.category} +
+ )} + {tag.description && ( +
{tag.description}
+ )} +
)) ) : ( tagInput.trim().length > 0 && ( @@ -486,7 +486,7 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI {/* Футер с действиями */}
-