nakama/backend/routes/users.js

277 lines
9.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;