Files
netrunner-landing/proxy-ws
2026-07-03 14:20:07 +07:00
..
2026-07-03 14:20:07 +07:00
2026-05-11 15:12:26 +07:00
2026-05-11 15:12:26 +07:00
2026-07-02 23:13:24 +07:00
2026-07-02 23:13:24 +07:00
2026-07-02 23:13:24 +07:00

proxy-ws

Rust/Axum-шлюз, стоящий между фронтендом (Next.js) и PocketBase (CMS/БД). Независимый сервис — не зависит от matrix-engine или самого фронта, общается с ними только по HTTP.

У сервиса две обязанности:

  1. AI-модерация гостевой книги — принимает комментарии с фронта, прогоняет никнейм и текст через ONNX-классификатор токсичности и, если всё чисто, форвардит запись в PocketBase.
  2. Backend для спидтеста — отдаёт несжимаемый блоб на скачивание, принимает и считает байты на загрузку, отвечает на ping — без сохранения каких-либо данных.

Эндпоинты

Метод Путь Назначение
POST /api/comments Приём сообщения гостевой книги, ИИ-модерация, форвардинг в PocketBase
GET /api/speedtest/ping Возвращает pong — для измерения RTT
GET /api/speedtest/download Отдаёт статический 25 МБ несжимаемый блоб — для измерения скорости скачивания
POST /api/speedtest/upload Принимает тело запроса, считает байты, ничего не сохраняет

Лимит тела запроса — 100 МБ (DefaultBodyLimit), нужен именно из-за speedtest upload.

Как работает модерация

Пайплайн для каждого проверяемого текста (classifier.rs):

текст -> токенизация (tokenizers) -> ONNX-модель (tract, чистый Rust, без Python) -> логиты -> сигмоида -> порог 0.85

Модель — бинарный BERT-подобный классификатор токсичности. Инференс выполняется через tract-onnx, поэтому сервису не нужен ни Python, ни какой-либо ML-рантайм на уровне ОС — только сам .onnx-файл модели и токенизатор, лежащие в models/.

Особенности пайплайна:

  • Никнейм проверяется всегда, без исключений.
  • Текст сообщения проходит гвард-рейлы перед инференсом: сообщения из whitelist безобидных слов (vpn, service, спасибо, тест и т.п.), короткие (< 4 символов) или не содержащие ни одной буквы — на модель вообще не отправляются и автоматически считаются нетоксичными. Это защищает от false positives на коротких безобидных фразах вроде "спасибо, работает" и экономит инференс.
  • Инференс — блокирующая операция, поэтому выполняется в tokio::task::spawn_blocking, чтобы не задерживать поток из пула async-раннтайма.
  • Fail-open при ошибках модели: любая ошибка на любом шаге (токенизация, прогон модели, неожиданная форма выходного тензора) означает, что текст считается нетоксичным и пропускается дальше. Это осознанный компромисс в пользу доступности сервиса — сбой модели не должен ронять или блокировать гостевую книгу целиком.

Если хотя бы один из текстов (никнейм или прошедшее гвард-рейлы сообщение) признан токсичным — сервис отвечает 406 Not Acceptable и ничего не форвардит в PocketBase.

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

Переменная Назначение Значение по умолчанию
PB_INTERNAL_URL Внутренний адрес PocketBase, куда форвардятся комментарии http://netrunner-cms:8090

Как собрать и запустить локально

Через Cargo

Нужны файлы модели в models/ (model.onnx, tokenizer.json и сопутствующие конфиги — уже лежат в репозитории).

cd proxy-ws
cargo run

Сервис поднимется на 0.0.0.0:3001.

Через Docker

Dockerfile лежит внутри proxy-ws/, но собирать образ нужно из корня репозитория (контекст сборки — вся папка proxy-ws):

docker build -t proxy-ws ./proxy-ws
docker run -p 3001:3001 -e PB_INTERNAL_URL=http://netrunner-cms:8090 proxy-ws

Сборка двухстадийная (Alpine): сначала кэшируются зависимости отдельной компиляцией фейкового main.rs, затем собирается реальный код — итоговый образ содержит только бинарник и models/, без Rust-тулчейна.