277 lines
9.0 KiB
JavaScript
277 lines
9.0 KiB
JavaScript
const express = require('express');
|
||
const router = express.Router();
|
||
const { authenticate } = require('../middleware/auth');
|
||
const User = require('../models/User');
|
||
const Post = require('../models/Post');
|
||
const Notification = require('../models/Notification');
|
||
|
||
const ALLOWED_SEARCH_PREFERENCES = ['furry', 'anime'];
|
||
|
||
// Получить профиль пользователя
|
||
router.get('/:id', authenticate, async (req, res) => {
|
||
try {
|
||
const user = await User.findById(req.params.id)
|
||
.select('-__v')
|
||
.populate('followers', 'username firstName lastName photoUrl')
|
||
.populate('following', 'username firstName lastName photoUrl');
|
||
|
||
if (!user) {
|
||
return res.status(404).json({ error: 'Пользователь не найден' });
|
||
}
|
||
|
||
// Проверить подписку текущего пользователя
|
||
const isFollowing = user.followers.some(f => f._id.equals(req.user._id));
|
||
|
||
res.json({
|
||
user: {
|
||
id: user._id,
|
||
username: user.username,
|
||
firstName: user.firstName,
|
||
lastName: user.lastName,
|
||
photoUrl: user.photoUrl,
|
||
bio: user.bio,
|
||
followersCount: user.followers.length,
|
||
followingCount: user.following.length,
|
||
followers: user.followers,
|
||
following: user.following,
|
||
isFollowing,
|
||
createdAt: user.createdAt
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка получения профиля:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Получить посты пользователя
|
||
router.get('/:id/posts', authenticate, async (req, res) => {
|
||
try {
|
||
const { page = 1, limit = 20 } = req.query;
|
||
const query = { author: req.params.id };
|
||
|
||
// Применить фильтры текущего пользователя
|
||
if (req.user.settings?.whitelist?.noNSFW) {
|
||
query.isNSFW = false;
|
||
}
|
||
if (req.user.settings?.whitelist?.noHomo) {
|
||
query.isHomo = { $ne: true };
|
||
}
|
||
|
||
const posts = await Post.find(query)
|
||
.populate('author', 'username firstName lastName photoUrl')
|
||
.sort({ createdAt: -1 })
|
||
.limit(limit * 1)
|
||
.skip((page - 1) * limit)
|
||
.exec();
|
||
|
||
const count = await Post.countDocuments(query);
|
||
|
||
res.json({
|
||
posts,
|
||
totalPages: Math.ceil(count / limit),
|
||
currentPage: page
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка получения постов:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Подписаться / отписаться
|
||
router.post('/:id/follow', authenticate, async (req, res) => {
|
||
try {
|
||
if (req.params.id === req.user._id.toString()) {
|
||
return res.status(400).json({ error: 'Нельзя подписаться на себя' });
|
||
}
|
||
|
||
const targetUser = await User.findById(req.params.id);
|
||
|
||
if (!targetUser) {
|
||
return res.status(404).json({ error: 'Пользователь не найден' });
|
||
}
|
||
|
||
const isFollowing = targetUser.followers.includes(req.user._id);
|
||
|
||
if (isFollowing) {
|
||
// Отписаться
|
||
targetUser.followers = targetUser.followers.filter(id => !id.equals(req.user._id));
|
||
req.user.following = req.user.following.filter(id => !id.equals(targetUser._id));
|
||
} else {
|
||
// Подписаться
|
||
targetUser.followers.push(req.user._id);
|
||
req.user.following.push(targetUser._id);
|
||
|
||
// Создать уведомление
|
||
const notification = new Notification({
|
||
recipient: targetUser._id,
|
||
sender: req.user._id,
|
||
type: 'follow'
|
||
});
|
||
await notification.save();
|
||
}
|
||
|
||
await targetUser.save();
|
||
await req.user.save();
|
||
|
||
res.json({
|
||
following: !isFollowing,
|
||
followersCount: targetUser.followers.length
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка подписки:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Обновить профиль
|
||
router.put('/profile', authenticate, async (req, res) => {
|
||
try {
|
||
const { bio, settings } = req.body;
|
||
|
||
if (bio !== undefined) {
|
||
req.user.bio = bio;
|
||
}
|
||
|
||
if (settings) {
|
||
req.user.settings = req.user.settings || {};
|
||
|
||
if (settings.whitelist) {
|
||
const currentWhitelist =
|
||
req.user.settings.whitelist && typeof req.user.settings.whitelist.toObject === 'function'
|
||
? req.user.settings.whitelist.toObject()
|
||
: { ...(req.user.settings.whitelist || {}) };
|
||
|
||
req.user.settings.whitelist = {
|
||
...currentWhitelist,
|
||
...settings.whitelist
|
||
};
|
||
}
|
||
|
||
if (settings.searchPreference) {
|
||
req.user.settings.searchPreference = ALLOWED_SEARCH_PREFERENCES.includes(settings.searchPreference)
|
||
? settings.searchPreference
|
||
: 'furry';
|
||
}
|
||
|
||
if (!req.user.settings.searchPreference) {
|
||
req.user.settings.searchPreference = 'furry';
|
||
}
|
||
|
||
if (!req.user.settings.whitelist) {
|
||
req.user.settings.whitelist = { noNSFW: true, noHomo: true };
|
||
} else {
|
||
if (req.user.settings.whitelist.noNSFW === undefined) {
|
||
req.user.settings.whitelist.noNSFW = true;
|
||
}
|
||
if (req.user.settings.whitelist.noHomo === undefined) {
|
||
req.user.settings.whitelist.noHomo = true;
|
||
}
|
||
}
|
||
|
||
req.user.markModified('settings');
|
||
}
|
||
|
||
await req.user.save();
|
||
|
||
res.json({
|
||
message: 'Профиль обновлен',
|
||
user: req.user
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка обновления профиля:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Поиск пользователей
|
||
router.get('/search/:query', authenticate, async (req, res) => {
|
||
try {
|
||
const users = await User.find({
|
||
$or: [
|
||
{ username: { $regex: req.params.query, $options: 'i' } },
|
||
{ firstName: { $regex: req.params.query, $options: 'i' } },
|
||
{ lastName: { $regex: req.params.query, $options: 'i' } }
|
||
]
|
||
})
|
||
.select('username firstName lastName photoUrl')
|
||
.limit(10);
|
||
|
||
res.json({ users });
|
||
} catch (error) {
|
||
console.error('Ошибка поиска пользователей:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Получить топ пользователей по билетам (Monthly Ladder)
|
||
router.get('/ladder/top', authenticate, async (req, res) => {
|
||
try {
|
||
const { limit = 5 } = req.query;
|
||
const limitNum = parseInt(limit);
|
||
|
||
// Получить топ пользователей по билетам (исключаем glpshchn00)
|
||
// Берем немного больше, чтобы гарантировать топ 5 после фильтрации
|
||
const topUsers = await User.find({
|
||
banned: { $ne: true },
|
||
username: { $ne: 'glpshchn00' }
|
||
})
|
||
.select('username firstName lastName photoUrl tickets')
|
||
.sort({ tickets: -1 })
|
||
.limit(limitNum)
|
||
.lean();
|
||
|
||
// Найти позицию текущего пользователя (исключаем glpshchn00)
|
||
const userTickets = req.user.tickets || 0;
|
||
const userRank = await User.countDocuments({
|
||
tickets: { $gt: userTickets },
|
||
banned: { $ne: true },
|
||
username: { $ne: 'glpshchn00' }
|
||
}) + 1;
|
||
|
||
// Исключить glpshchn00 из отображения
|
||
const isExcludedUser = req.user.username === 'glpshchn00';
|
||
|
||
// Проверить, есть ли текущий пользователь в топе
|
||
const currentUserInTop = !isExcludedUser && topUsers.some(u => u._id.toString() === req.user._id.toString());
|
||
|
||
// Если пользователь не в топе и не исключен, добавить его отдельно
|
||
let currentUserData = null;
|
||
if (!isExcludedUser && !currentUserInTop) {
|
||
currentUserData = {
|
||
_id: req.user._id,
|
||
username: req.user.username,
|
||
firstName: req.user.firstName,
|
||
lastName: req.user.lastName,
|
||
photoUrl: req.user.photoUrl,
|
||
tickets: req.user.tickets || 0,
|
||
rank: userRank
|
||
};
|
||
} else if (!isExcludedUser && currentUserInTop) {
|
||
// Если в топе, добавить rank к существующему пользователю
|
||
topUsers.forEach((user, index) => {
|
||
if (user._id.toString() === req.user._id.toString()) {
|
||
user.rank = index + 1;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Добавить rank к топ пользователям
|
||
topUsers.forEach((user, index) => {
|
||
user.rank = index + 1;
|
||
});
|
||
|
||
res.json({
|
||
topUsers,
|
||
currentUser: currentUserData,
|
||
currentUserRank: isExcludedUser ? null : (currentUserInTop ? topUsers.find(u => u._id.toString() === req.user._id.toString())?.rank : userRank)
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка получения топа:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|
||
|