diff --git a/EMPTY_SCREEN_FIX.md b/EMPTY_SCREEN_FIX.md
new file mode 100644
index 0000000..b2296ad
--- /dev/null
+++ b/EMPTY_SCREEN_FIX.md
@@ -0,0 +1,156 @@
+# Исправление: Пустой экран после верификации
+
+## 🔴 Проблема
+После успешной верификации UI открывается, но моментально пропадает (полностью пустой экран).
+
+---
+
+## ✅ Что исправлено
+
+### 1. **Улучшена обработка ошибок**
+- Добавлено подробное логирование в консоль браузера
+- Теперь видно, на каком этапе происходит ошибка
+
+### 2. **Исправлен пустой экран**
+- Вместо `return null` теперь показывается сообщение с кнопкой перезагрузки
+- Это помогает понять, что происходит
+
+### 3. **Отложен запуск initDataChecker**
+- `initDataChecker` теперь запускается только после успешной загрузки пользователя
+- Это предотвращает преждевременную перезагрузку страницы
+
+### 4. **Улучшена валидация данных**
+- Проверяется, что `userData` действительно получен
+- Если `null` или `undefined` - показывается ошибка
+
+---
+
+## 🔍 Как диагностировать
+
+### Шаг 1: Откройте консоль браузера
+
+1. В Telegram откройте приложение
+2. Нажмите **F12** или **Cmd+Option+I** (Mac)
+3. Перейдите на вкладку **Console**
+
+### Шаг 2: Посмотрите логи
+
+**Должны увидеть:**
+```
+[App] Начало инициализации...
+[App] Telegram WebApp найден, initData: есть
+[API] verifyAuth: отправка запроса...
+[API] verifyAuth: получен ответ: { hasUser: true, userId: "...", username: "..." }
+[App] verifyAuth вернул: данные пользователя
+[App] Пользователь установлен, ID: ...
+[App] Инициализация завершена, loading: false
+```
+
+**Если видите ошибку:**
+```
+[API] verifyAuth: ошибка: { message: "...", status: 401/500 }
+[App] Ошибка инициализации: ...
+```
+
+---
+
+## 🐛 Возможные причины
+
+### 1. **Ошибка 401 (Unauthorized)**
+**Причина:** `initData` невалиден или истек
+
+**Решение:**
+- Перезагрузите приложение в Telegram
+- Убедитесь, что используете официальный клиент Telegram
+
+### 2. **Ошибка 500 (Server Error)**
+**Причина:** Проблема на backend
+
+**Решение:**
+```bash
+# Проверьте логи backend
+docker logs nakama-backend --tail 100
+
+# Ищите ошибки:
+# - "Ошибка verify"
+# - "MongoDB connection"
+# - "MinIO connection"
+```
+
+### 3. **userData = null/undefined**
+**Причина:** Backend не вернул данные пользователя
+
+**Решение:**
+- Проверьте маршрут `/api/auth/verify` на backend
+- Убедитесь, что пользователь существует в БД
+- Проверьте, что `respondWithUser` работает правильно
+
+### 4. **Ошибка в компонентах Feed/Layout**
+**Причина:** Компонент падает при рендеринге
+
+**Решение:**
+- В консоли браузера будет **красная ошибка** с указанием файла и строки
+- Проверьте, что все данные пользователя присутствуют (`user.settings`, `user.photoUrl`, etc.)
+
+---
+
+## 🔧 Быстрое решение
+
+### 1. Перезагрузите приложение
+```javascript
+// В консоли браузера
+window.location.reload()
+```
+
+### 2. Проверьте backend
+```bash
+# Проверьте, что backend работает
+curl http://your-backend-url/api/health
+
+# Проверьте логи
+docker logs nakama-backend -f
+```
+
+### 3. Проверьте MongoDB
+```bash
+# Подключитесь к MongoDB
+docker exec -it nakama-mongodb mongosh
+
+# Проверьте пользователей
+use nakama
+db.users.find().limit(5)
+```
+
+---
+
+## 📋 Контрольный список
+
+- [ ] Консоль браузера открыта (F12)
+- [ ] Видны логи `[App]` и `[API]`
+- [ ] Нет красных ошибок в консоли
+- [ ] Backend доступен (`/api/health`)
+- [ ] MongoDB подключена
+- [ ] Пользователь существует в БД
+- [ ] `initData` валиден (не истек)
+
+---
+
+## 💡 Если все еще не работает
+
+### Отправьте мне:
+1. **Логи из консоли браузера** (скопируйте все сообщения)
+2. **Логи backend** (`docker logs nakama-backend --tail 200`)
+3. **Скриншот пустого экрана**
+4. **Ошибки из консоли** (красные сообщения)
+
+---
+
+## ✅ После исправления
+
+После применения исправлений вы должны видеть:
+- ✅ Логи в консоли на каждом этапе
+- ✅ Сообщение об ошибке вместо пустого экрана
+- ✅ Кнопку "Перезагрузить" если что-то пошло не так
+
+**Теперь откройте приложение и посмотрите консоль браузера!** 🔍
+
diff --git a/backend/routes/posts.js b/backend/routes/posts.js
index 8fd3ff6..66e87fe 100644
--- a/backend/routes/posts.js
+++ b/backend/routes/posts.js
@@ -39,7 +39,7 @@ router.get('/', authenticate, async (req, res) => {
query.isNSFW = false;
}
- const posts = await Post.find(query)
+ let posts = await Post.find(query)
.populate('author', 'username firstName lastName photoUrl')
.populate('mentionedUsers', 'username firstName lastName')
.populate('comments.author', 'username firstName lastName photoUrl')
@@ -48,6 +48,9 @@ router.get('/', authenticate, async (req, res) => {
.skip((page - 1) * limit)
.exec();
+ // Фильтруем посты без автора (защита от ошибок)
+ posts = posts.filter(post => post.author !== null && post.author !== undefined);
+
const count = await Post.countDocuments(query);
res.json({
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 1547602..f0e1476 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -30,8 +30,7 @@ function AppContent() {
initApp()
}
- // Запустить проверку initData
- startInitDataChecker()
+ // НЕ запускать initDataChecker здесь - он запустится после успешной загрузки пользователя
return () => {
stopInitDataChecker()
@@ -40,6 +39,7 @@ function AppContent() {
const initApp = async () => {
try {
+ console.log('[App] Начало инициализации...')
initTelegramApp()
const tg = window.Telegram?.WebApp
@@ -52,12 +52,25 @@ function AppContent() {
throw new Error('Telegram не передал initData. Откройте приложение из официального клиента.')
}
+ console.log('[App] Telegram WebApp найден, initData:', tg.initData ? 'есть' : 'нет')
+
tg.disableVerticalSwipes?.()
tg.expand?.()
+ console.log('[App] Вызов verifyAuth...')
const userData = await verifyAuth()
+ console.log('[App] verifyAuth вернул:', userData ? 'данные пользователя' : 'null/undefined', userData)
+
+ if (!userData) {
+ throw new Error('Не удалось получить данные пользователя. Попробуйте перезагрузить страницу.')
+ }
+
setUser(userData)
setError(null)
+ console.log('[App] Пользователь установлен, ID:', userData.id || userData._id)
+
+ // Запустить проверку initData только после успешной загрузки
+ startInitDataChecker()
if (!startParamProcessed.current && tg?.startParam?.startsWith('post_')) {
startParamProcessed.current = true
@@ -67,10 +80,17 @@ function AppContent() {
}, 200)
}
} catch (err) {
- console.error('Ошибка инициализации:', err)
+ console.error('[App] Ошибка инициализации:', err)
+ console.error('[App] Детали ошибки:', {
+ message: err.message,
+ response: err.response?.data,
+ status: err.response?.status
+ })
setError(err?.response?.data?.error || err.message || 'Ошибка авторизации')
+ setUser(null) // Явно сбросить user при ошибке
} finally {
setLoading(false)
+ console.log('[App] Инициализация завершена, loading:', false)
}
}
@@ -126,7 +146,36 @@ function AppContent() {
}
if (!user) {
- return null
+ // Показываем сообщение вместо пустого экрана
+ return (
+
+
+ Загрузка данных пользователя...
+
+
+
+ )
}
return (
diff --git a/frontend/src/components/CommentsModal.jsx b/frontend/src/components/CommentsModal.jsx
index 22f4751..fb8846f 100644
--- a/frontend/src/components/CommentsModal.jsx
+++ b/frontend/src/components/CommentsModal.jsx
@@ -64,16 +64,17 @@ export default function CommentsModal({ post, onClose, onUpdate }) {

{ e.target.src = '/default-avatar.png' }}
/>
- {post.author.firstName || ''} {post.author.lastName || ''}
- {!post.author.firstName && !post.author.lastName && 'Пользователь'}
+ {post.author?.firstName || ''} {post.author?.lastName || ''}
+ {!post.author?.firstName && !post.author?.lastName && 'Пользователь'}
-
@{post.author.username || post.author.firstName || 'user'}
+
@{post.author?.username || post.author?.firstName || 'user'}
@@ -96,18 +97,21 @@ export default function CommentsModal({ post, onClose, onUpdate }) {
Будьте первым!
) : (
- comments.map((c, index) => (
+ comments
+ .filter(c => c.author) // Фильтруем комментарии без автора
+ .map((c, index) => (

{ e.target.src = '/default-avatar.png' }}
/>
- {c.author.firstName || ''} {c.author.lastName || ''}
- {!c.author.firstName && !c.author.lastName && 'Пользователь'}
+ {c.author?.firstName || ''} {c.author?.lastName || ''}
+ {!c.author?.firstName && !c.author?.lastName && 'Пользователь'}
{formatDate(c.createdAt)}
diff --git a/frontend/src/components/PostCard.jsx b/frontend/src/components/PostCard.jsx
index 016d5f4..4ef4dad 100644
--- a/frontend/src/components/PostCard.jsx
+++ b/frontend/src/components/PostCard.jsx
@@ -19,13 +19,28 @@ const TAG_NAMES = {
export default function PostCard({ post, currentUser, onUpdate }) {
const navigate = useNavigate()
- const [liked, setLiked] = useState(post.likes.includes(currentUser.id))
- const [likesCount, setLikesCount] = useState(post.likes.length)
+ const [liked, setLiked] = useState(post.likes?.includes(currentUser.id) || false)
+ const [likesCount, setLikesCount] = useState(post.likes?.length || 0)
const [currentImageIndex, setCurrentImageIndex] = useState(0)
const [showFullView, setShowFullView] = useState(false)
+ // Проверка на существование автора
+ if (!post.author) {
+ console.warn('[PostCard] Post without author:', post._id)
+ return null // Не показываем посты без автора
+ }
+
// Поддержка и старого поля imageUrl и нового images
- const images = post.images && post.images.length > 0 ? post.images : (post.imageUrl ? [post.imageUrl] : [])
+ // Фильтруем старые URL из Telegram API
+ const allImages = post.images && post.images.length > 0 ? post.images : (post.imageUrl ? [post.imageUrl] : [])
+ const images = allImages.filter(img => {
+ // Игнорируем старые URL из Telegram API
+ if (img && typeof img === 'string' && img.includes('api.telegram.org/file/bot')) {
+ console.warn('[PostCard] Skipping old Telegram URL:', img)
+ return false
+ }
+ return true
+ })
const handleLike = async () => {
try {
@@ -61,7 +76,9 @@ export default function PostCard({ post, currentUser, onUpdate }) {
}
const goToProfile = () => {
- navigate(`/user/${post.author._id}`)
+ if (post.author?._id) {
+ navigate(`/user/${post.author._id}`)
+ }
}
const openFullView = () => {
@@ -103,17 +120,20 @@ export default function PostCard({ post, currentUser, onUpdate }) {

{
+ e.target.src = '/default-avatar.png'
+ }}
/>
- {post.author.firstName || ''} {post.author.lastName || ''}
- {!post.author.firstName && !post.author.lastName && 'Пользователь'}
+ {post.author?.firstName || ''} {post.author?.lastName || ''}
+ {!post.author?.firstName && !post.author?.lastName && 'Пользователь'}
- @{post.author.username || post.author.firstName || 'user'} · {formatDate(post.createdAt)}
+ @{post.author?.username || post.author?.firstName || 'user'} · {formatDate(post.createdAt)}
diff --git a/frontend/src/pages/CommentsPage.jsx b/frontend/src/pages/CommentsPage.jsx
index e574373..9f227f9 100644
--- a/frontend/src/pages/CommentsPage.jsx
+++ b/frontend/src/pages/CommentsPage.jsx
@@ -122,16 +122,17 @@ export default function CommentsPage({ user }) {

{ e.target.src = '/default-avatar.png' }}
/>
- {post.author.firstName || ''} {post.author.lastName || ''}
- {!post.author.firstName && !post.author.lastName && 'Пользователь'}
+ {post.author?.firstName || ''} {post.author?.lastName || ''}
+ {!post.author?.firstName && !post.author?.lastName && 'Пользователь'}
-
@{post.author.username || post.author.firstName || 'user'}
+
@{post.author?.username || post.author?.firstName || 'user'}
@@ -156,23 +157,26 @@ export default function CommentsPage({ user }) {
Будьте первым!
) : (
- comments.map((c, index) => {
+ comments
+ .filter(c => c.author) // Фильтруем комментарии без автора
+ .map((c, index) => {
const isEditing = editingCommentId === c._id
- const isOwnComment = c.author._id === user.id
+ const isOwnComment = c.author?._id === user.id
const canEdit = isOwnComment || user.role === 'moderator' || user.role === 'admin'
return (

{ e.target.src = '/default-avatar.png' }}
/>
- {c.author.firstName || ''} {c.author.lastName || ''}
- {!c.author.firstName && !c.author.lastName && 'Пользователь'}
+ {c.author?.firstName || ''} {c.author?.lastName || ''}
+ {!c.author?.firstName && !c.author?.lastName && 'Пользователь'}
{formatDate(c.createdAt)}
diff --git a/frontend/src/pages/Feed.jsx b/frontend/src/pages/Feed.jsx
index bd248b2..e5db88a 100644
--- a/frontend/src/pages/Feed.jsx
+++ b/frontend/src/pages/Feed.jsx
@@ -71,10 +71,13 @@ export default function Feed({ user }) {
const data = await getPosts(params)
+ // Фильтруем посты без автора (защита от ошибок)
+ const validPosts = data.posts.filter(post => post.author)
+
if (pageNum === 1) {
- setPosts(data.posts)
+ setPosts(validPosts)
} else {
- setPosts(prev => [...prev, ...data.posts])
+ setPosts(prev => [...prev, ...validPosts])
}
setHasMore(pageNum < data.totalPages)
diff --git a/frontend/src/pages/PostMenuPage.jsx b/frontend/src/pages/PostMenuPage.jsx
index 0f795fd..d6700a7 100644
--- a/frontend/src/pages/PostMenuPage.jsx
+++ b/frontend/src/pages/PostMenuPage.jsx
@@ -188,16 +188,17 @@ export default function PostMenuPage({ user }) {

{ e.target.src = '/default-avatar.png' }}
/>
- {post.author.firstName || ''} {post.author.lastName || ''}
- {!post.author.firstName && !post.author.lastName && 'Пользователь'}
+ {post.author?.firstName || ''} {post.author?.lastName || ''}
+ {!post.author?.firstName && !post.author?.lastName && 'Пользователь'}
-
@{post.author.username || post.author.firstName || 'user'}
+
@{post.author?.username || post.author?.firstName || 'user'}
diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js
index 89e2646..ca674b1 100644
--- a/frontend/src/utils/api.js
+++ b/frontend/src/utils/api.js
@@ -79,8 +79,29 @@ export const signInWithTelegram = async (initData) => {
}
export const verifyAuth = async () => {
- const response = await api.post('/auth/verify')
- return response.data.user
+ try {
+ console.log('[API] verifyAuth: отправка запроса...')
+ const response = await api.post('/auth/verify')
+ console.log('[API] verifyAuth: получен ответ:', {
+ hasUser: !!response.data?.user,
+ userId: response.data?.user?.id || response.data?.user?._id,
+ username: response.data?.user?.username
+ })
+
+ if (!response.data?.user) {
+ console.error('[API] verifyAuth: ответ не содержит user:', response.data)
+ throw new Error('Сервер не вернул данные пользователя')
+ }
+
+ return response.data.user
+ } catch (error) {
+ console.error('[API] verifyAuth: ошибка:', {
+ message: error.message,
+ response: error.response?.data,
+ status: error.response?.status
+ })
+ throw error
+ }
}
// Авторизация через Telegram OAuth (Login Widget)