Files
min-chat/README.md
Комаров Данил Анатольевич 6 199a3ea2fe Обновлён README, добавлена лицензия MIT
2026-05-29 16:42:32 +03:00

7.7 KiB
Raw Permalink Blame History

Чат с общей комнатой

Веб-приложение чата с одной общей комнатой на WebSocket.
Текстовые сообщения, файлы, изображения, аудио, видео и голосовые сообщения.

Сервер: Node.js + ws
Клиент: React + Vite
Прокси: nginx (production-сборка)


Быстрый запуск (разработка)

Сервер

cd server
npm install
npm start

WebSocket сервер на ws://localhost:8080.

Клиент

cd client
npm install
npx vite

Dev-сервер на http://localhost:3000.
Клиент подключается напрямую к ws://localhost:8080.

Пользователи по умолчанию

Логин Пароль
alice 123
bob 456

Production (Docker)

Сборка и запуск

docker build -t chat-app .
docker run -d -p 80:80 chat-app

Открой http://localhost.

Как это работает

  • nginx на порту 80 раздаёт статику React и проксирует WebSocket
  • Путь / → статические файлы клиента
  • Путь /ws → прокси на Node.js сервер (порт 8080 внутри контейнера)
  • nginx поддерживает Upgrade до WebSocket, таймаут соединения — 24 часа
  • Максимальный размер загружаемых файлов через nginx — 50 МБ
  • Auto-restart: если Node.js сервер упал, entrypoint перезапускает его через 1 секунду
  • Чистое завершение (exit 0, SIGTERM) — контейнер останавливается без перезапуска

Переменные окружения

Сервер

Переменная По умолчанию Описание
PORT 8080 Порт WebSocket сервера
MAX_FILE_SIZE 10485760 (10 МБ) Максимальный размер файла в байтах
USERS_FILE users.json Путь к файлу с пользователями
$env:PORT=9090; $env:MAX_FILE_SIZE=2097152; node server.js

Клиент

Переменная По умолчанию (dev) По умолчанию (production) Описание
VITE_WS_URL ws://localhost:8080 /ws URL WebSocket сервера

Настройки в client/.env.development (dev) и client/.env (production).


Протокол обмена

Аутентификация

// → сервер
{ "type": "auth", "login": "alice", "password": "123" }
// ← ответ
{ "type": "auth_result", "success": true }
{ "type": "auth_result", "success": false, "reason": "Invalid login or password" }

Текстовое сообщение

// → сервер
{ "type": "text", "text": "Привет!" }
// ← всем
{ "type": "text", "from": "alice", "timestamp": 1717000000000, "text": "Привет!" }

Файл / изображение / аудио / видео

// → сервер
{ "type": "file", "filename": "cat.png", "mime": "image/png", "data": "<base64>" }
// ← всем (от сервера добавлены from и timestamp)
{ "type": "file", "from": "alice", "timestamp": 1717000000000, "filename": "cat.png", "mime": "image/png", "data": "<base64>" }

Клиент определяет тип файла по MIME: image/* — картинка, audio/* — плеер, video/* — плеер, остальное — скачивание.

Системные сообщения

{ "type": "system", "text": "alice joined the chat" }
{ "type": "system", "text": "alice left the chat" }

Функциональность

  • Текстовые сообщения — логин отправителя, время, ссылки (http/https) автоматически становятся кликабельными
  • Файлы — любые типы, до 10 МБ (настраивается), base64 в JSON
  • Изображенияimage/*<img>, клик открывает в новой вкладке
  • Аудиоaudio/*<audio controls> + ссылка скачивания
  • Видеоvideo/*<video> с controls + ссылка скачивания
  • Голосовые сообщения — кнопка микрофона, запись через MediaRecorder, отправка как файл
  • Уведомления — звук (Web Audio API) и красная точка на favicon при сообщении на неактивной вкладке
  • Системные сообщения — подключение/отключение участников
  • Автоскролл — кастомный тонкий скроллбар
  • Вставка изображений из буфера обмена — Ctrl+V в поле ввода, отправка как файл
  • Авто-переподключение — при разрыве WebSocket клиент автоматически пересоздаёт соединение
  • Сохранение учётных данных — логин и пароль сохраняются в localStorage, при повторном входе поле не требуется
  • Сохранение истории — последние 50 сообщений (текст, файлы) сохраняются в localStorage
  • Закреплённые сообщения — любое сообщение можно закрепить в шапке чата

Структура проекта

chat/
├── server/
│   ├── config.js           # Настройки из env
│   ├── auth.js             # Загрузка пользователей, authenticate()
│   ├── clients.js          # Управление соединениями, broadcast()
│   ├── server.js           # Точка входа, WebSocketServer
│   ├── users.json          # Хардкоженые логины/пароли
│   └── package.json
├── client/
│   ├── src/
│   │   ├── App.jsx / .css
│   │   ├── main.jsx
│   │   ├── hooks/
│   │   │   ├── useWebSocket.js       # WebSocket + auth
│   │   │   └── useChatMessages.js    # Приём сообщений + уведомления
│   │   ├── utils/
│   │   │   ├── blob.js               # base64 → Blob URL
│   │   │   ├── linkify.jsx           # URL → React-ссылки
│   │   │   └── notify.js             # Звук + favicon
│   │   └── components/
│   │       ├── Login.jsx / .css
│   │       ├── Chat.jsx / .css
│   │       ├── MessageList.jsx
│   │       ├── Message.jsx / .css
│   │       ├── MessageInput.jsx / .css
│   │       └── VoiceRecorder.jsx / .css
│   ├── .env                 # Production настройки
│   ├── .env.development     # Dev настройки
│   ├── index.html
│   ├── vite.config.js
│   └── package.json
├── Dockerfile               # Многостадийная сборка
├── docker-entrypoint.sh     # Auto-restart при падении
├── nginx.conf
├── .dockerignore
├── .gitignore
├── LICENSE
└── README.md