From 63bf73d2c440978dea7924d8d1220929ca465c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D0=BC=D0=B0=D1=80=D0=BE=D0=B2=20=D0=94=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=BB=20=D0=90=D0=BD=D0=B0=D1=82=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87=206?= Date: Fri, 29 May 2026 05:23:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D1=8B=D0=B9=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0=20=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=B0,=20=D0=B3=D0=BE=D0=BB?= =?UTF-8?q?=D0=BE=D1=81=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D1=8F,=20=D0=B0=D1=83=D0=B4=D0=B8?= =?UTF-8?q?=D0=BE/=D0=B2=D0=B8=D0=B4=D0=B5=D0=BE,=20Docker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сервер: - Вынесена конфигурация в server/config.js (порт, размер файла из env) - Вынесена аутентификация в server/auth.js (загрузка users.json, authenticate()) - Вынесено управление соединениями в server/clients.js (createJsonSender, broadcast, validateFile) - server/server.js стал точкой входа — минимальный код Клиент: - Вынесены хуки: hooks/useWebSocket.js (WebSocket + auth), hooks/useChatMessages.js (сообщения + уведомления) - Вынесены утилиты: utils/blob.js (base64 → Blob URL), utils/linkify.jsx (URL → ссылки), utils/notify.js (звук + favicon) Новые функции: - VoiceRecorder — запись голоса через MediaRecorder, отправка как файл - Аудио/видео плеер в Message (audio/*, video/* с controls) - URL linkification — http/https ссылки автоматически кликабельны - Звуковое уведомление (Web Audio API) при сообщении на неактивной вкладке - Красная точка на favicon при непрочитанных сообщениях Инфраструктура: - docker-entrypoint.sh: авто-перезапуск Node.js сервера при падении - Обновлён README.md: новая структура проекта, список функций, примеры - Кастомный тонкий скроллбар в Message.css --- README.md | 130 ++++++++----------- client/src/App.jsx | 71 +---------- client/src/components/Chat.jsx | 124 +----------------- client/src/components/Message.css | 20 +++ client/src/components/Message.jsx | 130 ++++++++----------- client/src/components/MessageInput.jsx | 44 ++++--- client/src/components/VoiceRecorder.css | 60 +++++++++ client/src/components/VoiceRecorder.jsx | 141 +++++++++++++++++++++ client/src/hooks/useChatMessages.js | 68 ++++++++++ client/src/hooks/useWebSocket.js | 73 +++++++++++ client/src/utils/blob.js | 16 +++ client/src/utils/linkify.jsx | 23 ++++ client/src/utils/notify.js | 51 ++++++++ docker-entrypoint.sh | 27 ++-- server/auth.js | 21 ++++ server/clients.js | 78 ++++++++++++ server/config.js | 7 ++ server/server.js | 161 ++++++++---------------- 18 files changed, 760 insertions(+), 485 deletions(-) create mode 100644 client/src/components/VoiceRecorder.css create mode 100644 client/src/components/VoiceRecorder.jsx create mode 100644 client/src/hooks/useChatMessages.js create mode 100644 client/src/hooks/useWebSocket.js create mode 100644 client/src/utils/blob.js create mode 100644 client/src/utils/linkify.jsx create mode 100644 client/src/utils/notify.js create mode 100644 server/auth.js create mode 100644 server/clients.js create mode 100644 server/config.js diff --git a/README.md b/README.md index a2fe4c6..fc8b7a4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Чат с общей комнатой -Веб-приложение чата с одной общей комнатой на WebSocket. Поддерживает текстовые сообщения, отправку файлов и отображение изображений. +Веб-приложение чата с одной общей комнатой на WebSocket. +Текстовые сообщения, файлы, изображения, аудио, видео и голосовые сообщения. **Сервер:** Node.js + `ws` **Клиент:** React + Vite @@ -18,7 +19,7 @@ npm install npm start ``` -Запускает WebSocket сервер на `ws://localhost:8080`. +WebSocket сервер на `ws://localhost:8080`. ### Клиент @@ -28,8 +29,8 @@ npm install npx vite ``` -Запускает dev-сервер на `http://localhost:3000`. -В режиме разработки клиент подключается напрямую к `ws://localhost:8080`. +Dev-сервер на `http://localhost:3000`. +Клиент подключается напрямую к `ws://localhost:8080`. ### Пользователи по умолчанию @@ -42,15 +43,10 @@ npx vite ## Production (Docker) -### Сборка образа +### Сборка и запуск ```bash docker build -t chat-app . -``` - -### Запуск контейнера - -```bash docker run -d -p 80:80 chat-app ``` @@ -59,11 +55,12 @@ docker run -d -p 80:80 chat-app ### Как это работает - **nginx** на порту 80 раздаёт статику React и проксирует WebSocket -- Путь `/` → статические файлы клиента (`index.html`, JS, CSS) -- Путь `/ws` → прокси на Node.js WebSocket сервер (порт 8080 внутри контейнера) +- Путь `/` → статические файлы клиента +- Путь `/ws` → прокси на Node.js сервер (порт 8080 внутри контейнера) - nginx поддерживает Upgrade до WebSocket, таймаут соединения — 24 часа - Максимальный размер загружаемых файлов через nginx — 50 МБ -- После остановки Node.js контейнер завершается (через `wait`) +- **Auto-restart:** если Node.js сервер упал, entrypoint перезапускает его через 1 секунду +- Чистое завершение (exit 0, SIGTERM) — контейнер останавливается без перезапуска --- @@ -77,8 +74,6 @@ docker run -d -p 80:80 chat-app | `MAX_FILE_SIZE` | `10485760` (10 МБ) | Максимальный размер файла в байтах | | `USERS_FILE` | `users.json` | Путь к файлу с пользователями | -Пример: - ```bash $env:PORT=9090; $env:MAX_FILE_SIZE=2097152; node server.js ``` @@ -89,9 +84,7 @@ $env:PORT=9090; $env:MAX_FILE_SIZE=2097152; node server.js |-----------|-------------------|--------------------------|---------| | `VITE_WS_URL` | `ws://localhost:8080` | `/ws` | URL WebSocket сервера | -Настройки хранятся в файлах: -- `client/.env.development` — для `npx vite` (dev-режим) -- `client/.env` — для `vite build` (production-сборка) +Настройки в `client/.env.development` (dev) и `client/.env` (production). --- @@ -99,81 +92,54 @@ $env:PORT=9090; $env:MAX_FILE_SIZE=2097152; node server.js ### Аутентификация -Клиент → Сервер: - ```json +// → сервер { "type": "auth", "login": "alice", "password": "123" } -``` - -Сервер → Клиент: - -```json +// ← ответ { "type": "auth_result", "success": true } -``` - -```json { "type": "auth_result", "success": false, "reason": "Invalid login or password" } ``` ### Текстовое сообщение -Клиент → Сервер: - ```json +// → сервер { "type": "text", "text": "Привет!" } -``` - -Сервер → Все: - -```json +// ← всем { "type": "text", "from": "alice", "timestamp": 1717000000000, "text": "Привет!" } ``` -### Файл / изображение - -Клиент → Сервер: +### Файл / изображение / аудио / видео ```json -{ - "type": "file", - "filename": "cat.png", - "mime": "image/png", - "data": "" -} +// → сервер +{ "type": "file", "filename": "cat.png", "mime": "image/png", "data": "" } +// ← всем (от сервера добавлены from и timestamp) +{ "type": "file", "from": "alice", "timestamp": 1717000000000, "filename": "cat.png", "mime": "image/png", "data": "" } ``` -Сервер → Все (такая же структура, добавлены `from` и `timestamp`): - -```json -{ - "type": "file", - "from": "alice", - "timestamp": 1717000000000, - "filename": "cat.png", - "mime": "image/png", - "data": "" -} -``` +Клиент определяет тип файла по MIME: `image/*` — картинка, `audio/*` — плеер, `video/*` — плеер, остальное — скачивание. ### Системные сообщения -Сервер → Все: - ```json { "type": "system", "text": "alice joined the chat" } +{ "type": "system", "text": "alice left the chat" } ``` --- ## Функциональность -- **Текстовые сообщения** — отображаются с логином отправителя и временем -- **Файлы** — любые типы, до 10 МБ (настраивается). Файлы передаются через base64 в JSON -- **Изображения** — автоматически определяются по MIME-типу (`image/*`), рендерятся как ``, клик открывает в новой вкладке -- **Не-изображения** — отображаются как ссылка для скачивания с именем файла -- **Уведомления на неактивной вкладке** — звуковой сигнал (Web Audio API) и красная точка на favicon -- **Системные сообщения** — подключение / отключение участников -- **Скролл** — автоматический, кастомный тонкий скроллбар в теме приложения +- **Текстовые сообщения** — логин отправителя, время, ссылки (http/https) автоматически становятся кликабельными +- **Файлы** — любые типы, до 10 МБ (настраивается), base64 в JSON +- **Изображения** — `image/*` → ``, клик открывает в новой вкладке +- **Аудио** — `audio/*` → `