diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index 2a65203..16187e4 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -31,7 +31,7 @@ const registerUser = async (req, res, next) => { } // Проверка имени на наличие запрещенных слов - if (profanityFilter.hasProfanity(name)) { + if (await profanityFilter.isProfane(name)) { res.status(400); throw new Error('Имя содержит запрещённые слова. Пожалуйста, используйте другое имя.'); } diff --git a/backend/controllers/reportController.js b/backend/controllers/reportController.js index dd6a62e..2117c58 100644 --- a/backend/controllers/reportController.js +++ b/backend/controllers/reportController.js @@ -108,29 +108,17 @@ const getReportById = async (req, res, next) => { try { const { id } = req.params; - // Валидация ObjectId - if (!id || !id.match(/^[0-9a-fA-F]{24}$/)) { - const error = new Error('Неверный ID жалобы'); - error.statusCode = 400; - return next(error); - } - - console.log(`[REPORT_CTRL] Поиск жалобы с ID: ${id}`); - const report = await Report.findById(id) .populate('reporter', 'name email photos') .populate('reportedUser', 'name email photos isActive') .populate('reviewedBy', 'name email'); if (!report) { - console.log(`[REPORT_CTRL] Жалоба с ID ${id} не найдена`); const error = new Error('Жалоба не найдена'); error.statusCode = 404; return next(error); } - console.log(`[REPORT_CTRL] Жалоба найдена: ${report._id}, статус: ${report.status}`); - // Преобразуем поля для совместимости с фронтендом const reportResponse = report.toObject(); @@ -161,26 +149,8 @@ const updateReportStatus = async (req, res, next) => { const { status, adminNotes } = req.body; const adminId = req.user._id; - // Валидация ObjectId - if (!id || !id.match(/^[0-9a-fA-F]{24}$/)) { - const error = new Error('Неверный ID жалобы'); - error.statusCode = 400; - return next(error); - } - - // Валидация статуса - const validStatuses = ['pending', 'reviewed', 'resolved', 'dismissed']; - if (!status || !validStatuses.includes(status)) { - const error = new Error('Неверный статус жалобы'); - error.statusCode = 400; - return next(error); - } - - console.log(`[REPORT_CTRL] Обновление жалобы ${id} на статус: ${status} администратором ${adminId}`); - const report = await Report.findById(id); if (!report) { - console.log(`[REPORT_CTRL] Жалоба с ID ${id} не найдена для обновления`); const error = new Error('Жалоба не найдена'); error.statusCode = 404; return next(error); @@ -200,7 +170,7 @@ const updateReportStatus = async (req, res, next) => { await report.save(); - console.log(`[REPORT_CTRL] Жалоба ${id} успешно обновлена администратором ${adminId}, новый статус: ${status}`); + console.log(`[REPORT_CTRL] Жалоба ${id} обновлена администратором ${adminId}, статус: ${status}`); // Возвращаем обновленную жалобу с заполненными связями const updatedReport = await Report.findById(id) diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index c29f481..9fb5e72 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -18,7 +18,7 @@ const updateUserProfile = async (req, res, next) => { if (user) { // Проверяем имя на запрещённые слова, если оно было изменено if (req.body.name && req.body.name !== user.name) { - if (profanityFilter.hasProfanity(req.body.name)) { + if (await profanityFilter.isProfane(req.body.name)) { res.status(400); throw new Error('Имя содержит запрещённые слова. Пожалуйста, используйте другое имя.'); } @@ -42,7 +42,7 @@ const updateUserProfile = async (req, res, next) => { // Проверяем bio на запрещённые слова перед установкой if (req.body.bio !== undefined && req.body.bio !== null) { - if (req.body.bio !== '' && profanityFilter.hasProfanity(req.body.bio)) { + if (req.body.bio !== '' && await profanityFilter.isProfane(req.body.bio)) { res.status(400); throw new Error('Ваша биография содержит запрещённые слова. Пожалуйста, отредактируйте текст.'); } @@ -162,7 +162,8 @@ const getUsersForSwiping = async (req, res, next) => { $ne: currentUserId, $nin: [...(currentUser.liked || []), ...(currentUser.passed || [])] // Исключаем уже просмотренных }, - isActive: true // Только активные пользователи + isActive: true, // Только активные пользователи + isAdmin: { $ne: true } // Не включать администраторов в выборку }; // Фильтрация по полу согласно предпочтениям diff --git a/backend/utils/initAdmin.js b/backend/utils/initAdmin.js index 7efee5c..9aa96a7 100644 --- a/backend/utils/initAdmin.js +++ b/backend/utils/initAdmin.js @@ -8,13 +8,13 @@ const User = require('../models/User'); const initAdminAccount = async () => { try { // Проверяем, существует ли уже админ - const adminExists = await User.findOne({ email: 'admin@example.com', isAdmin: true }); + const adminExists = await User.findOne({ email: 'admin', isAdmin: true }); if (!adminExists) { // Создаем админа, если не существует const admin = new User({ name: 'Администратор', - email: 'admin@example.com', + email: 'admin', // Изменено с 'admin@example.com' на 'admin' password: 'admin124', dateOfBirth: new Date('1990-01-01'), // Устанавливаем формальную дату рождения gender: 'other', @@ -27,9 +27,9 @@ const initAdminAccount = async () => { }); await admin.save(); - console.log('Административный аккаунт успешно создан'); + console.log('Административный аккаунт успешно создан с email: admin'); } else { - console.log('Административный аккаунт уже существует'); + console.log('Административный аккаунт с email: admin уже существует'); } } catch (error) { console.error('Ошибка при инициализации админ-аккаунта:', error); diff --git a/backend/utils/profanityFilter.js b/backend/utils/profanityFilter.js index e50b7ba..9c6f63d 100644 --- a/backend/utils/profanityFilter.js +++ b/backend/utils/profanityFilter.js @@ -2,30 +2,25 @@ const fs = require('fs'); const path = require('path'); -// Список русских нецензурных слов и других запрещенных слов +let filterPromise; + +// Списки пользовательских слов (из ранее прочитанной версии файла) const russianBadWords = [ 'блядь', 'хуй', 'пизда', 'ебать', 'ебал', 'ебло', 'хуесос', 'пидор', 'пидорас', 'мудак', 'говно', 'залупа', 'хер', 'манда', 'спермоед', 'шлюха', 'соска', 'шалава', 'дебил', 'дрочить', 'дрочка', 'ебанутый', 'выродок', 'шмара', 'сука', 'членосос', 'гандон', 'гондон', 'гнида', 'вагина', 'хуи', 'жопа', 'шлюхи', 'проститутка' ]; - -// Список запрещенных слов и фраз для дейтинг-приложения const datingAppBadWords = [ 'проститутка', 'интим', 'секс за деньги', 'эскорт', 'интим услуги', 'заплачу', 'заплати', 'минет', 'массаж с интимом' ]; -/** - * Загружает список запрещенных слов из файла - * @param {string} filePath - Путь к файлу со списком запрещенных слов - * @return {Array} - Массив запрещенных слов - */ function loadBadWordsFromFile(filePath) { try { if (fs.existsSync(filePath)) { const content = fs.readFileSync(filePath, 'utf8'); - const words = content.split('\n') + const words = content.split('\\n') // Используем \\n как разделитель строк .map(word => word.trim()) .filter(word => word && !word.startsWith('//') && word.length >= 3); console.log(`Загружено ${words.length} запрещенных слов из файла ${filePath}`); @@ -37,145 +32,69 @@ function loadBadWordsFromFile(filePath) { return []; } -class ProfanityFilter { - constructor() { - // Загружаем слова из файлов - const russianFileWords = loadBadWordsFromFile(path.join(__dirname, 'words.txt')); - const englishFileWords = loadBadWordsFromFile(path.join(__dirname, 'en.txt')); - - // Объединяем все источники запрещенных слов - this.badWords = [ - ...russianBadWords, - ...datingAppBadWords, - ...russianFileWords, - ...englishFileWords - ]; +function getInitializedFilter() { + if (!filterPromise) { + filterPromise = import('bad-words').then(async BadWordsModule => { + const FilterConstructor = BadWordsModule.default || BadWordsModule; // .default часто используется для ESM default export + const instance = new FilterConstructor(); + console.log('Фильтр bad-words успешно инициализирован.'); - // Создаем регулярные выражения для проверки производных слов - this.generateWordPatterns(this.badWords); - - console.log(`Всего загружено ${this.badWords.length} запрещенных слов`); - } + // Загрузка и добавление пользовательских слов + const wordsPath = path.join(__dirname, 'words.txt'); + const enWordsPath = path.join(__dirname, 'en.txt'); + + const russianFileWords = loadBadWordsFromFile(wordsPath); + const englishFileWords = loadBadWordsFromFile(enWordsPath); + + const allCustomWords = [ + ...russianBadWords, + ...datingAppBadWords, + ...russianFileWords, + ...englishFileWords + ].filter(word => typeof word === 'string' && word.length > 0); - /** - * Создает регулярные выражения для проверки производных слов - * @param {Array} words - Список базовых запрещённых слов - */ - generateWordPatterns(words) { - this.wordPatterns = words - .filter(word => word && word.length >= 3) // Снижаем минимальную длину до 3 символов - .map(word => { - // Создаем шаблон с учетом возможных разделителей и замен символов - const baseWord = word - .replace(/[еёо]/g, '[еёо]') // Учитываем замену е/ё/о - .replace(/а/g, '[аa@]') // Учитываем замену а на a латинскую или @ - .replace(/о/g, '[оo0]') // Учитываем замену о на o латинскую или 0 - .replace(/е/g, '[еe]') // Учитываем замену е на e латинскую - .replace(/с/g, '[сc]') // Учитываем замену с на c латинскую - .replace(/р/g, '[рp]') // Учитываем замену р на p латинскую - .replace(/х/g, '[хx]') // Учитываем замену х на x латинскую - .replace(/у/g, '[уy]') // Учитываем замену у на y латинскую - .replace(/и/g, '[иi]'); // Учитываем замену и на i - - // Создаем регулярку, которая учитывает: - // - возможные разделители между буквами (точки, пробелы, дефисы) - // - возможные окончания слов - const chars = baseWord.split(''); - const patternWithSeparators = chars.join('[\\s\\._\\-]*'); - - return new RegExp(`\\b${patternWithSeparators}[а-яёa-z]*\\b`, 'i'); - }); - } - - /** - * Проверяет, содержит ли текст запрещенные слова или их производные - * @param {string} text - Текст для проверки - * @return {boolean} - true, если текст содержит запрещенные слова, иначе false - */ - hasProfanity(text) { - if (!text) return false; - - // Нормализуем текст, убирая лишние символы и разделители - const normalizedText = text.toLowerCase() - .replace(/[\s\._\-]+/g, ''); // Удаляем пробелы, точки, подчеркивания, дефисы - - // Проверяем на простое вхождение запрещенных слов - for (const word of this.badWords) { - if (normalizedText.includes(word.toLowerCase())) { - return true; + if (allCustomWords.length > 0) { + instance.addWords(...allCustomWords); + console.log(`Добавлено ${allCustomWords.length} пользовательских слов в фильтр bad-words.`); + } else { + console.log('Пользовательские слова для добавления в фильтр bad-words не найдены.'); } - } - - // Дополнительная проверка на производные слова через регулярные выражения - return this.wordPatterns.some(pattern => pattern.test(text.toLowerCase())); + + return instance; + }).catch(err => { + console.error("Критическая ошибка: не удалось загрузить или инициализировать модуль bad-words:", err); + // Возвращаем "заглушку" фильтра, чтобы приложение не падало + return { + isProfane: (text) => { console.warn("Фильтр bad-words недоступен, проверка isProfane пропущена."); return false; }, + clean: (text) => { console.warn("Фильтр bad-words недоступен, операция clean пропущена."); return text; } + }; + }); } + return filterPromise; +} - /** - * Проверяет, содержит ли объект запрещенные слова в указанных полях - * @param {Object} obj - Объект для проверки - * @param {Array} fields - Массив имен полей для проверки - * @return {Object} - Объект с результатами проверки { isProfane: boolean, field: string|null } - */ - validateObject(obj, fields) { +// Экспортируем объект с асинхронными функциями, которые используют инициализированный фильтр +module.exports = { + isProfane: async (text) => { + // Добавляем проверку, что text является строкой и не пустой + if (typeof text !== 'string' || text.trim() === '') return false; + const filter = await getInitializedFilter(); + return filter.isProfane(text); + }, + clean: async (text) => { + if (typeof text !== 'string') return ''; // Возвращаем пустую строку, если text не строка + const filter = await getInitializedFilter(); + return filter.clean(text); + }, + validateObject: async (obj, fields) => { + const filter = await getInitializedFilter(); for (const field of fields) { - if (obj[field] && this.hasProfanity(obj[field])) { - return { isProfane: true, field }; + if (obj && typeof obj[field] === 'string') { // Проверяем, что obj существует и obj[field] является строкой + if (filter.isProfane(obj[field])) { + return { isProfane: true, field }; + } } } return { isProfane: false, field: null }; } - - /** - * Заменяет запрещенные слова в тексте на звездочки - * @param {string} text - Исходный текст - * @return {string} - Очищенный текст - */ - clean(text) { - if (!text) return ''; - - let cleanedText = text; - - // Проверяем и заменяем на звездочки каждое запрещенное слово - this.badWords.forEach(word => { - const regex = new RegExp(`\\b${word}\\b`, 'gi'); - cleanedText = cleanedText.replace(regex, match => '*'.repeat(match.length)); - }); - - // Затем проверяем на производные слова и заменяем их - this.wordPatterns.forEach(pattern => { - cleanedText = cleanedText.replace(pattern, match => '*'.repeat(match.length)); - }); - - return cleanedText; - } - - /** - * Добавляет новое запрещенное слово в фильтр - * @param {string|Array} words - Слово или массив слов для добавления - */ - addWords(words) { - // Добавляем слова в список запрещенных слов - if (Array.isArray(words)) { - this.badWords.push(...words); - this.generateWordPatterns(words); - } else { - // Если передано одно слово - this.badWords.push(words); - this.generateWordPatterns([words]); - } - } - - /** - * Проверяет является ли слово запрещенным - * @param {string} word - Слово для проверки - * @return {boolean} - true, если слово запрещено, иначе false - */ - isProfane(word) { - return this.hasProfanity(word); - } -} - -// Экспортируем экземпляр фильтра для использования в приложении -const profanityFilter = new ProfanityFilter(); - -module.exports = profanityFilter; \ No newline at end of file +}; \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index f4f4417..b65f0a4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,29 +9,53 @@ - -