Files
2026-08-20 04:18:30 +09:00

12 KiB

WSChat

Self-hosted онлайн-консультант для сайта (аналог Jivo/Tidio) с Telegram в роли интерфейса оператора. Посетитель пишет через встраиваемый виджет, сообщение попадает в бэкенд, оттуда рассылается назначенным операторам в Telegram; ответ оператора возвращается в виджет по WebSocket. Предназначен для владельца одного или нескольких сайтов, который не хочет платить за SaaS-чат и держит инфраструктуру на своём VPS с FastPanel.

Стек

  • Node.js 20+, ESM.
  • Backend: Fastify 5, @fastify/cors, @fastify/rate-limit, @fastify/websocket.
  • БД: SQLite через better-sqlite3 (WAL, foreign keys), схема создаётся кодом при старте.
  • Telegram: telegraf 4, опционально через SOCKS5 (socks-proxy-agent, схема socks5h://).
  • Админ-панель: React + Vite, lucide-react.
  • Виджет: ванильный JS, сборка Vite, изоляция через Shadow DOM.
  • Деплой: npm workspaces, PM2, FastPanel (nginx/Apache под управлением панели), rsync.

Структура

Путь Назначение
package.json Корневой workspace: backend, admin-panel, widget, deploy/deploy-agent; скрипты сборки и деплоя
backend/src/server.js Fastify-приложение: HTTP API, CORS по списку сайтов, rate limit, WebSocket /ws, старт Telegram-моста
backend/src/db.js Подключение к SQLite, миграция схемы (sites, operators, site_operators, visitors, conversations, messages, settings), запросы и работа с настройками
backend/src/telegram.js Telegraf-мост: рассылка сообщений операторам, разбор ответов (reply и inline-кнопка выбора диалога), команда /status, перезапуск бота на лету
backend/src/config.js Чтение .env, значения по умолчанию, production-путь к базе
backend/.env.example Шаблон переменных окружения бэкенда
admin-panel/src/main.jsx Панель: статистика, список сайтов и операторов, embed-код, настройки Telegram/SOCKS5, лента последних сообщений, диагностика /health
admin-panel/vite.config.js Сборка с base: '/admin/'
widget/src/widget.js Встраиваемый виджет: Shadow DOM, localStorage-идентификатор посетителя, POST сообщения и WebSocket-подписка на ответы
widget/vite.config.js Сборка с фиксированным именем артефакта widget.js
deploy/vps/install.sh Установщик VPS: пакеты, Node.js, PM2, npm install, сборка фронтенда, запуск бэкенда
deploy/vps/bootstrap.sh, deploy/raspberry/bootstrap.sh Первичная подготовка VPS и Raspberry Pi
deploy/deploy-agent/src/deploy-frontend.js Агент обновления фронтенда: git pull, npm ci, сборка, rsync в webroot FastPanel с проверкой безопасности пути
docs/ Инструкции: INSTALL.md, VPS_ONLY_INSTALL.md, FASTPANEL.md, FASTPANEL_MANUAL_FRONTEND.md, RASPBERRY_PI.md, UPDATE_BUNDLE.md
.sw Журнал разработки: текущее состояние, продовые пути, диагностика, список нерешённых задач
README.upstream.md Исходное техническое задание проекта (сохранено при пересборке README)

Как запустить

Локально:

npm install
cp backend/.env.example backend/.env   # заполнить значения
npm run dev:backend                    # node --watch backend/src/server.js
npm run dev:admin                      # Vite, админка
npm --workspace widget run dev         # Vite, виджет на порту 5174

Сборка фронтенда:

npm run build          # build:admin + build:widget

Отдельных миграций нет: схема SQLite создаётся функцией migrate() при старте бэкенда, база и каталог создаются автоматически по DATABASE_PATH.

Установка на VPS (скрипт ставит пакеты, Node.js, PM2, собирает фронтенд и поднимает бэкенд под PM2):

cp deploy/vps/.env.example deploy/vps/.env
sudo bash deploy/vps/install.sh

Публикация фронтенда в webroot FastPanel:

npm run deploy:frontend    # deploy/deploy-agent, требует свой .env

Проверка:

curl -s http://127.0.0.1:3000/health
pm2 logs wschat-backend --lines 100

Встраивание виджета на сайт:

<script src="https://widget.example.ru/widget.js"
        data-site-id="site_xxxxx"
        data-api-url="https://api.example.ru"></script>

Конфигурация

backend/.env

Переменная Назначение Пример
APP_ENV Режим работы; в production меняется путь к базе по умолчанию и отключается pino-pretty production
APP_HOST Адрес прослушивания бэкенда 127.0.0.1
APP_PORT Порт бэкенда 3000
PUBLIC_API_URL Публичный URL API, отдаётся виджету через /api/config/public https://api.example.ru
PUBLIC_WS_URL Публичный WebSocket URL wss://api.example.ru/ws
TRUST_PROXY Доверять заголовкам reverse proxy true
DATABASE_PATH Путь к файлу SQLite /opt/ws-chat/data/chat.sqlite
ADMIN_ORIGIN Origin админки для CORS https://admin.example.ru
WIDGET_ORIGIN Origin виджета для CORS и домен сайта по умолчанию https://widget.example.ru
TELEGRAM_BOT_TOKEN Токен бота; при первом старте копируется в таблицу settings, дальше редактируется из админки см. .env
TELEGRAM_PROXY_ENABLED Включить SOCKS5 для Telegram false
TELEGRAM_PROXY_TYPE Тип прокси; в коде поддерживается только socks5 socks5
TELEGRAM_PROXY_HOST / TELEGRAM_PROXY_PORT Адрес и порт прокси 127.0.0.1 / 9050
TELEGRAM_PROXY_USERNAME / TELEGRAM_PROXY_PASSWORD Учётные данные прокси см. .env
JWT_SECRET Объявлен в конфиге, но нигде не используется — авторизации нет см. .env
FRONTEND_DEPLOY_ENABLED, FRONTEND_DEPLOY_MODE, FASTPANEL_SAFE_MODE Объявлены в .env.example, бэкендом не читаются false

Приоритет источников: значения Telegram/SOCKS5 после первого запуска хранятся в таблице settings SQLite и правятся через админку; .env задаёт только начальные значения.

deploy/vps/.env

PROJECT_ROOT, SOURCE_PATH, DATA_PATH, LOGS_PATH, BACKUPS_PATH, UPDATES_PATH, ADMIN_WEBROOT, WIDGET_WEBROOT, BACKEND_HOST, BACKEND_PORT, PUBLIC_API_URL, PUBLIC_WS_URL, FASTPANEL_SAFE_MODE, PM2_PROCESS_NAME, NODE_MAJOR.

deploy/deploy-agent/.env

FRONTEND_DEPLOY_BRANCH, FRONTEND_DEPLOY_SOURCE_PATH, FRONTEND_DEPLOY_ADMIN_WEBROOT, FRONTEND_DEPLOY_WIDGET_WEBROOT, FASTPANEL_SAFE_MODE. При FASTPANEL_SAFE_MODE != false webroot обязан лежать внутри /var/www/<user>/data/www/<domain>, иначе деплой прерывается.

Состояние

Рабочий MVP, разработка остановлена. 106 коммитов, все от 2026-05-08, последний коммит — 2026-05-08. По записям .sw система была развёрнута и работала: виджет и админка на widget.stackworks.ru, API на api.stackworks.ru, бэкенд под PM2, SQLite в /opt/ws-chat/data/chat.sqlite.

Сквозной сценарий реализован полностью: виджет → API → Telegram-оператор → ответ → WebSocket → виджет. Управление сайтами, операторами и настройками Telegram/SOCKS5 работает из админки.

Raspberry Pi (изначальная цель проекта, см. README.upstream.md и docs/RASPBERRY_PI.md) из MVP исключён — в .sw указана причина: нестабильность Node.js/ОС на Pi.

Что не доделано

Явных TODO/FIXME в коде нет; список ниже собран из кода и раздела «Current next steps» в .sw.

  • Нет авторизации в админ-панели. Все эндпоинты /api/admin/* открыты без проверки; JWT_SECRET объявлен, но не используется. Это главный незакрытый риск, зафиксированный и в самом .sw.
  • Нет страницы диалогов в админке: есть только лента последних сообщений, нельзя открыть переписку целиком.
  • Не реализованы статусы диалогов (open/closed) и назначение оператора «взять в работу» — поля status и assigned_operator_id в БД есть, UI и API для них нет.
  • Нет удаления/редактирования сайтов и операторов: реализованы только GET/POST сайтов и привязка/отвязка операторов.
  • Не реализована обещанная в ТЗ система деплоя из админки: нет ни кнопки обновления, ни GitHub-вебхука, ни откатов, ни логов деплоя. Есть только CLI-агент deploy-frontend.js.
  • test-proxy не проверяет соединение — функция validateSocks5Settings только валидирует поля формы.
  • Нет офлайн-режима (сбор имени/телефона/email посетителя), звуковых уведомлений и счётчиков непрочитанного.
  • Нет кастомизации виджета из админки (заголовок, цвет, позиция) — параметры зашиты в widget.js.
  • Нет тестов и CI: тестовых файлов, конфигов линтера и workflow в репозитории нет.
  • Планы, отмеченные в .sw как отложенные: перенос админки на отдельный поддомен, переход с SQLite на PostgreSQL, Docker Compose/установщик, JWT и API под будущее Android-приложение.