{"id":23282148,"url":"https://github.com/07rinat07/social-network","last_synced_at":"2026-04-04T22:34:11.223Z","repository":{"id":154135236,"uuid":"627588621","full_name":"07Rinat07/social-network","owner":"07Rinat07","description":"Solid Social Network SPA URALSK + IPTV/Radio ==\u003e SPA-социальная сеть на Laravel + Vue с чатами, realtime, медиа-контентом и админ-панелью. ","archived":false,"fork":false,"pushed_at":"2026-03-29T12:23:51.000Z","size":85595,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-29T13:46:40.659Z","etag":null,"topics":["composer","docker-compose","html5","iptv-player","iptv-playlist","laravel-framework","mysql-database","mysql8","nodejs","php","radio","swagger","vue","vue-router"],"latest_commit_sha":null,"homepage":"","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/07Rinat07.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-04-13T19:26:43.000Z","updated_at":"2026-03-29T12:23:54.000Z","dependencies_parsed_at":"2023-05-23T16:15:45.303Z","dependency_job_id":null,"html_url":"https://github.com/07Rinat07/social-network","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/07Rinat07/social-network","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/07Rinat07%2Fsocial-network","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/07Rinat07%2Fsocial-network/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/07Rinat07%2Fsocial-network/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/07Rinat07%2Fsocial-network/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/07Rinat07","download_url":"https://codeload.github.com/07Rinat07/social-network/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/07Rinat07%2Fsocial-network/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31416778,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["composer","docker-compose","html5","iptv-player","iptv-playlist","laravel-framework","mysql-database","mysql8","nodejs","php","radio","swagger","vue","vue-router"],"created_at":"2024-12-20T00:14:46.191Z","updated_at":"2026-04-04T22:34:11.215Z","avatar_url":"https://github.com/07Rinat07.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Solid Social Network SPA URALSK + IPTV/Radio\n\nSPA-социальная сеть на `Laravel + Vue` с realtime-чатами, медиа-лентой, интернет-радио, IPTV и административным кабинетом.\n\n**Автор проекта:** Rinat Sarmuldin  \n**Email:** [ura07srr@gmail.com](mailto:ura07srr@gmail.com)\n\n\u003e IPTV/Radio: используются открытые источники. Ответственность за соблюдение прав на конкретные потоки лежит на пользователе/владельце инстанса.\n\n## Содержание\n- [О проекте](#о-проекте)\n- [Ключевая функциональность](#ключевая-функциональность)\n- [Скриншоты интерфейса](#скриншоты-интерфейса)\n- [Архитектура](#архитектура)\n- [Методика аналитики](#методика-аналитики)\n- [Безопасность](#безопасность)\n- [Технологический стек](#технологический-стек)\n- [Быстрый старт](#быстрый-старт)\n- [Переменные окружения](#переменные-окружения)\n- [Тестирование](#тестирование)\n- [API ориентиры](#api-ориентиры)\n- [Swagger / OpenAPI](#swagger--openapi)\n- [Тестовые аккаунты](#тестовые-аккаунты)\n- [Частые проблемы](#частые-проблемы)\n- [Деплой в production](#деплой-в-production)\n- [Лицензия и права использования](#лицензия-и-права-использования)\n\n## О проекте\n`Solid Social` объединяет классический social feed и realtime-взаимодействие с мультимедийными сценариями:\n- социальные публикации с медиа, комментариями, репостами и лайками;\n- realtime-чаты с presence-статусами и typing-индикаторами;\n- радио и IPTV-плеер (direct/proxy/transcode/relay режимы);\n- админ-панель для модерации, настройки и аналитического дашборда;\n- lifetime text error log для серверных, клиентских и analytics failure ошибок с просмотром и экспортом из админки;\n- серверный heartbeat-трекинг пользовательской активности по модулям;\n- клиентский event-трекинг для видео, upload-failure, радио и IPTV;\n- мультиязычный интерфейс `RU/EN` с SEO URL (`/ru/...`, `/en/...`).\n\n## Ключевая функциональность\n\n### Социальная часть\n- Регистрация, вход, подтверждение email.\n- Профиль пользователя, подписки, лента подписок.\n- Посты, комментарии, лайки, репосты, просмотры.\n- Список комментариев к посту заново запрашивается с сервера при каждом открытии, поэтому новые комментарии и удаления видны разным пользователям без hard refresh.\n- Если у поста уже есть комментарии, но они свернуты, карточка показывает отдельную заметную плашку `Показать комментарии`, чтобы не пропустить обсуждение.\n- Загрузка изображений и видео через отдельные API-эндпоинты.\n- Поддержка форматов пост-медиа: `jpg/jpeg/png/webp/gif/mp4/webm/mov/m4v/avi/mkv`, размер файла до `200 MB`.\n- В личном кабинете отображаются очередь загрузки, общий и покомпонентный прогресс, статусы и ошибки по каждому файлу.\n\n### Realtime и чаты\n- Личные и общие диалоги.\n- Presence: кто онлайн на сайте и в конкретном чате.\n- Typing indicator через `whisper`.\n- Статусы настроения с настройкой видимости (для всех или скрытие от выбранных участников).\n- Архивирование/восстановление чатов, реакции, вложения.\n- Удаление сообщений/вложений автором и администратором (через API), синхронизация между страницей чатов и виджетом.\n- Транспорт realtime: `Laravel Reverb` + `pusher` protocol.\n\n### IPTV\n- Импорт плейлиста по URL (`m3u/m3u8`) и пользовательские сидеры.\n- Фильтрация каналов по имени/группе, избранное, recent.\n- Режимы воспроизведения:\n  - `Direct` (прямой поток),\n  - `Proxy` (серверный прокси для обхода CORS),\n  - `Compatibility/Transcode` (FFmpeg HLS-подготовка),\n  - `Relay` (ретрансляция с минимальной обработкой).\n- Автоочистка и остановка серверных сессий при уходе со страницы.\n\n### Радио\n- Поиск станций через Radio Browser API.\n- Встроенные подборки станций по категориям с проверкой доступности потоков.\n- Карусели подборок и избранного с режимами `Авто/Вручную`, скоростью автодвижения и темами `Прозрачнее/Контрастнее`.\n- Блок \"Сейчас играет\" со статусом воспроизведения, временем сессии и метаданными станции.\n- Синхронизация воспроизведения между страницей радио и виджетом, включая корректный `LIVE`-таймер сессии.\n- Компактный адаптивный режим: на телефонах блок \"Сейчас играет\" сжат, найденные станции по умолчанию свернуты, а счетчики/чипы и служебные badge усилены по контрасту.\n- Избранные станции пользователя с мини-контролом текущего трека (play/pause/seek для не-live потоков).\n- Одноразовая команда распределения избранного админов всем не-админам: `php artisan radio:distribute-admin-favorites` (`--dry-run` для проверки).\n\n### Главная страница и контент\n- Медиа-карусель.\n- Компактная карусель авторов с поиском по имени и никнейму.\n- Глобальная кнопка `В начало` на длинных экранах разделов с адаптивным положением для планшетов и телефонов.\n- Виджет времени и погоды для городов (Нью-Йорк, Москва, Минск, Астана, Анкара, Уральск).\n- Источник погоды: `Open-Meteo` через серверный запрос.\n- В админке редактирование контента главной отдельно для `RU` и `EN`.\n\n### Админ-аналитика и heartbeat\n- Админ-дашборд с KPI, графиками подписок/активности и выбором периода `date_from/date_to` (в рамках выбранного года).\n- Отдельные аналитические секции по удержанию и stickiness, эффективности контента, чатам, медиа/видео, радио, IPTV и ошибкам/модерации.\n- Cohort-retention по месяцам регистрации, top posts/authors, reply-time по direct chat, mode split для IPTV и failure rate по transport/media.\n- Сравнение предпочтений пользователей по `social/chats/radio/iptv`.\n- Выгрузка фактической аналитики за выбранный диапазон в `Excel (.xls)` и `JSON` (скачивание на локальный компьютер/смартфон через браузер).\n- Excel-выгрузка теперь включает блоки `Summary`, `Retention`, `Retention Cohorts`, `Content`, `Top Posts`, `Top Authors`, `Chats`, `Media`, `Radio`, `IPTV`, `Errors / Moderation`, а также пользовательскую сводку за период.\n- Серверный endpoint heartbeat: `POST /api/activity/heartbeat` (auth + verified).\n- Серверный endpoint client analytics: `POST /api/analytics/events` (auth + verified).\n- Публичный endpoint client runtime log: `POST /api/client-errors` с записью `runtime/promise/vue/http` ошибок в lifetime text log.\n- В админке есть preview на dashboard и отдельная вкладка журнала ошибок с поиском, фильтром по типу, пагинацией, filtered export и скачиванием полного `.log`.\n- История журнала не теряется после ротации: старые файлы архивируются, при необходимости сжимаются в `.gz`, а поиск/фильтр/экспорт читают и активный лог, и архивы.\n- Агрегация активности в таблицы:\n  - `user_activity_sessions` (сессии пользователя по feature),\n  - `user_activity_daily_stats` (дневная агрегированная статистика),\n  - `analytics_events` (клиентские события радио/IPTV/видео/upload-failure).\n- Для дашборда автоматически выбирается модель аналитики:\n  - `actions` (fallback по действиям),\n  - `time_minutes` (если доступны heartbeat-данные за период).\n\n### Мультиязычность и SEO\n- Локализованные роуты: `/ru/...` и `/en/...`.\n- Автоматический редирект на язык, если префикс не указан.\n- Хранение выбранного языка в `localStorage` (`solid-social:locale`).\n- Поддержка runtime-перевода legacy-строк.\n\n## Скриншоты интерфейса\n\nАктуальная галерея интерфейса. Нажмите на изображение, чтобы открыть полный размер.\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\" width=\"33%\"\u003e\n      \u003ca href=\"docs/screenshots/home-feed.png\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n        \u003cimg src=\"docs/screenshots/home-feed.png\" alt=\"Главная страница Solid Social\" height=\"200\"\u003e\n      \u003c/a\u003e\n      \u003cbr\u003e\n      \u003csub\u003e\u003cb\u003eГлавная / лента\u003c/b\u003e\u003c/sub\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=\"33%\"\u003e\n      \u003ca href=\"docs/screenshots/chat-page.png\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n        \u003cimg src=\"docs/screenshots/chat-page.png\" alt=\"Страница чатов Solid Social\" height=\"200\"\u003e\n      \u003c/a\u003e\n      \u003cbr\u003e\n      \u003csub\u003e\u003cb\u003eЧаты\u003c/b\u003e\u003c/sub\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=\"33%\"\u003e\n      \u003ca href=\"docs/screenshots/radio.png\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n        \u003cimg src=\"docs/screenshots/radio.png\" alt=\"Радио Solid Social\" height=\"200\"\u003e\n      \u003c/a\u003e\n      \u003cbr\u003e\n      \u003csub\u003e\u003cb\u003eРадио\u003c/b\u003e\u003c/sub\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\" width=\"33%\"\u003e\n      \u003ca href=\"docs/screenshots/iptv-playing.png\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n        \u003cimg src=\"docs/screenshots/iptv-playing.png\" alt=\"IPTV с воспроизводимым каналом\" height=\"200\"\u003e\n      \u003c/a\u003e\n      \u003cbr\u003e\n      \u003csub\u003e\u003cb\u003eIPTV\u003c/b\u003e\u003c/sub\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=\"33%\"\u003e\n      \u003ca href=\"docs/screenshots/cabinet.png\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n        \u003cimg src=\"docs/screenshots/cabinet.png\" alt=\"Кабинет пользователя Solid Social\" height=\"200\"\u003e\n      \u003c/a\u003e\n      \u003cbr\u003e\n      \u003csub\u003e\u003cb\u003eКабинет пользователя\u003c/b\u003e\u003c/sub\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=\"33%\"\u003e\n      \u003ca href=\"docs/screenshots/admin-settings.png\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n        \u003cimg src=\"docs/screenshots/admin-settings.png\" alt=\"Админка: раздел настроек\" height=\"200\"\u003e\n      \u003c/a\u003e\n      \u003cbr\u003e\n      \u003csub\u003e\u003cb\u003eАдмин-панель\u003c/b\u003e\u003c/sub\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd colspan=\"3\" align=\"center\"\u003e\n      \u003csub\u003eСкриншоты открываются в новой вкладке — вернуться можно, просто закрыв её.\u003c/sub\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nКаталог со скриншотами: `docs/screenshots/`.\n\n## Карта модулей\n| Модуль | Назначение | Точка входа |\n|---|---|---|\n| Auth + профиль | Регистрация, вход, подтверждение email, кабинет | `routes/web.php`, `routes/api.php` |\n| Feed / posts | Публикации, лайки, комментарии, репосты, discover | `app/Http/Controllers/PostController.php` |\n| Realtime chat | Диалоги, presence, typing, вложения, архивы | `app/Http/Controllers/ChatController.php`, `routes/channels.php` |\n| IPTV | Загрузка плейлистов, proxy/transcode/relay сессии | `app/Http/Controllers/IptvController.php`, `app/Services/Iptv*` |\n| Radio | Поиск, подборки, воспроизведение, избранное и их распределение | `app/Http/Controllers/RadioController.php`, `app/Console/Commands/DistributeAdminRadioFavorites.php` |\n| Админка | Модерация пользователей/контента/обращений, настройки сайта | `/api/admin/*`, `app/Http/Controllers/AdminController.php` |\n| Аналитика активности | 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` |\n| Диагностика ошибок | Lifetime log сайта, preview/search/filter/export/download, archive rotation | `app/Http/Controllers/SiteErrorLogController.php`, `app/Services/SiteErrorLogService.php`, `resources/js/utils/siteErrorReporter.js` |\n| Контент главной | RU/EN контент, world overview (время/погода) | `app/Http/Controllers/SiteSettingController.php`, `app/Services/WorldOverviewService.php` |\n| i18n + SEO | RU/EN маршруты, runtime перевод, SEO мета | `resources/js/router/index.js`, `resources/js/i18n` |\n\n## Архитектура\n\n### Схема компонентов\n```mermaid\nflowchart LR\n    U[Browser / Vue SPA] --\u003e|HTTP API + Cookie Session + CSRF| N[Nginx]\n    U --\u003e|WebSocket (Pusher protocol)| R[Laravel Reverb :6001]\n    N --\u003e A[Laravel API / PHP-FPM]\n    A --\u003e DB[(MySQL 8.4)]\n    A --\u003e F[FFmpeg]\n    A --\u003e WM[Open-Meteo API]\n    A --\u003e RB[Radio Browser API]\n    A \u003c--\u003e R\n```\n\n### Слои приложения\n- **Frontend (Vue 3 SPA):**\n  - роутер: `resources/js/router/index.js`\n  - представления: `resources/js/views`\n  - i18n: `resources/js/i18n`\n- **Backend (Laravel 10):**\n  - HTTP API: `routes/api.php`\n  - web/shell: `routes/web.php`\n  - broadcast channels: `routes/channels.php`\n  - бизнес-логика: `app/Services`\n  - валидация/безопасность: `app/Rules`, `app/Http/Middleware`\n- **Realtime:** `Laravel Reverb` (сервис `websocket` в Docker).\n- **Storage/DB:** MySQL + файловое хранилище `storage`.\n\n## Методика аналитики\n\nПодробная спецификация аналитики вынесена в отдельный документ:\n\n- [docs/analytics-metrics.md](docs/analytics-metrics.md)\n\nЧто в нём зафиксировано:\n\n- точные таблицы-источники и endpoint-источники;\n- формулы всех KPI и блоков `summary`, `dashboard`, `retention`, `content`, `chats`, `media`, `radio`, `iptv`, `errors_and_moderation`;\n- логика fallback между heartbeat-метриками и action-based расчётом;\n- правила `DAU/WAU/MAU`, `stickiness`, cohort retention, `engagement per post`, `failure rate`, `mode split`;\n- команды для ручной проверки цифр через `php artisan tinker`.\n\nКороткая карта:\n\n- `GET /api/admin/summary` считает прямые `COUNT(*)` из БД.\n- `GET /api/admin/dashboard` строит единый payload через `app/Services/AdminDashboardService.php`.\n- `GET /api/admin/dashboard/export` экспортирует тот же payload через `app/Services/AdminDashboardExportService.php`, поэтому экран и Excel/JSON должны совпадать.\n- Heartbeat хранится в `user_activity_sessions` и `user_activity_daily_stats`.\n- Client analytics events для media/radio/IPTV/video хранятся в `analytics_events`.\n\nВажно:\n\n- при наличии heartbeat-данных дашборд переключается на `time_minutes`;\n- без heartbeat включается fallback по действиям;\n- transport/media/video блоки зависят от реально отправленных `analytics_events`;\n- demo/seed-данные могут смещать пики по месяцам и retention-картины.\n\n### Ключевые директории\n```text\napp/\n  Http/\n  Services/\n  Events/\n  Rules/\nresources/js/\n  views/\n  components/\n  router/\n  i18n/\nroutes/\ntests/Feature/\ndocker/\n```\n\n## Безопасность\n\n### Аутентификация и доступ\n- `Laravel Sanctum` в stateful-cookie режиме (`EnsureFrontendRequestsAreStateful`).\n- Основные API сгруппированы под `auth:sanctum` + `verified` (подтвержденный email).\n- Админские маршруты защищены middleware `admin` (`EnsureUserIsAdmin`).\n- Broadcast-каналы имеют явную авторизацию в `routes/channels.php`.\n\n### CSRF / Cookies / CORS\n- CSRF защита включена для web-запросов.\n- XSRF cookie name вынесен в env (`XSRF_COOKIE`), чтобы не конфликтовать между local и Docker.\n- Сессионный cookie name тоже разделен (`SESSION_COOKIE`).\n- CORS ограничен списком `CORS_ALLOWED_ORIGINS`, credentials включены.\n\n### Валидация входных данных\n- Запросы покрыты FormRequest/валидацией.\n- Правило `NoUnsafeMarkup` блокирует:\n  - HTML-теги,\n  - encoded-теги (`\u0026lt;...\u0026gt;`),\n  - `javascript:` / `vbscript:` / inline handlers,\n  - скрытые управляющие символы.\n- Для IPTV URL действует фильтрация:\n  - только `http/https`,\n  - блок private/reserved IP и `localhost`.\n- Upload-валидация пост-медиа опирается на серверный `MIME`/`Fileinfo`; клиентский `MIME` используется только как fallback для контейнеров вроде `mkv`, которые браузер может отправлять как `application/octet-stream`.\n- Файлы, замаскированные под видео, но распознанные сервером как не-media (`application/pdf` и т.п.), отклоняются до сохранения.\n\n### Ограничение нагрузки\n- API throttling настроен по маршрутам:\n  - авторизованные API-запросы: `throttle:600,1`;\n  - публичные `site/home-content` и `site/world-overview`: `throttle:240,1`;\n- feedback anti-spam: `throttle:20,1`;\n- public client error log: `throttle:30,1` на `POST /api/client-errors`;\n- media (`post-images`, `avatars`): `throttle:600,1`;\n- IPTV segment/playlist delivery (public relay/proxy/transcode): `throttle:1200,1`.\n- Отправка email verification notification: `throttle:6,1`.\n- Heartbeat endpoint: `throttle:120,1` на `POST /api/activity/heartbeat`.\n- Client analytics endpoint: `throttle:240,1` на `POST /api/analytics/events`.\n- IPTV сессии ограничены по количеству и TTL (proxy/transcode).\n\n## Технологический стек\n- **Backend:** PHP `^8.2` (Docker: PHP 8.3), Laravel 10, Sanctum, Reverb.\n- **Frontend:** Vue 3, Vue Router, Axios, Vite 7, Tailwind CSS 4.\n- **Media/Streaming:** HLS.js, DASH.js, MPEGTS.js, Plyr, FFmpeg.\n- **Database:** MySQL (Docker: `mysql:8.4`).\n- **Infra:** Docker Compose (Nginx + PHP-FPM + Reverb + MySQL + Node profiles).\n\n## Быстрый старт\n\n### 1) Локальный режим (без Docker)\n1. Установите зависимости:\n   - `composer install`\n   - `npm ci`\n2. Создайте `.env` из примера:\n   - Linux/macOS: `cp .env.example .env`\n   - PowerShell: `Copy-Item .env.example .env`\n   - CMD: `copy .env.example .env`\n3. Настройте подключение к БД в `.env`.\n4. Выполните инициализацию:\n   - `php artisan key:generate`\n   - `php artisan migrate --seed` (включая демо-контент: пользователи, посты, комментарии, лайки, изображения)\n   - `php artisan storage:link`\n5. Запустите процессы:\n   - `php artisan serve`\n   - `npm run dev`\n   - `php artisan reverb:start --host=0.0.0.0 --port=6001`\n6. Откройте `http://127.0.0.1:8000`.\n\n### 2) Docker режим\n1. Запуск:\n   - Обязательно создайте отдельный Docker env из шаблона `.env.docker.example`:\n     - Linux/macOS: `cp .env.docker.example .env.docker`\n     - PowerShell: `Copy-Item .env.docker.example .env.docker -Force`\n     - CMD: `copy .env.docker.example .env.docker`\n   - Не копируйте локальный `.env` в `.env.docker`: для Docker здесь нужны `DB_HOST=db` и переменные инициализации MySQL (`MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD`).\n   - `docker compose up -d --build`\n   - Если порты заняты, задайте forward-порты перед запуском (PowerShell):\n     `$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`\n2. Проверка:\n   - `docker compose ps`\n   - `docker compose top websocket`\n3. Откройте `http://localhost:8080`.\n\nВ Docker:\n- миграции запускаются автоматически (`RUN_MIGRATIONS=1`);\n- при пустой БД (например после `docker compose down -v`) сиды запускаются автоматически (`RUN_SEEDERS_ON_EMPTY_DB=1`);\n- если нужны реальные demo-фото в seed-постах, включите `DEMO_SEED_USE_REMOTE_IMAGES=1` в `.env.docker` и затем повторно запустите `docker compose exec --user=www-data app php artisan db:seed --class=DemoSocialContentSeeder --force`;\n- `ffmpeg` уже установлен в образе `app`;\n- отдельный websocket сервис поднимает Reverb на `6001`.\n- Docker-шаблон использует `CACHE_DRIVER=file`, чтобы `websocket`/Reverb не зависел от таблицы `cache` и доступности MySQL при старте.\n- при первом запуске зависимости (`composer`/`npm`) могут ставиться несколько минут, это нормально.\n- `frontend-build` это одноразовый сервис сборки ассетов, поэтому состояние `Exited (0)` для него нормально;\n- runtime-директории Laravel (`bootstrap/cache`, `storage/framework/*`) вынесены в named volumes, чтобы уменьшить лаги на Windows/macOS/Linux bind-mount.\n\n### Полезные Docker команды\n- Миграции вручную: `docker compose exec --user=www-data app php artisan migrate --seed`\n- Сиды: `docker compose exec --user=www-data app php artisan db:seed`\n- Демо-наполнение соцконтентом: `docker compose exec --user=www-data app php artisan db:seed --class=DemoSocialContentSeeder`\n- Наполнение IPTV каналов: `docker compose exec --user=www-data app php artisan db:seed --class=IptvSeedSeeder`\n- Проверка раздачи радио-избранного админа: `docker compose exec --user=www-data app php artisan radio:distribute-admin-favorites --dry-run`\n- Тесты (без пересборки): `docker compose --profile test run --rm --no-build test`\n- Тесты (с пересборкой test image): `docker compose --profile test run --rm --build test`\n- Логи: `docker compose logs --tail=100 app`\n- Остановка: `docker compose down`\n\n### Docker порты и БД\n- App: `http://localhost:8080` (переменная `WEB_FORWARD_PORT`)\n- Vite dev (`profile dev`): `http://localhost:5173` (переменная `VITE_FORWARD_PORT`)\n- Reverb websocket: `ws://localhost:6001` (переменная `REVERB_FORWARD_PORT`)\n- MySQL с хоста: `127.0.0.1:3307` (переменная `DB_FORWARD_PORT`)\n- MySQL внутри сети Docker: `db:3306`\n\n## Переменные окружения\n\n### Важно\n- Локальный режим: используйте `.env` (из `.env.example`).\n- Docker режим: сервисы используют `${DOCKER_ENV_FILE}`; рекомендуемый рабочий файл `.env.docker`, шаблон `.env.docker.example`.\n- Не смешивайте local и docker cookies/домены в одном браузерном контексте.\n- Если меняете cache backend для Docker на `database` или `redis`, он должен быть доступен и `app`, и `websocket`; для `database` также нужны таблицы `cache`/`cache_locks`.\n\n### Ключевые env-переменные\n- Приложение: `APP_ENV`, `APP_DEBUG`, `APP_URL`\n- База: `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`\n- Инициализация MySQL в Docker: `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD`\n- Безопасность/сессия: `SANCTUM_STATEFUL_DOMAINS`, `CORS_ALLOWED_ORIGINS`, `SESSION_COOKIE`, `XSRF_COOKIE`, `VITE_XSRF_COOKIE_NAME`\n- Realtime: `REVERB_*`, `VITE_REVERB_*`\n- IPTV: `IPTV_FFMPEG_BIN`\n- Radio API: `RADIO_BROWSER_BASE_URL`\n- 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`\n- 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`\n- Docker env selector: `DOCKER_ENV_FILE` (например `.env.docker`)\n\n## Тестирование\nЛокально:\n- Все тесты: `php artisan test`\n- Feature suite: `php artisan test --testsuite=Feature`\n- Rate limit и anti-spam: `php artisan test tests/Feature/ApiRateLimitFeatureTest.php`\n- Login lockout/retry-after: `php artisan test tests/Feature/LoginThrottleFeatureTest.php`\n- Медиа (доступ, fallback, cache-bust query): `php artisan test tests/Feature/MediaPostFeatureTest.php`\n- Heartbeat трекинг: `php artisan test tests/Feature/ActivityHeartbeatFeatureTest.php`\n- Дашборд/экспорт админ-аналитики: `php artisan test tests/Feature/AdminAndChatFeatureTest.php`\n- Lifetime error log, фильтры, export и archive rotation: `php artisan test tests/Feature/SiteErrorLogFeatureTest.php`\n- Сид демо-контента: `php artisan test tests/Feature/DemoSocialContentSeederFeatureTest.php`\n- IPTV: `php artisan test tests/Feature/IptvFeatureTest.php`\n- IPTV Админка (Сидеры): `php artisan test tests/Feature/AdminIptvSeedFeatureTest.php`\n- Чаты и realtime: `php artisan test tests/Feature/ChatFeatureTest.php`\n- Broadcast channels: `php artisan test tests/Feature/BroadcastChannelsFeatureTest.php`\n- Swagger/OpenAPI: `php artisan test tests/Feature/SwaggerDocumentationFeatureTest.php`\n- Админка: `php artisan test tests/Feature/AdminPanelFeatureTest.php`, `php artisan test tests/Feature/AdminAndChatFeatureTest.php`\n- JS unit-тесты helper-логики (`radio`, `iptv`, `avatar search`, `section top button`): `npm run test:js`\n- Фронт-билд: `npm run build`\n\nВ Docker:\n- Все тесты (одноразовый test-контейнер): `docker compose --profile test run --rm --no-build test`\n- Все тесты (в запущенном app-контейнере): `docker compose exec --user=www-data app php artisan test`\n- Feature suite: `docker compose exec --user=www-data app php artisan test --testsuite=Feature`\n- Конкретный файл тестов: `docker compose exec --user=www-data app php artisan test tests/Feature/ChatFeatureTest.php`\n- Фронт-билд: `docker compose run --rm frontend-build`\n\nИзоляция тестового окружения:\n- Bootstrap тестов (`tests/CreatesApplication.php`) принудительно выставляет testing-env (`sqlite :memory:`, `CACHE_DRIVER=array`, `SESSION_DRIVER=array`, `BROADCAST_DRIVER=log`) до загрузки приложения.\n- Для каждого процесса формируется `TEST_TOKEN`, а cache/storage paths переносятся в отдельный temp-каталог.\n- Это устраняет конфликты и падения тестов из-за смешивания cache/storage между запуском от `root` и `www-data` в Docker.\n\nРекомендуемый pre-commit check:\n- `php artisan test`\n- `npm run test:js`\n- `npm run build`\n\nПоследний полный прогон (1 марта 2026):\n- `php artisan test`: `205 passed` (`1760 assertions`).\n- `npm run test:js`: `33 passed`.\n\nПроверка зависимостей (1 марта 2026):\n- `npm audit`: `0 vulnerabilities`.\n- `composer audit --format=json`: `0 advisories`.\n- `doctrine/annotations` помечен как `abandoned` транзитивно через `l5-swagger`; это maintenance-вопрос без security advisory, но `composer audit` из-за этого может завершаться с non-zero exit code.\n\nРучной smoke-check интерфейса перед релизом:\n- длинные секции и кнопки `В начало` на desktop/tablet/mobile;\n- радио: карусели, счетчики, контрастные плашки, режимы `Прозрачнее/Контрастнее`, компактный mobile-блок `Сейчас играет`;\n- чаты и виджеты: кнопки удаления, контраст текста/статусов и адаптивные списки.\n\n## API ориентиры\n\n### Публичные\n- `POST /api/feedback`\n- `POST /api/client-errors`\n- `GET /api/site/home-content`\n- `GET /api/site/world-overview?locale=ru|en`\n\n### Авторизованные + verified\n- Пользователи и профиль:\n  - `GET /api/users`, `GET /api/users/{user}/posts`, `GET /api/users/following_posts`\n  - `POST /api/users/profile`, `POST /api/users/{user}/toggle_following`\n- Посты и медиа:\n  - `POST /api/post_media`, `GET /api/media/post-images/{postImage}`\n  - `GET /api/posts`, `POST /api/posts`, `GET /api/posts/discover`, `GET /api/posts/carousel`\n  - `POST /api/posts/{post}/view`, `GET /api/posts/{post}/comment`, `POST /api/posts/{post}/comment`, `DELETE /api/posts/{post}/comments/{comment}`\n- Чаты:\n  - `GET /api/chats`, `GET /api/chats/unread-summary`, `GET /api/chats/users`\n  - `POST /api/chats/direct/{user}`, `GET /api/chats/{conversation}`, `POST /api/chats/{conversation}/read`\n  - `GET /api/chats/{conversation}/messages`, `POST /api/chats/{conversation}/messages`\n  - `POST /api/chats/{conversation}/messages/{message}/reactions`\n  - `DELETE /api/chats/{conversation}/messages/{message}`\n  - `DELETE /api/chats/{conversation}/messages/{message}/attachments/{attachment}`\n  - `GET /api/chats/settings`, `PATCH /api/chats/settings`\n  - `GET /api/chats/archives`, `POST /api/chats/archives`, `GET /api/chats/archives/{archive}/download`, `POST /api/chats/archives/{archive}/restore`\n  - `PATCH /api/chats/{conversation}/mood-status`\n- Радио: `GET /api/radio/stations`, `GET /api/radio/stream`, `GET /api/radio/favorites`, `POST /api/radio/favorites`, `DELETE /api/radio/favorites/{stationUuid}`\n- 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}`\n- IPTV возможности: `GET /api/iptv/transcode/capabilities`\n- `POST /api/iptv/proxy/start`, `DELETE /api/iptv/proxy/{session}`\n- `POST /api/iptv/transcode/start`, `DELETE /api/iptv/transcode/{session}`\n- `POST /api/iptv/relay/start`, `DELETE /api/iptv/relay/{session}`\n- Activity tracking: `POST /api/activity/heartbeat`, `POST /api/analytics/events`\n\n### Админские (`/api/admin/*`)\n- Пользователи, посты, комментарии, обращения, диалоги, блокировки, настройки сайта.\n- Дашборд аналитики:\n  - `GET /api/admin/summary`\n  - `GET /api/admin/dashboard?year=YYYY\u0026date_from=YYYY-MM-DD\u0026date_to=YYYY-MM-DD`\n- `GET /api/admin/dashboard/export?format=xls|json\u0026locale=ru|en\u0026year=YYYY\u0026date_from=YYYY-MM-DD\u0026date_to=YYYY-MM-DD`\n- Диагностика lifetime error log:\n  - `GET /api/admin/error-log`\n  - `GET /api/admin/error-log/entries?search=...\u0026type=all|server_exception|client_error|analytics_failure\u0026page=1\u0026per_page=20`\n  - `GET /api/admin/error-log/export?search=...\u0026type=...`\n  - `GET /api/admin/error-log/download`\n- Экспорт запускается из админки и возвращает поток файла (`Content-Disposition`) для прямого скачивания.\n- JSON/XLS payload синхронизирован с блоками `retention`, `content`, `chats`, `media`, `radio`, `iptv`, `errors_and_moderation`.\n- Lifetime error log работает как отдельный operational diagnostics контур: хранит подробные сырые записи за весь срок жизни сайта, умеет искать по активному логу и архивам и не зависит от периодного dashboard payload.\n- Формулы и источники данных описаны в [docs/analytics-metrics.md](docs/analytics-metrics.md).\n\n## Swagger / OpenAPI\n- Swagger UI: `GET /api/documentation`\n- Docs route для JSON, который использует Swagger UI: `GET /docs?api-docs.json`\n- Сгенерированный JSON-файл на диске: `storage/api-docs/api-docs.json`\n- Генерация документации: `php artisan l5-swagger:generate`\n- Базовые аннотации: `app/OpenApi/OpenApiSpec.php`\n- Актуальная версия спецификации: `1.4.1`\n- Спецификация синхронизирована с ключевыми маршрутами `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`.\n- Для блока админ-аналитики Swagger синхронизирован с проектной методикой расчётов из [docs/analytics-metrics.md](docs/analytics-metrics.md).\n- Генерация и доступность Swagger UI дополнительно проверяются тестом `tests/Feature/SwaggerDocumentationFeatureTest.php`.\n\n## Тестовые аккаунты\nДоступны после `php artisan db:seed`:\n- `admin@example.com` / `password`\n- `user1@example.com` / `password`\n- `user2@example.com` / `password`\n- `user3@example.com` / `password`\n- `user4@example.com` / `password`\n- `user5@example.com` / `password`\n- `demo01@example.com` ... `demo18@example.com` / `password`\n\n`DemoSocialContentSeeder` дополнительно создаёт демо-посты, комментарии, лайки, подписки и placeholder-изображения.\nПо умолчанию изображения генерируются локально (без зависимости от интернета). Если нужны внешние фото из `loremflickr.com`, включите `DEMO_SEED_USE_REMOTE_IMAGES=1`.\nЕсли seed-посты уже были созданы с локальными SVG-плейсхолдерами, повторный запуск `DemoSocialContentSeeder` при `DEMO_SEED_USE_REMOTE_IMAGES=1` заменит их на реальные загруженные изображения.\n\n## Частые проблемы\n\n### `SQLSTATE[HY000] [2002] Connection refused`\n- Проверьте `DB_HOST`/`DB_PORT` в активном env-файле.\n- Из контейнеров Docker MySQL доступен как `db:3306`.\n- С хоста Windows/MySQL доступен как `127.0.0.1:3307`.\n\n### `Access denied`\n- Проверьте `DB_USERNAME`/`DB_PASSWORD` и пользователя в MySQL.\n\n### `Container social-network-db-1 is unhealthy`\n- Самая частая причина: Docker запущен с неправильным `.env.docker` или без `MYSQL_ROOT_PASSWORD`.\n- Пересоздайте docker-env из шаблона:\n  - PowerShell: `Copy-Item .env.docker.example .env.docker -Force`\n- Проверьте, что в `.env.docker` есть:\n  - `DB_HOST=db`\n  - `MYSQL_DATABASE=...`\n  - `MYSQL_USER=...`\n  - `MYSQL_PASSWORD=...`\n  - `MYSQL_ROOT_PASSWORD=...`\n- После исправления env перезапустите стек:\n  - `docker compose down -v`\n  - `docker compose up -d --build`\n\n### `ports are not available` / `bind ... already in use`\n- Порт уже занят другим приложением.\n- Запустите Docker с другими host-портами через переменные:\n  `WEB_FORWARD_PORT`, `DB_FORWARD_PORT`, `VITE_FORWARD_PORT`, `REVERB_FORWARD_PORT`.\n\n### После `docker compose down -v` пропали демо-данные\n- Это ожидаемо: флаг `-v` удаляет том MySQL (`db_data`), база пересоздаётся с нуля.\n- В текущей конфигурации контейнер `app` сам запускает `db:seed`, если таблица `users` пустая (`RUN_SEEDERS_ON_EMPTY_DB=1`).\n- Если вы отключили авто-сидирование, запустите вручную:\n  - `docker compose exec --user=www-data app php artisan db:seed --force`\n\n### В ленте видно fallback-картинку вместо фото\n- Частая причина: прерванный запрос изображения (например `499` при смене маршрута), а не отсутствие файла или интернета.\n- Маршруты media (`post-images`, `avatars`) используют отдельный лимит `throttle:600,1`.\n- Во фронтенде добавлен автоповтор загрузки изображения перед fallback (cache-bust query `?_img_retry=...`).\n- Если вы обновили код, но эффект не появился, очистите кэш:\n  - `docker compose exec --user=www-data app php artisan optimize:clear`\n  - `Ctrl+F5` в браузере\n\n### Видео не загружается в новый пост\n- Поддерживаются `mp4`, `webm`, `mov`, `m4v`, `avi`, `mkv` и изображения `jpg/jpeg/png/webp/gif`.\n- Лимит одного файла: `200 MB`.\n- В личном кабинете показываются статус и текст ошибки по каждому файлу; если загрузка отклонена, причина видна под формой.\n- Для `mkv` загрузка и скачивание поддерживаются, но встроенное воспроизведение в браузере зависит от кодеков конкретного файла.\n- В production проверьте, что `Nginx`/reverse proxy разрешает тело запроса не меньше `200M`, иначе запрос будет отклонен до Laravel.\n\n### `No services to build` при `docker compose --profile test run ...`\n- Это warning от Docker Compose, а не ошибка тестов.\n- Используйте:\n  - без пересборки: `docker compose --profile test run --rm --no-build test`\n  - с пересборкой: `docker compose --profile test run --rm --build test`\n\n### `500 /login` + `fopen(...storage/framework/cache/data/..): No such file or directory`\n- Причина: `artisan` запускался в контейнере от `root`, и часть cache-папок создавалась с правами, недоступными для `www-data`.\n- Профилактика: запускайте artisan в Docker так: `docker compose exec --user=www-data app php artisan ...`\n- Если уже сломалось:\n  - `docker compose exec app sh -lc \"chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache \u0026\u0026 find /var/www/html/storage /var/www/html/bootstrap/cache -type d -exec chmod 775 {} \\\\; \u0026\u0026 find /var/www/html/storage /var/www/html/bootstrap/cache -type f -exec chmod 664 {} \\\\;\"`\n  - `docker compose exec --user=www-data app php artisan optimize:clear`\n\n### `FilesystemIterator::__construct(...storage/framework/testing/disks/...): Permission denied` в тестах\n- Причина: тестовые storage-пути были созданы от другого пользователя (обычно `root`), после чего запуск от `www-data` не может очистить директории.\n- Профилактика: запускайте тесты в Docker от `www-data` и не смешивайте `docker compose exec app ...` и `docker compose exec --user=www-data app ...`.\n- Если уже сломалось:\n  - `docker compose exec app sh -lc \"rm -rf /var/www/html/storage/framework/testing/disks \u0026\u0026 mkdir -p /var/www/html/storage/framework/testing/disks \u0026\u0026 chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache \u0026\u0026 chmod -R ug+rwX /var/www/html/storage /var/www/html/bootstrap/cache\"`\n  - `docker compose exec --user=www-data app php artisan test`\n\n### `A temporary file could not be opened to write the process output` (Windows/OSPanel)\n- Причина: PHP/Symfony Process не может писать lock-файлы во временную директорию по умолчанию (`C:/OSPanel/temp/...`).\n- Решение: запускать тесты с локальным writable temp-каталогом проекта:\n  - PowerShell:\n    - `$tempPath = Join-Path (Get-Location) 'storage\\\\framework\\\\testing-runtime\\\\tmp'`\n    - `New-Item -ItemType Directory -Force -Path $tempPath | Out-Null`\n    - `php -d sys_temp_dir=\"$tempPath\" artisan test`\n\n### Пустая страница / сломанный фронт\n- Локально: запущен ли `npm run dev`.\n- Docker: пересоберите фронт `docker compose run --rm frontend-build`.\n- Сделайте `Ctrl+F5`.\n\n### Контейнер `frontend-build` в `Exited (0)`\n- Это нормально: сервис делает `npm run build` и завершается после успешной сборки.\n- Проверяйте именно долгоживущие сервисы: `app`, `web`, `websocket`, `db`.\n- Если после простоя сайт отвечает медленно, смотрите `docker compose ps` и `docker compose logs --tail=100 app web db websocket`, а не статус `frontend-build`.\n\n### `502 Bad Gateway` в Docker\n- Короткий `502` во время ручного `docker compose up -d --force-recreate app` возможен, пока `php-fpm` поднимается заново.\n- Постоянный `502` уже не должен быть связан со stale upstream IP: `web` резолвит `app` через Docker DNS во время работы.\n- Если `502` не исчезает после того, как `app` стал `healthy`, проверьте:\n  - `docker compose ps`\n  - `docker compose logs --tail=100 app web`\n  - `docker compose exec --user=www-data app php artisan optimize:clear`\n\n### `403` на `/api/broadcasting/auth`\n- Убедитесь, что пользователь авторизован.\n- Проверьте домен/порт (`APP_URL`) и фактический URL браузера.\n- Проверьте, что Reverb запущен.\n\n### `CSRF token mismatch`\n- Очистите cookie и обновите страницу (`Ctrl+F5`).\n- Убедитесь, что не смешаны окружения:\n  - local: `XSRF_COOKIE=social_network_local_xsrf`\n  - Docker: `XSRF_COOKIE=social_network_docker_xsrf`\n- После переключения режимов выполните logout и удалите старые cookies.\n\n### Битая аватарка или файлы из `/storage` (локально)\n- Исправление:\n  - `php -r \"if (is_link('public/storage')) unlink('public/storage');\"`\n  - `php artisan storage:link`\n\n## Деплой в production\nПодробный production-гайд (Ubuntu + Nginx + PHP-FPM + Supervisor):  \n`DEPLOY.md`\n\n## Лицензия и права использования\n- `© 2026 Rinat Sarmuldin. All rights reserved.`\n- Репозиторий распространяется по проприетарной лицензии: `LICENSE`.\n- Без письменного разрешения автора запрещены:\n  - использование в production и коммерческих проектах;\n  - копирование, изменение, распространение и создание производных работ.\n- Для получения прав на использование свяжитесь с автором: `ura07srr@gmail.com`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F07rinat07%2Fsocial-network","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F07rinat07%2Fsocial-network","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F07rinat07%2Fsocial-network/lists"}