Cluster · разработчикам

Сайт

Сайт написан на React и TypeScript. Исходники находятся в web/src/, готовая сборка в web/dist/. Сайт работает с HTTP API ядра. В пакет cluster-core интерфейс не входит: его можно обновлять и размещать отдельно.

Сборка

Нужны Python 3.12 или новее и Node.js, совместимый с Vite 8. Зависимости закреплены в web/package-lock.json. Python собирает публичную документацию без сторонних пакетов.

cd web
npm ci
npm test
npm run build

После изменения исходников сборку нужно повторить. Для разработки есть npm run dev. Он проксирует /api/ на 127.0.0.1:8081; API должен принимать внешний адрес страницы через --origin. Для HTTP используй разрешённый ядром адрес http://127.0.0.1:8080; для другого адреса нужен HTTPS.

Локальный запуск

Ключ и настройки почты описаны в развёртывании. Запусти ядро, API и сайт в разных терминалах:

.venv/bin/cluster --path ./data serve --key-file /path/to/core.key
.venv/bin/cluster --path ./data api --origin http://127.0.0.1:8080 --port 8081
.venv/bin/python scripts/serve_web.py --listen 127.0.0.1:8080 --api http://127.0.0.1:8081

Открой http://127.0.0.1:8080. Скрипт отдаёт web/dist/ и передаёт запросы /api/ службе API. Заголовок Origin сохраняется. Тела запросов и токены в журнал скрипта не попадают. Встроенная HTTP-служба ядра по-прежнему отдаёт только API.

Страница и API доступны браузеру по одному адресу. При обычном входе используется cookie сеанса и CSRF. В Mini App служебные токены хранятся только в памяти страницы. После выхода они удаляются; в локальное хранилище браузера записывается только выбранный язык.

Разделы

Поля модулей строятся по описанию интерфейса. Списки редактируются поэлементно, числовые поля с конечными границами получают ползунок. Для объектов и вложенных списков есть редактор с применением и отменой. Сохранённые секреты не возвращаются в форму. Права выбираются отдельно от ограничений по чатам, пользователям и адресам.

Доступ на сайте зависит от выбранного юзербота и роли профиля. Окончательную проверку каждого действия выполняет ядро. Скрытие пункта меню само по себе не является защитой.

Размещение

Отдавай содержимое web/dist/ статическим сервером, а /api/* направляй на API ядра. Нужен HTTPS и совпадающий --origin. Разделы панели находятся после #. Главная доступна по / на русском и /en/ на английском, юридические документы по /legal/operator, /legal/terms, /legal/privacy и /legal/refunds. Старые ссылки /#/legal/... продолжают работать.

npm run build собирает интерфейс и запускает web/prerender.mjs. Он сохраняет главную и юридические документы в HTML с текстом, описанием и основным адресом страницы. После загрузки JavaScript работают те же компоненты React. Подготовка HTML не обращается к API и не включает данные пользователей. sitemap.xml содержит только эти страницы и открытую документацию. robots.txt закрывает служебные адреса от обхода, но не заменяет авторизацию API.

Статический сервер должен отдавать /legal/terms из файла /legal/terms.html и аналогично для остальных документов. Несуществующие адреса должны возвращать HTTP 404, а не главную страницу.

Режим оплаты сайт получает из billing/info. При рабочем Stripe пользователь переходит на страницу провайдера. Тестовый режим обозначается отдельно. Ключи оплаты в сборку сайта не попадают.

Раздел /developers/ собирается из docs/ командой python3 scripts/build_docs.py. Команды npm run build и npm run dev выполняют её перед запуском Vite. После изменения документов во время разработки выполни npm run docs. Готовые страницы, примеры и карта процессов входят в web/dist/developers/. Личные справочники и черновики из домашних каталогов сборщик не использует.

Руководства из guides/ собираются той же командой в web/public/guides/, затем попадают в web/dist/guides/. У них отдельные адреса /guides/connect.html, /guides/commands.html и /guides/modules.html. Сборка добавляет руководства в карту сайта. Связанные примеры из каталога проекта копируются рядом в web/public/examples/; личные файлы не используются.

Иконки и знак Cluster нарисованы в исходниках сайта. Внешние шрифты и наборы иконок не загружаются. Уведомления о лицензиях React и его зависимостей находятся в web/public/third-party-licenses.txt и копируются в сборку.

Пример раздачи через Caddy. Пути и домен замени на свои:

cluster.example.com {
    encode zstd gzip
    handle /api/* {
        reverse_proxy 127.0.0.1:8080 {
            header_up X-Real-IP {remote_host}
            header_up -X-Forwarded-For
        }
    }
    handle {
        root * /srv/cluster/web/dist
        try_files {path} {path}.html
        file_server
    }
    header {
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options nosniff
        Referrer-Policy same-origin
        Content-Security-Policy "default-src 'self'; style-src 'self'; font-src 'self'; img-src 'self'; script-src 'self' https://telegram.org; connect-src 'self'; form-action 'self'; base-uri 'self'; frame-ancestors 'self' https://web.telegram.org https://*.telegram.org"
    }
}