Update files

This commit is contained in:
glpshchn 2025-12-08 17:37:25 +03:00
parent ca7c508e57
commit d0ae307cd8
4 changed files with 246 additions and 43 deletions

View File

@ -257,43 +257,49 @@ const authenticate = async (req, res, next) => {
user.loginDates = [];
}
const { getMoscowStartOfDay, getMoscowDate } = require('../utils/moscowTime');
const today = getMoscowDate();
const todayTime = today.getTime();
// Получить уникальные даты из существующего массива (только даты, без времени по московскому времени)
const { getMoscowStartOfDay } = require('../utils/moscowTime');
const uniqueDates = new Set();
user.loginDates.forEach(date => {
const dateObj = getMoscowStartOfDay(new Date(date));
uniqueDates.add(dateObj.getTime());
});
// Если уже есть 2 уникальные даты, сразу засчитать реферал без добавления новой даты
// Добавить сегодняшнюю дату, если её еще нет
const todayExists = uniqueDates.has(todayTime);
if (!todayExists) {
user.loginDates.push(today);
uniqueDates.add(todayTime);
}
// Если уже есть 2 или более уникальные даты, засчитать реферала
if (uniqueDates.size >= 2) {
const User = require('../models/User');
await User.findByIdAndUpdate(user.referredBy, {
$inc: { referralsCount: 1 }
});
const referrer = await User.findById(user.referredBy);
// Начислить баллы за реферала
const { awardReferral } = require('../utils/tickets');
await awardReferral(user.referredBy);
if (referrer) {
// Увеличить счетчик рефералов
referrer.referralsCount = (referrer.referralsCount || 0) + 1;
await referrer.save();
console.log(`✅ Реферал засчитан: пользователь ${user.username} (${user._id}) засчитан для ${referrer.username} (${referrer._id}). Всего рефералов: ${referrer.referralsCount}`);
// Начислить баллы за реферала
const { awardReferral } = require('../utils/tickets');
await awardReferral(user.referredBy);
}
user.referralCounted = true;
// Очистить loginDates после засчета, чтобы не хранить лишние данные
user.loginDates = [];
await user.save();
} else {
// Если еще нет 2 уникальных дат, добавить сегодняшнюю дату по московскому времени (если её нет)
const { getMoscowDate } = require('../utils/moscowTime');
const today = getMoscowDate();
const todayTime = today.getTime();
// Проверить, есть ли уже сегодняшняя дата
const todayExists = uniqueDates.has(todayTime);
// Если сегодняшней даты нет, добавить её
if (!todayExists) {
user.loginDates.push(today);
await user.save();
}
// Если еще нет 2 уникальных дат, сохранить обновленный массив loginDates
await user.save();
console.log(`📅 Реферал ${user.username} (${user._id}): добавлена дата входа. Уникальных дат: ${uniqueDates.size}/2`);
}
}

View File

@ -28,14 +28,31 @@ const multerConfig = {
return cb(new Error('Запрещенный тип файла'));
}
// Разрешенные типы изображений и видео
// Разрешенные типы изображений и видео (расширенный список)
const allowedMimes = [
// Основные форматы изображений
'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp',
'video/mp4', 'video/quicktime', 'video/x-msvideo'
// Дополнительные форматы изображений
'image/bmp', 'image/x-ms-bmp', 'image/tiff', 'image/tif',
'image/heic', 'image/heif', 'image/avif', 'image/x-icon',
// Видео форматы
'video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/webm',
'video/x-matroska', 'video/avi'
];
if (!allowedMimes.includes(file.mimetype)) {
return cb(new Error('Только изображения и видео разрешены'));
// Также проверяем по расширению файла (на случай неправильного MIME типа)
const allowedExts = [
'.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.tif',
'.heic', '.heif', '.avif', '.ico',
'.mp4', '.mov', '.avi', '.webm', '.mkv'
];
const fileExt = ext.toLowerCase();
const isValidMime = allowedMimes.includes(file.mimetype);
const isValidExt = allowedExts.includes(fileExt);
if (!isValidMime && !isValidExt) {
return cb(new Error(`Неподдерживаемый формат файла. Разрешены: JPEG, PNG, GIF, WebP, BMP, HEIC, MP4, MOV, AVI, WebM. Получен: ${file.mimetype || 'неизвестно'}`));
}
cb(null, true);

View File

@ -0,0 +1,99 @@
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const path = require('path');
// Загрузить переменные окружения
dotenv.config({ path: path.join(__dirname, '../.env') });
const User = require('../models/User');
const { getMoscowStartOfDay } = require('../utils/moscowTime');
const { awardReferral } = require('../utils/tickets');
async function fixReferrals() {
try {
const mongoUri = process.env.MONGO_URI || 'mongodb://localhost:27017/nakama';
await mongoose.connect(mongoUri);
console.log('✅ Подключено к MongoDB');
// Найти всех пользователей, у которых есть referredBy, но они еще не засчитаны
const usersWithReferrals = await User.find({
referredBy: { $exists: true, $ne: null },
referralCounted: { $ne: true }
}).populate('referredBy', 'username referralCode referralsCount');
console.log(`\n📊 Найдено ${usersWithReferrals.length} пользователей с незасчитанными рефералами\n`);
let fixed = 0;
let needMoreDays = 0;
for (const user of usersWithReferrals) {
if (!user.referredBy) {
console.log(`⚠️ Пользователь ${user.username} (${user._id}) имеет referredBy, но реферер не найден`);
continue;
}
// Инициализировать loginDates если его нет
if (!user.loginDates || user.loginDates.length === 0) {
console.log(`📅 Пользователь ${user.username} (${user._id}) не имеет дат входа. Пропускаем.`);
needMoreDays++;
continue;
}
// Получить уникальные даты из массива loginDates
const uniqueDates = new Set();
user.loginDates.forEach(date => {
const dateObj = getMoscowStartOfDay(new Date(date));
uniqueDates.add(dateObj.getTime());
});
console.log(`👤 ${user.username} (${user._id})`);
console.log(` Реферер: ${user.referredBy.username} (${user.referredBy._id})`);
console.log(` Уникальных дат входа: ${uniqueDates.size}`);
console.log(` Даты: ${Array.from(uniqueDates).map(t => new Date(t).toLocaleDateString('ru-RU')).join(', ')}`);
// Если есть 2 или более уникальные даты, засчитать реферала
if (uniqueDates.size >= 2) {
const referrer = user.referredBy;
// Увеличить счетчик рефералов
referrer.referralsCount = (referrer.referralsCount || 0) + 1;
await referrer.save();
console.log(` ✅ РЕФЕРАЛ ЗАСЧИТАН! Новый счетчик: ${referrer.referralsCount}`);
// Начислить баллы за реферала
try {
await awardReferral(referrer._id);
console.log(` ✅ Баллы начислены`);
} catch (error) {
console.error(` ⚠️ Ошибка начисления баллов:`, error.message);
}
// Пометить как засчитанный
user.referralCounted = true;
user.loginDates = [];
await user.save();
fixed++;
} else {
console.log(` ⏳ Нужно еще ${2 - uniqueDates.size} уникальных дат входа`);
needMoreDays++;
}
console.log('');
}
console.log(`\n📊 Итого:`);
console.log(` ✅ Засчитано рефералов: ${fixed}`);
console.log(` ⏳ Требуется больше дней: ${needMoreDays}`);
await mongoose.connection.close();
console.log('✅ Готово!');
} catch (error) {
console.error('❌ Ошибка:', error);
process.exit(1);
}
}
fixReferrals();

View File

@ -72,32 +72,103 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI
}, [])
const handleImageSelect = (e) => {
const files = Array.from(e.target.files)
if (files.length === 0) return
const files = Array.from(e.target.files || [])
if (files.length === 0) {
console.warn('No files selected')
return
}
const remainingSlots = 5 - images.length
const filesToAdd = files.slice(0, remainingSlots)
filesToAdd.forEach(file => {
const reader = new FileReader()
reader.onloadend = () => {
setImagePreviews(prev => [...prev, reader.result])
// Валидация файлов на фронтенде
const validFiles = files.filter(file => {
// Проверка типа файла
const validTypes = [
'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp',
'image/bmp', 'image/tiff', 'image/heic', 'image/heif', 'image/avif',
'video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/webm'
]
if (!validTypes.includes(file.type)) {
console.warn('Invalid file type:', file.type, file.name)
alert(`Файл "${file.name}" имеет неподдерживаемый формат. Разрешены: JPEG, PNG, GIF, WebP, BMP, HEIC, MP4, MOV, AVI, WebM`)
return false
}
reader.readAsDataURL(file)
// Проверка размера (10MB)
const maxSize = 10 * 1024 * 1024
if (file.size > maxSize) {
console.warn('File too large:', file.name, file.size)
alert(`Файл "${file.name}" слишком большой. Максимальный размер: 10MB`)
return false
}
return true
})
setImages(prev => [...prev, ...filesToAdd])
if (validFiles.length === 0) {
return
}
const remainingSlots = 5 - images.length
const filesToAdd = validFiles.slice(0, remainingSlots)
if (filesToAdd.length < validFiles.length) {
alert(`Можно добавить максимум 5 изображений. Добавлено ${filesToAdd.length} из ${validFiles.length}`)
}
// Создаем превью для каждого файла
const previewPromises = filesToAdd.map((file, index) => {
return new Promise((resolve, reject) => {
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)
})
})
// Ждем загрузки всех превью, затем обновляем состояние
Promise.all(previewPromises)
.then((previewResults) => {
// Сортируем результаты по индексу
previewResults.sort((a, b) => a.index - b.index)
const newPreviews = previewResults.map(r => r.result)
setImagePreviews(prev => [...prev, ...newPreviews])
// Добавляем файлы в массив после успешной загрузки превью
setImages(prev => {
const newImages = [...prev, ...filesToAdd]
console.log('Images after add:', newImages.length, newImages.map(f => ({ name: f.name, type: f.type, size: f.size })))
return newImages
})
})
.catch((error) => {
console.error('Error loading previews:', error)
alert(error.message || 'Ошибка загрузки превью изображений')
})
hapticFeedback('light')
// Очищаем input для возможности повторного выбора того же файла
if (fileInputRef.current) {
fileInputRef.current.value = ''
}
}
const handleRemoveImage = (index) => {
setImages(prev => prev.filter((_, i) => i !== index))
console.log('Removing image at index:', index, 'Total images:', images.length)
setImages(prev => {
const newImages = prev.filter((_, i) => i !== index)
console.log('Images after remove:', newImages.length)
return newImages
})
setImagePreviews(prev => prev.filter((_, i) => i !== index))
setExternalImages(prev => prev.filter((_, i) => i !== index))
hapticFeedback('light')
}
const handleTagInputChange = (e) => {
@ -216,12 +287,22 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI
formData.append('isNSFW', isNSFW)
formData.append('isHomo', isHomo)
images.forEach((image, index) => {
if (image instanceof File) {
formData.append('images', image)
}
// Отправляем все файлы, которые являются File объектами
const fileImages = images.filter(img => img instanceof File)
if (fileImages.length === 0 && images.length > 0) {
console.warn('No File objects found in images array:', images.map(img => typeof img))
}
fileImages.forEach((image, index) => {
formData.append('images', image, image.name)
console.log(`Appending image ${index}:`, image.name, image.type, image.size)
})
console.log('FormData images count:', fileImages.length, 'Total images:', images.length)
// Если есть externalImages (из поиска), добавляем их отдельно
if (externalImages.length > 0) {
formData.append('externalImages', JSON.stringify(externalImages))
}
@ -405,10 +486,10 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI
{/* Футер с действиями */}
<div className="modal-footer">
<input
<input
ref={fileInputRef}
type="file"
accept="image/*"
accept="image/*,video/*,.heic,.heif,.avif"
multiple
onChange={handleImageSelect}
style={{ display: 'none' }}