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

View File

@ -28,14 +28,31 @@ const multerConfig = {
return cb(new Error('Запрещенный тип файла')); return cb(new Error('Запрещенный тип файла'));
} }
// Разрешенные типы изображений и видео // Разрешенные типы изображений и видео (расширенный список)
const allowedMimes = [ const allowedMimes = [
// Основные форматы изображений
'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', '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)) { // Также проверяем по расширению файла (на случай неправильного MIME типа)
return cb(new Error('Только изображения и видео разрешены')); 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); 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 handleImageSelect = (e) => {
const files = Array.from(e.target.files) const files = Array.from(e.target.files || [])
if (files.length === 0) return if (files.length === 0) {
console.warn('No files selected')
return
}
// Валидация файлов на фронтенде
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
}
// Проверка размера (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
})
if (validFiles.length === 0) {
return
}
const remainingSlots = 5 - images.length const remainingSlots = 5 - images.length
const filesToAdd = files.slice(0, remainingSlots) const filesToAdd = validFiles.slice(0, remainingSlots)
filesToAdd.forEach(file => { 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() const reader = new FileReader()
reader.onloadend = () => { reader.onloadend = () => {
setImagePreviews(prev => [...prev, reader.result]) 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)
}) })
})
// Ждем загрузки всех превью, затем обновляем состояние
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 || 'Ошибка загрузки превью изображений')
})
setImages(prev => [...prev, ...filesToAdd])
hapticFeedback('light') hapticFeedback('light')
// Очищаем input для возможности повторного выбора того же файла
if (fileInputRef.current) { if (fileInputRef.current) {
fileInputRef.current.value = '' fileInputRef.current.value = ''
} }
} }
const handleRemoveImage = (index) => { 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)) setImagePreviews(prev => prev.filter((_, i) => i !== index))
setExternalImages(prev => prev.filter((_, i) => i !== index)) setExternalImages(prev => prev.filter((_, i) => i !== index))
hapticFeedback('light')
} }
const handleTagInputChange = (e) => { const handleTagInputChange = (e) => {
@ -216,12 +287,22 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI
formData.append('isNSFW', isNSFW) formData.append('isNSFW', isNSFW)
formData.append('isHomo', isHomo) formData.append('isHomo', isHomo)
images.forEach((image, index) => { // Отправляем все файлы, которые являются File объектами
if (image instanceof File) { const fileImages = images.filter(img => img instanceof File)
formData.append('images', image)
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) { if (externalImages.length > 0) {
formData.append('externalImages', JSON.stringify(externalImages)) formData.append('externalImages', JSON.stringify(externalImages))
} }
@ -408,7 +489,7 @@ export default function CreatePostModal({ user, onClose, onPostCreated, initialI
<input <input
ref={fileInputRef} ref={fileInputRef}
type="file" type="file"
accept="image/*" accept="image/*,video/*,.heic,.heif,.avif"
multiple multiple
onChange={handleImageSelect} onChange={handleImageSelect}
style={{ display: 'none' }} style={{ display: 'none' }}