https://github.com/07rinat07/social-network
Solid Social Network SPA URALSK + IPTV/Radio ==> SPA-социальная сеть на Laravel + Vue с чатами, realtime, медиа-контентом и админ-панелью.
https://github.com/07rinat07/social-network
composer docker-compose html5 iptv-player iptv-playlist laravel-framework mysql-database mysql8 nodejs php radio swagger vue vue-router
Last synced: 3 months ago
JSON representation
Solid Social Network SPA URALSK + IPTV/Radio ==> SPA-социальная сеть на Laravel + Vue с чатами, realtime, медиа-контентом и админ-панелью.
- Host: GitHub
- URL: https://github.com/07rinat07/social-network
- Owner: 07Rinat07
- License: other
- Created: 2023-04-13T19:26:43.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2026-03-29T12:23:51.000Z (3 months ago)
- Last Synced: 2026-03-29T13:46:40.659Z (3 months ago)
- Topics: composer, docker-compose, html5, iptv-player, iptv-playlist, laravel-framework, mysql-database, mysql8, nodejs, php, radio, swagger, vue, vue-router
- Language: Vue
- Homepage:
- Size: 81.6 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Solid Social Network SPA URALSK + IPTV/Radio
SPA-социальная сеть на `Laravel + Vue` с realtime-чатами, медиа-лентой, интернет-радио, IPTV и административным кабинетом.
**Автор проекта:** Rinat Sarmuldin
**Email:** [ura07srr@gmail.com](mailto:ura07srr@gmail.com)
> IPTV/Radio: используются открытые источники. Ответственность за соблюдение прав на конкретные потоки лежит на пользователе/владельце инстанса.
## Содержание
- [О проекте](#о-проекте)
- [Ключевая функциональность](#ключевая-функциональность)
- [Скриншоты интерфейса](#скриншоты-интерфейса)
- [Архитектура](#архитектура)
- [Методика аналитики](#методика-аналитики)
- [Безопасность](#безопасность)
- [Технологический стек](#технологический-стек)
- [Быстрый старт](#быстрый-старт)
- [Переменные окружения](#переменные-окружения)
- [Тестирование](#тестирование)
- [API ориентиры](#api-ориентиры)
- [Swagger / OpenAPI](#swagger--openapi)
- [Тестовые аккаунты](#тестовые-аккаунты)
- [Частые проблемы](#частые-проблемы)
- [Деплой в production](#деплой-в-production)
- [Лицензия и права использования](#лицензия-и-права-использования)
## О проекте
`Solid Social` объединяет классический social feed и realtime-взаимодействие с мультимедийными сценариями:
- социальные публикации с медиа, комментариями, репостами и лайками;
- realtime-чаты с presence-статусами и typing-индикаторами;
- радио и IPTV-плеер (direct/proxy/transcode/relay режимы);
- админ-панель для модерации, настройки и аналитического дашборда;
- lifetime text error log для серверных, клиентских и analytics failure ошибок с просмотром и экспортом из админки;
- серверный heartbeat-трекинг пользовательской активности по модулям;
- клиентский event-трекинг для видео, upload-failure, радио и IPTV;
- мультиязычный интерфейс `RU/EN` с SEO URL (`/ru/...`, `/en/...`).
## Ключевая функциональность
### Социальная часть
- Регистрация, вход, подтверждение email.
- Профиль пользователя, подписки, лента подписок.
- Посты, комментарии, лайки, репосты, просмотры.
- Список комментариев к посту заново запрашивается с сервера при каждом открытии, поэтому новые комментарии и удаления видны разным пользователям без hard refresh.
- Если у поста уже есть комментарии, но они свернуты, карточка показывает отдельную заметную плашку `Показать комментарии`, чтобы не пропустить обсуждение.
- Загрузка изображений и видео через отдельные API-эндпоинты.
- Поддержка форматов пост-медиа: `jpg/jpeg/png/webp/gif/mp4/webm/mov/m4v/avi/mkv`, размер файла до `200 MB`.
- В личном кабинете отображаются очередь загрузки, общий и покомпонентный прогресс, статусы и ошибки по каждому файлу.
### Realtime и чаты
- Личные и общие диалоги.
- Presence: кто онлайн на сайте и в конкретном чате.
- Typing indicator через `whisper`.
- Статусы настроения с настройкой видимости (для всех или скрытие от выбранных участников).
- Архивирование/восстановление чатов, реакции, вложения.
- Удаление сообщений/вложений автором и администратором (через API), синхронизация между страницей чатов и виджетом.
- Транспорт realtime: `Laravel Reverb` + `pusher` protocol.
### IPTV
- Импорт плейлиста по URL (`m3u/m3u8`) и пользовательские сидеры.
- Фильтрация каналов по имени/группе, избранное, recent.
- Режимы воспроизведения:
- `Direct` (прямой поток),
- `Proxy` (серверный прокси для обхода CORS),
- `Compatibility/Transcode` (FFmpeg HLS-подготовка),
- `Relay` (ретрансляция с минимальной обработкой).
- Автоочистка и остановка серверных сессий при уходе со страницы.
### Радио
- Поиск станций через Radio Browser API.
- Встроенные подборки станций по категориям с проверкой доступности потоков.
- Карусели подборок и избранного с режимами `Авто/Вручную`, скоростью автодвижения и темами `Прозрачнее/Контрастнее`.
- Блок "Сейчас играет" со статусом воспроизведения, временем сессии и метаданными станции.
- Синхронизация воспроизведения между страницей радио и виджетом, включая корректный `LIVE`-таймер сессии.
- Компактный адаптивный режим: на телефонах блок "Сейчас играет" сжат, найденные станции по умолчанию свернуты, а счетчики/чипы и служебные badge усилены по контрасту.
- Избранные станции пользователя с мини-контролом текущего трека (play/pause/seek для не-live потоков).
- Одноразовая команда распределения избранного админов всем не-админам: `php artisan radio:distribute-admin-favorites` (`--dry-run` для проверки).
### Главная страница и контент
- Медиа-карусель.
- Компактная карусель авторов с поиском по имени и никнейму.
- Глобальная кнопка `В начало` на длинных экранах разделов с адаптивным положением для планшетов и телефонов.
- Виджет времени и погоды для городов (Нью-Йорк, Москва, Минск, Астана, Анкара, Уральск).
- Источник погоды: `Open-Meteo` через серверный запрос.
- В админке редактирование контента главной отдельно для `RU` и `EN`.
### Админ-аналитика и heartbeat
- Админ-дашборд с KPI, графиками подписок/активности и выбором периода `date_from/date_to` (в рамках выбранного года).
- Отдельные аналитические секции по удержанию и stickiness, эффективности контента, чатам, медиа/видео, радио, IPTV и ошибкам/модерации.
- Cohort-retention по месяцам регистрации, top posts/authors, reply-time по direct chat, mode split для IPTV и failure rate по transport/media.
- Сравнение предпочтений пользователей по `social/chats/radio/iptv`.
- Выгрузка фактической аналитики за выбранный диапазон в `Excel (.xls)` и `JSON` (скачивание на локальный компьютер/смартфон через браузер).
- Excel-выгрузка теперь включает блоки `Summary`, `Retention`, `Retention Cohorts`, `Content`, `Top Posts`, `Top Authors`, `Chats`, `Media`, `Radio`, `IPTV`, `Errors / Moderation`, а также пользовательскую сводку за период.
- Серверный endpoint heartbeat: `POST /api/activity/heartbeat` (auth + verified).
- Серверный endpoint client analytics: `POST /api/analytics/events` (auth + verified).
- Публичный endpoint client runtime log: `POST /api/client-errors` с записью `runtime/promise/vue/http` ошибок в lifetime text log.
- В админке есть preview на dashboard и отдельная вкладка журнала ошибок с поиском, фильтром по типу, пагинацией, filtered export и скачиванием полного `.log`.
- История журнала не теряется после ротации: старые файлы архивируются, при необходимости сжимаются в `.gz`, а поиск/фильтр/экспорт читают и активный лог, и архивы.
- Агрегация активности в таблицы:
- `user_activity_sessions` (сессии пользователя по feature),
- `user_activity_daily_stats` (дневная агрегированная статистика),
- `analytics_events` (клиентские события радио/IPTV/видео/upload-failure).
- Для дашборда автоматически выбирается модель аналитики:
- `actions` (fallback по действиям),
- `time_minutes` (если доступны heartbeat-данные за период).
### Мультиязычность и SEO
- Локализованные роуты: `/ru/...` и `/en/...`.
- Автоматический редирект на язык, если префикс не указан.
- Хранение выбранного языка в `localStorage` (`solid-social:locale`).
- Поддержка runtime-перевода legacy-строк.
## Скриншоты интерфейса
Актуальная галерея интерфейса. Нажмите на изображение, чтобы открыть полный размер.
Главная / лента
Чаты
Радио
IPTV
Кабинет пользователя
Админ-панель
Скриншоты открываются в новой вкладке — вернуться можно, просто закрыв её.
Каталог со скриншотами: `docs/screenshots/`.
## Карта модулей
| Модуль | Назначение | Точка входа |
|---|---|---|
| Auth + профиль | Регистрация, вход, подтверждение email, кабинет | `routes/web.php`, `routes/api.php` |
| Feed / posts | Публикации, лайки, комментарии, репосты, discover | `app/Http/Controllers/PostController.php` |
| Realtime chat | Диалоги, presence, typing, вложения, архивы | `app/Http/Controllers/ChatController.php`, `routes/channels.php` |
| IPTV | Загрузка плейлистов, proxy/transcode/relay сессии | `app/Http/Controllers/IptvController.php`, `app/Services/Iptv*` |
| Radio | Поиск, подборки, воспроизведение, избранное и их распределение | `app/Http/Controllers/RadioController.php`, `app/Console/Commands/DistributeAdminRadioFavorites.php` |
| Админка | Модерация пользователей/контента/обращений, настройки сайта | `/api/admin/*`, `app/Http/Controllers/AdminController.php` |
| Аналитика активности | Heartbeat + client analytics events, дашборд и Excel/JSON export | `app/Http/Controllers/ActivityHeartbeatController.php`, `app/Http/Controllers/AnalyticsEventController.php`, `app/Services/AdminDashboardService.php`, `app/Services/AdminDashboardExportService.php` |
| Диагностика ошибок | Lifetime log сайта, preview/search/filter/export/download, archive rotation | `app/Http/Controllers/SiteErrorLogController.php`, `app/Services/SiteErrorLogService.php`, `resources/js/utils/siteErrorReporter.js` |
| Контент главной | RU/EN контент, world overview (время/погода) | `app/Http/Controllers/SiteSettingController.php`, `app/Services/WorldOverviewService.php` |
| i18n + SEO | RU/EN маршруты, runtime перевод, SEO мета | `resources/js/router/index.js`, `resources/js/i18n` |
## Архитектура
### Схема компонентов
```mermaid
flowchart LR
U[Browser / Vue SPA] -->|HTTP API + Cookie Session + CSRF| N[Nginx]
U -->|WebSocket (Pusher protocol)| R[Laravel Reverb :6001]
N --> A[Laravel API / PHP-FPM]
A --> DB[(MySQL 8.4)]
A --> F[FFmpeg]
A --> WM[Open-Meteo API]
A --> RB[Radio Browser API]
A <--> R
```
### Слои приложения
- **Frontend (Vue 3 SPA):**
- роутер: `resources/js/router/index.js`
- представления: `resources/js/views`
- i18n: `resources/js/i18n`
- **Backend (Laravel 10):**
- HTTP API: `routes/api.php`
- web/shell: `routes/web.php`
- broadcast channels: `routes/channels.php`
- бизнес-логика: `app/Services`
- валидация/безопасность: `app/Rules`, `app/Http/Middleware`
- **Realtime:** `Laravel Reverb` (сервис `websocket` в Docker).
- **Storage/DB:** MySQL + файловое хранилище `storage`.
## Методика аналитики
Подробная спецификация аналитики вынесена в отдельный документ:
- [docs/analytics-metrics.md](docs/analytics-metrics.md)
Что в нём зафиксировано:
- точные таблицы-источники и endpoint-источники;
- формулы всех KPI и блоков `summary`, `dashboard`, `retention`, `content`, `chats`, `media`, `radio`, `iptv`, `errors_and_moderation`;
- логика fallback между heartbeat-метриками и action-based расчётом;
- правила `DAU/WAU/MAU`, `stickiness`, cohort retention, `engagement per post`, `failure rate`, `mode split`;
- команды для ручной проверки цифр через `php artisan tinker`.
Короткая карта:
- `GET /api/admin/summary` считает прямые `COUNT(*)` из БД.
- `GET /api/admin/dashboard` строит единый payload через `app/Services/AdminDashboardService.php`.
- `GET /api/admin/dashboard/export` экспортирует тот же payload через `app/Services/AdminDashboardExportService.php`, поэтому экран и Excel/JSON должны совпадать.
- Heartbeat хранится в `user_activity_sessions` и `user_activity_daily_stats`.
- Client analytics events для media/radio/IPTV/video хранятся в `analytics_events`.
Важно:
- при наличии heartbeat-данных дашборд переключается на `time_minutes`;
- без heartbeat включается fallback по действиям;
- transport/media/video блоки зависят от реально отправленных `analytics_events`;
- demo/seed-данные могут смещать пики по месяцам и retention-картины.
### Ключевые директории
```text
app/
Http/
Services/
Events/
Rules/
resources/js/
views/
components/
router/
i18n/
routes/
tests/Feature/
docker/
```
## Безопасность
### Аутентификация и доступ
- `Laravel Sanctum` в stateful-cookie режиме (`EnsureFrontendRequestsAreStateful`).
- Основные API сгруппированы под `auth:sanctum` + `verified` (подтвержденный email).
- Админские маршруты защищены middleware `admin` (`EnsureUserIsAdmin`).
- Broadcast-каналы имеют явную авторизацию в `routes/channels.php`.
### CSRF / Cookies / CORS
- CSRF защита включена для web-запросов.
- XSRF cookie name вынесен в env (`XSRF_COOKIE`), чтобы не конфликтовать между local и Docker.
- Сессионный cookie name тоже разделен (`SESSION_COOKIE`).
- CORS ограничен списком `CORS_ALLOWED_ORIGINS`, credentials включены.
### Валидация входных данных
- Запросы покрыты FormRequest/валидацией.
- Правило `NoUnsafeMarkup` блокирует:
- HTML-теги,
- encoded-теги (`<...>`),
- `javascript:` / `vbscript:` / inline handlers,
- скрытые управляющие символы.
- Для IPTV URL действует фильтрация:
- только `http/https`,
- блок private/reserved IP и `localhost`.
- Upload-валидация пост-медиа опирается на серверный `MIME`/`Fileinfo`; клиентский `MIME` используется только как fallback для контейнеров вроде `mkv`, которые браузер может отправлять как `application/octet-stream`.
- Файлы, замаскированные под видео, но распознанные сервером как не-media (`application/pdf` и т.п.), отклоняются до сохранения.
### Ограничение нагрузки
- API throttling настроен по маршрутам:
- авторизованные API-запросы: `throttle:600,1`;
- публичные `site/home-content` и `site/world-overview`: `throttle:240,1`;
- feedback anti-spam: `throttle:20,1`;
- public client error log: `throttle:30,1` на `POST /api/client-errors`;
- media (`post-images`, `avatars`): `throttle:600,1`;
- IPTV segment/playlist delivery (public relay/proxy/transcode): `throttle:1200,1`.
- Отправка email verification notification: `throttle:6,1`.
- Heartbeat endpoint: `throttle:120,1` на `POST /api/activity/heartbeat`.
- Client analytics endpoint: `throttle:240,1` на `POST /api/analytics/events`.
- IPTV сессии ограничены по количеству и TTL (proxy/transcode).
## Технологический стек
- **Backend:** PHP `^8.2` (Docker: PHP 8.3), Laravel 10, Sanctum, Reverb.
- **Frontend:** Vue 3, Vue Router, Axios, Vite 7, Tailwind CSS 4.
- **Media/Streaming:** HLS.js, DASH.js, MPEGTS.js, Plyr, FFmpeg.
- **Database:** MySQL (Docker: `mysql:8.4`).
- **Infra:** Docker Compose (Nginx + PHP-FPM + Reverb + MySQL + Node profiles).
## Быстрый старт
### 1) Локальный режим (без Docker)
1. Установите зависимости:
- `composer install`
- `npm ci`
2. Создайте `.env` из примера:
- Linux/macOS: `cp .env.example .env`
- PowerShell: `Copy-Item .env.example .env`
- CMD: `copy .env.example .env`
3. Настройте подключение к БД в `.env`.
4. Выполните инициализацию:
- `php artisan key:generate`
- `php artisan migrate --seed` (включая демо-контент: пользователи, посты, комментарии, лайки, изображения)
- `php artisan storage:link`
5. Запустите процессы:
- `php artisan serve`
- `npm run dev`
- `php artisan reverb:start --host=0.0.0.0 --port=6001`
6. Откройте `http://127.0.0.1:8000`.
### 2) Docker режим
1. Запуск:
- Обязательно создайте отдельный Docker env из шаблона `.env.docker.example`:
- Linux/macOS: `cp .env.docker.example .env.docker`
- PowerShell: `Copy-Item .env.docker.example .env.docker -Force`
- CMD: `copy .env.docker.example .env.docker`
- Не копируйте локальный `.env` в `.env.docker`: для Docker здесь нужны `DB_HOST=db` и переменные инициализации MySQL (`MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD`).
- `docker compose up -d --build`
- Если порты заняты, задайте forward-порты перед запуском (PowerShell):
`$env:WEB_FORWARD_PORT=8081; $env:DB_FORWARD_PORT=3308; $env:VITE_FORWARD_PORT=5174; $env:REVERB_FORWARD_PORT=6002; docker compose up -d --build`
2. Проверка:
- `docker compose ps`
- `docker compose top websocket`
3. Откройте `http://localhost:8080`.
В Docker:
- миграции запускаются автоматически (`RUN_MIGRATIONS=1`);
- при пустой БД (например после `docker compose down -v`) сиды запускаются автоматически (`RUN_SEEDERS_ON_EMPTY_DB=1`);
- если нужны реальные demo-фото в seed-постах, включите `DEMO_SEED_USE_REMOTE_IMAGES=1` в `.env.docker` и затем повторно запустите `docker compose exec --user=www-data app php artisan db:seed --class=DemoSocialContentSeeder --force`;
- `ffmpeg` уже установлен в образе `app`;
- отдельный websocket сервис поднимает Reverb на `6001`.
- Docker-шаблон использует `CACHE_DRIVER=file`, чтобы `websocket`/Reverb не зависел от таблицы `cache` и доступности MySQL при старте.
- при первом запуске зависимости (`composer`/`npm`) могут ставиться несколько минут, это нормально.
- `frontend-build` это одноразовый сервис сборки ассетов, поэтому состояние `Exited (0)` для него нормально;
- runtime-директории Laravel (`bootstrap/cache`, `storage/framework/*`) вынесены в named volumes, чтобы уменьшить лаги на Windows/macOS/Linux bind-mount.
### Полезные Docker команды
- Миграции вручную: `docker compose exec --user=www-data app php artisan migrate --seed`
- Сиды: `docker compose exec --user=www-data app php artisan db:seed`
- Демо-наполнение соцконтентом: `docker compose exec --user=www-data app php artisan db:seed --class=DemoSocialContentSeeder`
- Наполнение IPTV каналов: `docker compose exec --user=www-data app php artisan db:seed --class=IptvSeedSeeder`
- Проверка раздачи радио-избранного админа: `docker compose exec --user=www-data app php artisan radio:distribute-admin-favorites --dry-run`
- Тесты (без пересборки): `docker compose --profile test run --rm --no-build test`
- Тесты (с пересборкой test image): `docker compose --profile test run --rm --build test`
- Логи: `docker compose logs --tail=100 app`
- Остановка: `docker compose down`
### Docker порты и БД
- App: `http://localhost:8080` (переменная `WEB_FORWARD_PORT`)
- Vite dev (`profile dev`): `http://localhost:5173` (переменная `VITE_FORWARD_PORT`)
- Reverb websocket: `ws://localhost:6001` (переменная `REVERB_FORWARD_PORT`)
- MySQL с хоста: `127.0.0.1:3307` (переменная `DB_FORWARD_PORT`)
- MySQL внутри сети Docker: `db:3306`
## Переменные окружения
### Важно
- Локальный режим: используйте `.env` (из `.env.example`).
- Docker режим: сервисы используют `${DOCKER_ENV_FILE}`; рекомендуемый рабочий файл `.env.docker`, шаблон `.env.docker.example`.
- Не смешивайте local и docker cookies/домены в одном браузерном контексте.
- Если меняете cache backend для Docker на `database` или `redis`, он должен быть доступен и `app`, и `websocket`; для `database` также нужны таблицы `cache`/`cache_locks`.
### Ключевые env-переменные
- Приложение: `APP_ENV`, `APP_DEBUG`, `APP_URL`
- База: `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`
- Инициализация MySQL в Docker: `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD`
- Безопасность/сессия: `SANCTUM_STATEFUL_DOMAINS`, `CORS_ALLOWED_ORIGINS`, `SESSION_COOKIE`, `XSRF_COOKIE`, `VITE_XSRF_COOKIE_NAME`
- Realtime: `REVERB_*`, `VITE_REVERB_*`
- IPTV: `IPTV_FFMPEG_BIN`
- Radio API: `RADIO_BROWSER_BASE_URL`
- Lifetime error log: `SITE_ERROR_LOG_PATH`, `SITE_ERROR_LOG_ARCHIVE_PATH`, `SITE_ERROR_LOG_ROTATE_MAX_BYTES`, `SITE_ERROR_LOG_ROTATE_MAX_AGE_DAYS`, `SITE_ERROR_LOG_ARCHIVE_COMPRESS`
- Docker Compose (host only): `WEB_FORWARD_PORT`, `DB_FORWARD_PORT`, `VITE_FORWARD_PORT`, `REVERB_FORWARD_PORT`, `APP_HEALTHCHECK_START_PERIOD`, `RUN_MIGRATIONS`, `RUN_SEEDERS_ON_EMPTY_DB`, `DEMO_SEED_USE_REMOTE_IMAGES`
- Docker env selector: `DOCKER_ENV_FILE` (например `.env.docker`)
## Тестирование
Локально:
- Все тесты: `php artisan test`
- Feature suite: `php artisan test --testsuite=Feature`
- Rate limit и anti-spam: `php artisan test tests/Feature/ApiRateLimitFeatureTest.php`
- Login lockout/retry-after: `php artisan test tests/Feature/LoginThrottleFeatureTest.php`
- Медиа (доступ, fallback, cache-bust query): `php artisan test tests/Feature/MediaPostFeatureTest.php`
- Heartbeat трекинг: `php artisan test tests/Feature/ActivityHeartbeatFeatureTest.php`
- Дашборд/экспорт админ-аналитики: `php artisan test tests/Feature/AdminAndChatFeatureTest.php`
- Lifetime error log, фильтры, export и archive rotation: `php artisan test tests/Feature/SiteErrorLogFeatureTest.php`
- Сид демо-контента: `php artisan test tests/Feature/DemoSocialContentSeederFeatureTest.php`
- IPTV: `php artisan test tests/Feature/IptvFeatureTest.php`
- IPTV Админка (Сидеры): `php artisan test tests/Feature/AdminIptvSeedFeatureTest.php`
- Чаты и realtime: `php artisan test tests/Feature/ChatFeatureTest.php`
- Broadcast channels: `php artisan test tests/Feature/BroadcastChannelsFeatureTest.php`
- Swagger/OpenAPI: `php artisan test tests/Feature/SwaggerDocumentationFeatureTest.php`
- Админка: `php artisan test tests/Feature/AdminPanelFeatureTest.php`, `php artisan test tests/Feature/AdminAndChatFeatureTest.php`
- JS unit-тесты helper-логики (`radio`, `iptv`, `avatar search`, `section top button`): `npm run test:js`
- Фронт-билд: `npm run build`
В Docker:
- Все тесты (одноразовый test-контейнер): `docker compose --profile test run --rm --no-build test`
- Все тесты (в запущенном app-контейнере): `docker compose exec --user=www-data app php artisan test`
- Feature suite: `docker compose exec --user=www-data app php artisan test --testsuite=Feature`
- Конкретный файл тестов: `docker compose exec --user=www-data app php artisan test tests/Feature/ChatFeatureTest.php`
- Фронт-билд: `docker compose run --rm frontend-build`
Изоляция тестового окружения:
- Bootstrap тестов (`tests/CreatesApplication.php`) принудительно выставляет testing-env (`sqlite :memory:`, `CACHE_DRIVER=array`, `SESSION_DRIVER=array`, `BROADCAST_DRIVER=log`) до загрузки приложения.
- Для каждого процесса формируется `TEST_TOKEN`, а cache/storage paths переносятся в отдельный temp-каталог.
- Это устраняет конфликты и падения тестов из-за смешивания cache/storage между запуском от `root` и `www-data` в Docker.
Рекомендуемый pre-commit check:
- `php artisan test`
- `npm run test:js`
- `npm run build`
Последний полный прогон (1 марта 2026):
- `php artisan test`: `205 passed` (`1760 assertions`).
- `npm run test:js`: `33 passed`.
Проверка зависимостей (1 марта 2026):
- `npm audit`: `0 vulnerabilities`.
- `composer audit --format=json`: `0 advisories`.
- `doctrine/annotations` помечен как `abandoned` транзитивно через `l5-swagger`; это maintenance-вопрос без security advisory, но `composer audit` из-за этого может завершаться с non-zero exit code.
Ручной smoke-check интерфейса перед релизом:
- длинные секции и кнопки `В начало` на desktop/tablet/mobile;
- радио: карусели, счетчики, контрастные плашки, режимы `Прозрачнее/Контрастнее`, компактный mobile-блок `Сейчас играет`;
- чаты и виджеты: кнопки удаления, контраст текста/статусов и адаптивные списки.
## API ориентиры
### Публичные
- `POST /api/feedback`
- `POST /api/client-errors`
- `GET /api/site/home-content`
- `GET /api/site/world-overview?locale=ru|en`
### Авторизованные + verified
- Пользователи и профиль:
- `GET /api/users`, `GET /api/users/{user}/posts`, `GET /api/users/following_posts`
- `POST /api/users/profile`, `POST /api/users/{user}/toggle_following`
- Посты и медиа:
- `POST /api/post_media`, `GET /api/media/post-images/{postImage}`
- `GET /api/posts`, `POST /api/posts`, `GET /api/posts/discover`, `GET /api/posts/carousel`
- `POST /api/posts/{post}/view`, `GET /api/posts/{post}/comment`, `POST /api/posts/{post}/comment`, `DELETE /api/posts/{post}/comments/{comment}`
- Чаты:
- `GET /api/chats`, `GET /api/chats/unread-summary`, `GET /api/chats/users`
- `POST /api/chats/direct/{user}`, `GET /api/chats/{conversation}`, `POST /api/chats/{conversation}/read`
- `GET /api/chats/{conversation}/messages`, `POST /api/chats/{conversation}/messages`
- `POST /api/chats/{conversation}/messages/{message}/reactions`
- `DELETE /api/chats/{conversation}/messages/{message}`
- `DELETE /api/chats/{conversation}/messages/{message}/attachments/{attachment}`
- `GET /api/chats/settings`, `PATCH /api/chats/settings`
- `GET /api/chats/archives`, `POST /api/chats/archives`, `GET /api/chats/archives/{archive}/download`, `POST /api/chats/archives/{archive}/restore`
- `PATCH /api/chats/{conversation}/mood-status`
- Радио: `GET /api/radio/stations`, `GET /api/radio/stream`, `GET /api/radio/favorites`, `POST /api/radio/favorites`, `DELETE /api/radio/favorites/{stationUuid}`
- IPTV библиотека и импорт: `POST /api/iptv/playlist/fetch`, `GET /api/iptv/saved`, `POST /api/iptv/saved/playlists`, `PATCH /api/iptv/saved/playlists/{playlistId}`, `DELETE /api/iptv/saved/playlists/{playlistId}`, `POST /api/iptv/saved/channels`, `PATCH /api/iptv/saved/channels/{channelId}`, `DELETE /api/iptv/saved/channels/{channelId}`
- IPTV возможности: `GET /api/iptv/transcode/capabilities`
- `POST /api/iptv/proxy/start`, `DELETE /api/iptv/proxy/{session}`
- `POST /api/iptv/transcode/start`, `DELETE /api/iptv/transcode/{session}`
- `POST /api/iptv/relay/start`, `DELETE /api/iptv/relay/{session}`
- Activity tracking: `POST /api/activity/heartbeat`, `POST /api/analytics/events`
### Админские (`/api/admin/*`)
- Пользователи, посты, комментарии, обращения, диалоги, блокировки, настройки сайта.
- Дашборд аналитики:
- `GET /api/admin/summary`
- `GET /api/admin/dashboard?year=YYYY&date_from=YYYY-MM-DD&date_to=YYYY-MM-DD`
- `GET /api/admin/dashboard/export?format=xls|json&locale=ru|en&year=YYYY&date_from=YYYY-MM-DD&date_to=YYYY-MM-DD`
- Диагностика lifetime error log:
- `GET /api/admin/error-log`
- `GET /api/admin/error-log/entries?search=...&type=all|server_exception|client_error|analytics_failure&page=1&per_page=20`
- `GET /api/admin/error-log/export?search=...&type=...`
- `GET /api/admin/error-log/download`
- Экспорт запускается из админки и возвращает поток файла (`Content-Disposition`) для прямого скачивания.
- JSON/XLS payload синхронизирован с блоками `retention`, `content`, `chats`, `media`, `radio`, `iptv`, `errors_and_moderation`.
- Lifetime error log работает как отдельный operational diagnostics контур: хранит подробные сырые записи за весь срок жизни сайта, умеет искать по активному логу и архивам и не зависит от периодного dashboard payload.
- Формулы и источники данных описаны в [docs/analytics-metrics.md](docs/analytics-metrics.md).
## Swagger / OpenAPI
- Swagger UI: `GET /api/documentation`
- Docs route для JSON, который использует Swagger UI: `GET /docs?api-docs.json`
- Сгенерированный JSON-файл на диске: `storage/api-docs/api-docs.json`
- Генерация документации: `php artisan l5-swagger:generate`
- Базовые аннотации: `app/OpenApi/OpenApiSpec.php`
- Актуальная версия спецификации: `1.4.1`
- Спецификация синхронизирована с ключевыми маршрутами `site config`, `users`, `posts`, `post_media`, `radio favorites`, `chat settings/archives/mood-status`, `IPTV library/playback sessions`, `activity heartbeat`, `client analytics events`, `client error logging`, `admin summary/analytics/export`, `admin diagnostics error-log`.
- Для блока админ-аналитики Swagger синхронизирован с проектной методикой расчётов из [docs/analytics-metrics.md](docs/analytics-metrics.md).
- Генерация и доступность Swagger UI дополнительно проверяются тестом `tests/Feature/SwaggerDocumentationFeatureTest.php`.
## Тестовые аккаунты
Доступны после `php artisan db:seed`:
- `admin@example.com` / `password`
- `user1@example.com` / `password`
- `user2@example.com` / `password`
- `user3@example.com` / `password`
- `user4@example.com` / `password`
- `user5@example.com` / `password`
- `demo01@example.com` ... `demo18@example.com` / `password`
`DemoSocialContentSeeder` дополнительно создаёт демо-посты, комментарии, лайки, подписки и placeholder-изображения.
По умолчанию изображения генерируются локально (без зависимости от интернета). Если нужны внешние фото из `loremflickr.com`, включите `DEMO_SEED_USE_REMOTE_IMAGES=1`.
Если seed-посты уже были созданы с локальными SVG-плейсхолдерами, повторный запуск `DemoSocialContentSeeder` при `DEMO_SEED_USE_REMOTE_IMAGES=1` заменит их на реальные загруженные изображения.
## Частые проблемы
### `SQLSTATE[HY000] [2002] Connection refused`
- Проверьте `DB_HOST`/`DB_PORT` в активном env-файле.
- Из контейнеров Docker MySQL доступен как `db:3306`.
- С хоста Windows/MySQL доступен как `127.0.0.1:3307`.
### `Access denied`
- Проверьте `DB_USERNAME`/`DB_PASSWORD` и пользователя в MySQL.
### `Container social-network-db-1 is unhealthy`
- Самая частая причина: Docker запущен с неправильным `.env.docker` или без `MYSQL_ROOT_PASSWORD`.
- Пересоздайте docker-env из шаблона:
- PowerShell: `Copy-Item .env.docker.example .env.docker -Force`
- Проверьте, что в `.env.docker` есть:
- `DB_HOST=db`
- `MYSQL_DATABASE=...`
- `MYSQL_USER=...`
- `MYSQL_PASSWORD=...`
- `MYSQL_ROOT_PASSWORD=...`
- После исправления env перезапустите стек:
- `docker compose down -v`
- `docker compose up -d --build`
### `ports are not available` / `bind ... already in use`
- Порт уже занят другим приложением.
- Запустите Docker с другими host-портами через переменные:
`WEB_FORWARD_PORT`, `DB_FORWARD_PORT`, `VITE_FORWARD_PORT`, `REVERB_FORWARD_PORT`.
### После `docker compose down -v` пропали демо-данные
- Это ожидаемо: флаг `-v` удаляет том MySQL (`db_data`), база пересоздаётся с нуля.
- В текущей конфигурации контейнер `app` сам запускает `db:seed`, если таблица `users` пустая (`RUN_SEEDERS_ON_EMPTY_DB=1`).
- Если вы отключили авто-сидирование, запустите вручную:
- `docker compose exec --user=www-data app php artisan db:seed --force`
### В ленте видно fallback-картинку вместо фото
- Частая причина: прерванный запрос изображения (например `499` при смене маршрута), а не отсутствие файла или интернета.
- Маршруты media (`post-images`, `avatars`) используют отдельный лимит `throttle:600,1`.
- Во фронтенде добавлен автоповтор загрузки изображения перед fallback (cache-bust query `?_img_retry=...`).
- Если вы обновили код, но эффект не появился, очистите кэш:
- `docker compose exec --user=www-data app php artisan optimize:clear`
- `Ctrl+F5` в браузере
### Видео не загружается в новый пост
- Поддерживаются `mp4`, `webm`, `mov`, `m4v`, `avi`, `mkv` и изображения `jpg/jpeg/png/webp/gif`.
- Лимит одного файла: `200 MB`.
- В личном кабинете показываются статус и текст ошибки по каждому файлу; если загрузка отклонена, причина видна под формой.
- Для `mkv` загрузка и скачивание поддерживаются, но встроенное воспроизведение в браузере зависит от кодеков конкретного файла.
- В production проверьте, что `Nginx`/reverse proxy разрешает тело запроса не меньше `200M`, иначе запрос будет отклонен до Laravel.
### `No services to build` при `docker compose --profile test run ...`
- Это warning от Docker Compose, а не ошибка тестов.
- Используйте:
- без пересборки: `docker compose --profile test run --rm --no-build test`
- с пересборкой: `docker compose --profile test run --rm --build test`
### `500 /login` + `fopen(...storage/framework/cache/data/..): No such file or directory`
- Причина: `artisan` запускался в контейнере от `root`, и часть cache-папок создавалась с правами, недоступными для `www-data`.
- Профилактика: запускайте artisan в Docker так: `docker compose exec --user=www-data app php artisan ...`
- Если уже сломалось:
- `docker compose exec app sh -lc "chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache && find /var/www/html/storage /var/www/html/bootstrap/cache -type d -exec chmod 775 {} \\; && find /var/www/html/storage /var/www/html/bootstrap/cache -type f -exec chmod 664 {} \\;"`
- `docker compose exec --user=www-data app php artisan optimize:clear`
### `FilesystemIterator::__construct(...storage/framework/testing/disks/...): Permission denied` в тестах
- Причина: тестовые storage-пути были созданы от другого пользователя (обычно `root`), после чего запуск от `www-data` не может очистить директории.
- Профилактика: запускайте тесты в Docker от `www-data` и не смешивайте `docker compose exec app ...` и `docker compose exec --user=www-data app ...`.
- Если уже сломалось:
- `docker compose exec app sh -lc "rm -rf /var/www/html/storage/framework/testing/disks && mkdir -p /var/www/html/storage/framework/testing/disks && chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache && chmod -R ug+rwX /var/www/html/storage /var/www/html/bootstrap/cache"`
- `docker compose exec --user=www-data app php artisan test`
### `A temporary file could not be opened to write the process output` (Windows/OSPanel)
- Причина: PHP/Symfony Process не может писать lock-файлы во временную директорию по умолчанию (`C:/OSPanel/temp/...`).
- Решение: запускать тесты с локальным writable temp-каталогом проекта:
- PowerShell:
- `$tempPath = Join-Path (Get-Location) 'storage\\framework\\testing-runtime\\tmp'`
- `New-Item -ItemType Directory -Force -Path $tempPath | Out-Null`
- `php -d sys_temp_dir="$tempPath" artisan test`
### Пустая страница / сломанный фронт
- Локально: запущен ли `npm run dev`.
- Docker: пересоберите фронт `docker compose run --rm frontend-build`.
- Сделайте `Ctrl+F5`.
### Контейнер `frontend-build` в `Exited (0)`
- Это нормально: сервис делает `npm run build` и завершается после успешной сборки.
- Проверяйте именно долгоживущие сервисы: `app`, `web`, `websocket`, `db`.
- Если после простоя сайт отвечает медленно, смотрите `docker compose ps` и `docker compose logs --tail=100 app web db websocket`, а не статус `frontend-build`.
### `502 Bad Gateway` в Docker
- Короткий `502` во время ручного `docker compose up -d --force-recreate app` возможен, пока `php-fpm` поднимается заново.
- Постоянный `502` уже не должен быть связан со stale upstream IP: `web` резолвит `app` через Docker DNS во время работы.
- Если `502` не исчезает после того, как `app` стал `healthy`, проверьте:
- `docker compose ps`
- `docker compose logs --tail=100 app web`
- `docker compose exec --user=www-data app php artisan optimize:clear`
### `403` на `/api/broadcasting/auth`
- Убедитесь, что пользователь авторизован.
- Проверьте домен/порт (`APP_URL`) и фактический URL браузера.
- Проверьте, что Reverb запущен.
### `CSRF token mismatch`
- Очистите cookie и обновите страницу (`Ctrl+F5`).
- Убедитесь, что не смешаны окружения:
- local: `XSRF_COOKIE=social_network_local_xsrf`
- Docker: `XSRF_COOKIE=social_network_docker_xsrf`
- После переключения режимов выполните logout и удалите старые cookies.
### Битая аватарка или файлы из `/storage` (локально)
- Исправление:
- `php -r "if (is_link('public/storage')) unlink('public/storage');"`
- `php artisan storage:link`
## Деплой в production
Подробный production-гайд (Ubuntu + Nginx + PHP-FPM + Supervisor):
`DEPLOY.md`
## Лицензия и права использования
- `© 2026 Rinat Sarmuldin. All rights reserved.`
- Репозиторий распространяется по проприетарной лицензии: `LICENSE`.
- Без письменного разрешения автора запрещены:
- использование в production и коммерческих проектах;
- копирование, изменение, распространение и создание производных работ.
- Для получения прав на использование свяжитесь с автором: `ura07srr@gmail.com`.