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:
telegraf4, опционально через 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-приложение.