Update files
This commit is contained in:
parent
ed85d8f6db
commit
8092d25c0c
|
|
@ -0,0 +1,177 @@
|
||||||
|
# 🚀 Инструкция по обновлению сервера
|
||||||
|
|
||||||
|
## Вариант 1: PM2 (рекомендуется)
|
||||||
|
|
||||||
|
### 1. Подключитесь к серверу
|
||||||
|
```bash
|
||||||
|
ssh user@your-server
|
||||||
|
cd /var/www/nakama # или путь к вашему проекту
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Обновите код
|
||||||
|
```bash
|
||||||
|
# Если используете Git
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
# Или загрузите файлы вручную через SFTP/SCP
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Установите зависимости (если нужно)
|
||||||
|
```bash
|
||||||
|
# Backend зависимости
|
||||||
|
npm install --production
|
||||||
|
|
||||||
|
# Frontend зависимости и сборка
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Перезапустите backend
|
||||||
|
```bash
|
||||||
|
pm2 restart nakama-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Проверьте статус
|
||||||
|
```bash
|
||||||
|
pm2 status
|
||||||
|
pm2 logs nakama-backend --lines 50
|
||||||
|
```
|
||||||
|
|
||||||
|
### Или используйте готовый скрипт:
|
||||||
|
```bash
|
||||||
|
chmod +x update-server.sh
|
||||||
|
./update-server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Вариант 2: Docker
|
||||||
|
|
||||||
|
### 1. Подключитесь к серверу
|
||||||
|
```bash
|
||||||
|
ssh user@your-server
|
||||||
|
cd /path/to/nakama
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Обновите код
|
||||||
|
```bash
|
||||||
|
# Если используете Git
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
# Или загрузите файлы вручную
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Пересоберите и перезапустите контейнеры
|
||||||
|
```bash
|
||||||
|
# Пересобрать только backend (если изменился только backend)
|
||||||
|
docker-compose build backend
|
||||||
|
docker-compose up -d backend
|
||||||
|
|
||||||
|
# Или пересобрать всё
|
||||||
|
docker-compose build
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Проверьте статус
|
||||||
|
```bash
|
||||||
|
docker-compose ps
|
||||||
|
docker-compose logs -f backend
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Быстрое обновление (только backend)
|
||||||
|
|
||||||
|
Если изменился только backend код:
|
||||||
|
|
||||||
|
### PM2:
|
||||||
|
```bash
|
||||||
|
cd /var/www/nakama
|
||||||
|
git pull
|
||||||
|
pm2 restart nakama-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker:
|
||||||
|
```bash
|
||||||
|
cd /path/to/nakama
|
||||||
|
git pull
|
||||||
|
docker-compose build backend
|
||||||
|
docker-compose up -d backend
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проверка после обновления
|
||||||
|
|
||||||
|
1. **Проверьте логи:**
|
||||||
|
```bash
|
||||||
|
# PM2
|
||||||
|
pm2 logs nakama-backend --lines 50
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker-compose logs backend --tail 50
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Проверьте здоровье сервера:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Проверьте работу приложения:**
|
||||||
|
- Откройте приложение в браузере
|
||||||
|
- Попробуйте авторизоваться
|
||||||
|
- Проверьте, что ники и аватарки отображаются
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что изменилось в этом обновлении
|
||||||
|
|
||||||
|
✅ **Отключено автообновление аватарок** - больше не будет автоматического обновления всех аватарок каждый день
|
||||||
|
|
||||||
|
✅ **Улучшено обновление данных при авторизации** - при каждом перезаходе пользователя система проверяет и подтягивает отсутствующие данные (username, firstName, lastName, photoUrl) из Telegram
|
||||||
|
|
||||||
|
✅ **Добавлены fallback значения** - если данные отсутствуют, отображаются значения по умолчанию вместо пустых полей
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Откат изменений (если что-то пошло не так)
|
||||||
|
|
||||||
|
### PM2:
|
||||||
|
```bash
|
||||||
|
cd /var/www/nakama
|
||||||
|
git checkout HEAD~1 # или конкретный коммит
|
||||||
|
pm2 restart nakama-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker:
|
||||||
|
```bash
|
||||||
|
cd /path/to/nakama
|
||||||
|
git checkout HEAD~1
|
||||||
|
docker-compose build backend
|
||||||
|
docker-compose up -d backend
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Полезные команды
|
||||||
|
|
||||||
|
### PM2:
|
||||||
|
```bash
|
||||||
|
pm2 list # Список процессов
|
||||||
|
pm2 restart nakama-backend # Перезапуск
|
||||||
|
pm2 stop nakama-backend # Остановка
|
||||||
|
pm2 logs nakama-backend # Логи
|
||||||
|
pm2 monit # Мониторинг
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker:
|
||||||
|
```bash
|
||||||
|
docker-compose ps # Статус контейнеров
|
||||||
|
docker-compose logs -f # Логи всех сервисов
|
||||||
|
docker-compose restart backend # Перезапуск backend
|
||||||
|
docker-compose down # Остановка всех контейнеров
|
||||||
|
docker-compose up -d # Запуск всех контейнеров
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -48,40 +48,54 @@ const ensureUserSettings = async (user) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Нормализовать данные пользователя из Telegram (поддержка camelCase и snake_case)
|
||||||
|
const normalizeTelegramUser = (telegramUser) => {
|
||||||
|
return {
|
||||||
|
id: telegramUser.id,
|
||||||
|
username: telegramUser.username || telegramUser.userName,
|
||||||
|
firstName: telegramUser.firstName || telegramUser.first_name || '',
|
||||||
|
lastName: telegramUser.lastName || telegramUser.last_name || '',
|
||||||
|
photoUrl: telegramUser.photoUrl || telegramUser.photo_url || null
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Подтянуть отсутствующие данные пользователя из Telegram
|
// Подтянуть отсутствующие данные пользователя из Telegram
|
||||||
const ensureUserData = async (user, telegramUser) => {
|
const ensureUserData = async (user, telegramUser) => {
|
||||||
if (!user || !telegramUser) return;
|
if (!user || !telegramUser) return;
|
||||||
|
|
||||||
|
// Нормализовать данные (поддержка camelCase и snake_case)
|
||||||
|
const normalized = normalizeTelegramUser(telegramUser);
|
||||||
|
|
||||||
let updated = false;
|
let updated = false;
|
||||||
|
|
||||||
// Обновить username, если отсутствует или пустой
|
// Обновить username, если отсутствует или пустой
|
||||||
if (!user.username || user.username.trim() === '') {
|
if (!user.username || user.username.trim() === '') {
|
||||||
if (telegramUser.username) {
|
if (normalized.username) {
|
||||||
user.username = telegramUser.username;
|
user.username = normalized.username;
|
||||||
updated = true;
|
updated = true;
|
||||||
} else if (telegramUser.first_name) {
|
} else if (normalized.firstName) {
|
||||||
user.username = telegramUser.first_name;
|
user.username = normalized.firstName;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновить firstName, если отсутствует
|
// Обновить firstName, если отсутствует
|
||||||
if (!user.firstName && telegramUser.first_name) {
|
if (!user.firstName && normalized.firstName) {
|
||||||
user.firstName = telegramUser.first_name;
|
user.firstName = normalized.firstName;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновить lastName, если отсутствует
|
// Обновить lastName, если отсутствует
|
||||||
if (user.lastName === undefined || user.lastName === null) {
|
if (user.lastName === undefined || user.lastName === null) {
|
||||||
user.lastName = telegramUser.last_name || '';
|
user.lastName = normalized.lastName;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновить аватарку, если отсутствует
|
// Обновить аватарку, если отсутствует
|
||||||
if (!user.photoUrl) {
|
if (!user.photoUrl) {
|
||||||
// Сначала проверить photo_url из initData
|
// Сначала проверить photoUrl из initData
|
||||||
if (telegramUser.photo_url) {
|
if (normalized.photoUrl) {
|
||||||
user.photoUrl = telegramUser.photo_url;
|
user.photoUrl = normalized.photoUrl;
|
||||||
updated = true;
|
updated = true;
|
||||||
} else {
|
} else {
|
||||||
// Если нет в initData, попробовать получить через Bot API
|
// Если нет в initData, попробовать получить через Bot API
|
||||||
|
|
@ -145,37 +159,40 @@ const authenticate = async (req, res, next) => {
|
||||||
return res.status(401).json({ error: 'Неверный ID пользователя' });
|
return res.status(401).json({ error: 'Неверный ID пользователя' });
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await User.findOne({ telegramId: telegramUser.id.toString() });
|
// Нормализовать данные пользователя (библиотека возвращает camelCase, но может быть и snake_case)
|
||||||
|
const normalizedUser = normalizeTelegramUser(telegramUser);
|
||||||
|
|
||||||
|
let user = await User.findOne({ telegramId: normalizedUser.id.toString() });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = new User({
|
user = new User({
|
||||||
telegramId: telegramUser.id.toString(),
|
telegramId: normalizedUser.id.toString(),
|
||||||
username: telegramUser.username || telegramUser.first_name || 'user',
|
username: normalizedUser.username || normalizedUser.firstName || 'user',
|
||||||
firstName: telegramUser.first_name || '',
|
firstName: normalizedUser.firstName,
|
||||||
lastName: telegramUser.last_name || '',
|
lastName: normalizedUser.lastName,
|
||||||
photoUrl: telegramUser.photo_url || null
|
photoUrl: normalizedUser.photoUrl
|
||||||
});
|
});
|
||||||
await user.save();
|
await user.save();
|
||||||
} else {
|
} else {
|
||||||
// Обновлять только если есть новые данные, не перезаписывать существующие пустыми значениями
|
// Обновлять только если есть новые данные, не перезаписывать существующие пустыми значениями
|
||||||
if (telegramUser.username) {
|
if (normalizedUser.username) {
|
||||||
user.username = telegramUser.username;
|
user.username = normalizedUser.username;
|
||||||
} else if (!user.username && telegramUser.first_name) {
|
} else if (!user.username && normalizedUser.firstName) {
|
||||||
// Если username пустой, использовать first_name как fallback
|
// Если username пустой, использовать firstName как fallback
|
||||||
user.username = telegramUser.first_name;
|
user.username = normalizedUser.firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (telegramUser.first_name) {
|
if (normalizedUser.firstName) {
|
||||||
user.firstName = telegramUser.first_name;
|
user.firstName = normalizedUser.firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (telegramUser.last_name !== undefined) {
|
if (normalizedUser.lastName !== undefined) {
|
||||||
user.lastName = telegramUser.last_name || '';
|
user.lastName = normalizedUser.lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновлять аватарку только если есть новая
|
// Обновлять аватарку только если есть новая
|
||||||
if (telegramUser.photo_url) {
|
if (normalizedUser.photoUrl) {
|
||||||
user.photoUrl = telegramUser.photo_url;
|
user.photoUrl = normalizedUser.photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
@ -185,11 +202,13 @@ const authenticate = async (req, res, next) => {
|
||||||
return res.status(403).json({ error: 'Пользователь заблокирован' });
|
return res.status(403).json({ error: 'Пользователь заблокирован' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Подтянуть отсутствующие данные из Telegram (используем нормализованные данные)
|
||||||
|
await ensureUserData(user, normalizedUser);
|
||||||
await ensureUserSettings(user);
|
await ensureUserSettings(user);
|
||||||
await touchUserActivity(user);
|
await touchUserActivity(user);
|
||||||
|
|
||||||
req.user = user;
|
req.user = user;
|
||||||
req.telegramUser = telegramUser;
|
req.telegramUser = normalizedUser;
|
||||||
next();
|
next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка авторизации:', error);
|
console.error('❌ Ошибка авторизации:', error);
|
||||||
|
|
@ -254,37 +273,40 @@ const authenticateModeration = async (req, res, next) => {
|
||||||
return res.status(401).json({ error: 'Неверный ID пользователя' });
|
return res.status(401).json({ error: 'Неверный ID пользователя' });
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await User.findOne({ telegramId: telegramUser.id.toString() });
|
// Нормализовать данные пользователя (библиотека возвращает camelCase, но может быть и snake_case)
|
||||||
|
const normalizedUser = normalizeTelegramUser(telegramUser);
|
||||||
|
|
||||||
|
let user = await User.findOne({ telegramId: normalizedUser.id.toString() });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = new User({
|
user = new User({
|
||||||
telegramId: telegramUser.id.toString(),
|
telegramId: normalizedUser.id.toString(),
|
||||||
username: telegramUser.username || telegramUser.first_name || 'user',
|
username: normalizedUser.username || normalizedUser.firstName || 'user',
|
||||||
firstName: telegramUser.first_name || '',
|
firstName: normalizedUser.firstName,
|
||||||
lastName: telegramUser.last_name || '',
|
lastName: normalizedUser.lastName,
|
||||||
photoUrl: telegramUser.photo_url || null
|
photoUrl: normalizedUser.photoUrl
|
||||||
});
|
});
|
||||||
await user.save();
|
await user.save();
|
||||||
} else {
|
} else {
|
||||||
// Обновлять только если есть новые данные, не перезаписывать существующие пустыми значениями
|
// Обновлять только если есть новые данные, не перезаписывать существующие пустыми значениями
|
||||||
if (telegramUser.username) {
|
if (normalizedUser.username) {
|
||||||
user.username = telegramUser.username;
|
user.username = normalizedUser.username;
|
||||||
} else if (!user.username && telegramUser.first_name) {
|
} else if (!user.username && normalizedUser.firstName) {
|
||||||
// Если username пустой, использовать first_name как fallback
|
// Если username пустой, использовать firstName как fallback
|
||||||
user.username = telegramUser.first_name;
|
user.username = normalizedUser.firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (telegramUser.first_name) {
|
if (normalizedUser.firstName) {
|
||||||
user.firstName = telegramUser.first_name;
|
user.firstName = normalizedUser.firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (telegramUser.last_name !== undefined) {
|
if (normalizedUser.lastName !== undefined) {
|
||||||
user.lastName = telegramUser.last_name || '';
|
user.lastName = normalizedUser.lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновлять аватарку только если есть новая
|
// Обновлять аватарку только если есть новая
|
||||||
if (telegramUser.photo_url) {
|
if (normalizedUser.photoUrl) {
|
||||||
user.photoUrl = telegramUser.photo_url;
|
user.photoUrl = normalizedUser.photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
@ -294,13 +316,13 @@ const authenticateModeration = async (req, res, next) => {
|
||||||
return res.status(403).json({ error: 'Пользователь заблокирован' });
|
return res.status(403).json({ error: 'Пользователь заблокирован' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подтянуть отсутствующие данные из Telegram
|
// Подтянуть отсутствующие данные из Telegram (используем нормализованные данные)
|
||||||
await ensureUserData(user, telegramUser);
|
await ensureUserData(user, normalizedUser);
|
||||||
await ensureUserSettings(user);
|
await ensureUserSettings(user);
|
||||||
await touchUserActivity(user);
|
await touchUserActivity(user);
|
||||||
|
|
||||||
req.user = user;
|
req.user = user;
|
||||||
req.telegramUser = telegramUser;
|
req.telegramUser = normalizedUser;
|
||||||
next();
|
next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка авторизации модерации:', error);
|
console.error('❌ Ошибка авторизации модерации:', error);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Скрипт для исправления ошибки ContainerConfig в Docker
|
||||||
|
|
||||||
|
echo "🔧 Исправление ошибки Docker ContainerConfig..."
|
||||||
|
|
||||||
|
# 1. Остановить и удалить проблемный контейнер
|
||||||
|
echo "📦 Остановка и удаление старого контейнера..."
|
||||||
|
docker-compose stop backend
|
||||||
|
docker-compose rm -f backend
|
||||||
|
|
||||||
|
# 2. Удалить старый образ backend (опционально, если нужно полное пересоздание)
|
||||||
|
echo "🗑️ Удаление старого образа backend..."
|
||||||
|
docker rmi nakama-backend 2>/dev/null || echo "Образ не найден, пропускаем"
|
||||||
|
|
||||||
|
# 3. Очистить кэш Docker (опционально, если проблема сохраняется)
|
||||||
|
# docker system prune -f
|
||||||
|
|
||||||
|
# 4. Пересобрать образ
|
||||||
|
echo "🔨 Пересборка образа backend..."
|
||||||
|
docker-compose build --no-cache backend
|
||||||
|
|
||||||
|
# 5. Запустить контейнер
|
||||||
|
echo "🚀 Запуск контейнера..."
|
||||||
|
docker-compose up -d backend
|
||||||
|
|
||||||
|
# 6. Проверить статус
|
||||||
|
echo "✅ Проверка статуса..."
|
||||||
|
docker-compose ps backend
|
||||||
|
docker-compose logs backend --tail 20
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Готово! Если проблема сохраняется, попробуйте:"
|
||||||
|
echo " docker-compose down"
|
||||||
|
echo " docker-compose build --no-cache"
|
||||||
|
echo " docker-compose up -d"
|
||||||
|
|
||||||
Loading…
Reference in New Issue