2026-07-04 22:40:12 +07:00
2026-07-07 18:49:09 +07:00
2026-07-07 18:49:09 +07:00
2026-04-25 14:57:50 +07:00
ci
2026-04-21 19:33:42 +07:00
2026-07-05 20:01:53 +07:00
2026-04-18 21:17:51 +10:00
2026-04-17 14:54:33 +07:00
2026-05-11 15:37:44 +07:00
2026-04-20 13:26:07 +07:00
2026-07-02 23:13:24 +07:00
2026-04-17 14:54:33 +07:00
2026-07-04 22:40:12 +07:00
2026-07-07 18:49:09 +07:00

Netrunner VPN — Landing

Промо-сайт и мини-платформа VPN-сервиса «Netrunner»: Next.js 16 (App Router) фронтенд с двумя языками (en/ru), PocketBase в роли headless CMS и два самописных Rust-сервиса — WebAssembly-движок анимированного фона «матрицы» и WebSocket/HTTP-шлюз с AI-модерацией и speedtest-эндпоинтами.

Стек

  • Next.js 16 (App Router, React 19, Server Components) + TailwindCSS 4
  • PocketBase — headless CMS/БД для блога, FAQ, ссылок на загрузки и гостевой книги
  • Rust → WebAssembly (matrix-engine/) — физика фоновой анимации, рендер через WebGL2 в Web Worker
  • Rust / Axum (proxy-ws/) — прокси-шлюз: ONNX-модель токсичности для фильтрации сообщений + генератор нагрузки для speedtest
  • Docker Compose — три контейнера в проде: landing, netrunner-cms (PocketBase), ws-proxy

Структура репозитория

app/[lang]/          Страницы Next.js (App Router), маршруты локализованы через /en/* и /ru/*
components/sections/ Крупные блоки страниц (Hero, Pricing, Terminal-гостевая книга и т.д.)
components/ui/       低-уровневые UI-примитивы (shadcn/radix обвязка)
lib/                 Общие утилиты: клиент PocketBase, загрузчик словарей, типы
dictionaries/        JSON со строками локализации (en.json / ru.json)
matrix-engine/        Rust/WASM-движок фонового "дождя из матрицы" + его README
proxy-ws/              Rust/Axum-шлюз: AI-модерация комментариев и speedtest + его README
public/wasm-matrix/   Скомпилированный вывод matrix-engine, который реально грузит браузер
public/games/doom.zip  Пасхалка — DOOM в браузере на /doom
proxy.ts               Next.js "proxy" (аналог middleware) — определение и редирект локали

Подробности по Rust-подсистемам — в их собственных README: matrix-engine/README.md и proxy-ws/README.md. Подробности по фронтенду — в app/README.md, components/README.md и lib/README.md.

Как это работает вместе

  1. Next.js middleware (proxy.ts) на каждый запрос без /en или /ru в пути определяет язык браузера (Negotiator + @formatjs/intl-localematcher) и редиректит на /{locale}/.... /doom, /doom.html и /games/* из этой логики специально исключены.
  2. Серверные страницы в app/[lang]/... подгружают словарь (lib/dictionaries.ts) и контент из PocketBase (lib/pb.ts) — блог, FAQ, ссылки на загрузки и т.п.
  3. Фоновая WebGL-анимация (matrix-engine) инициализируется клиентским компонентом NetrunnerMatrix — она работает в отдельном Web Worker поверх OffscreenCanvas, не блокируя основной поток.
  4. Гостевая книга (components/sections/TerminalGuestbook.tsx) отправляет сообщения не напрямую в PocketBase, а через /api/proxy/* (rewrite в next.config.ts на ws-proxy:3001). Rust-шлюз прогоняет никнейм и текст через ONNX-классификатор токсичности и только потом пишет запись в PocketBase.
  5. Спидтест (components/sections/SpeedtestClient.tsx, страница /speedtest) бьёт напрямую в ws-proxy за /api/speedtest/ping|download|upload.

Разработка

pnpm install

# WASM-движок собирается отдельно (нужен Rust + wasm-pack), результат кладётся в public/wasm-matrix
./build-wasm.sh

pnpm dev

Открыть http://localhost:3000 — редиректнёт на /en или /ru по языку браузера.

Полезные переменные окружения (.env.local):

Переменная Назначение
NEXT_PUBLIC_PB_URL Публичный URL PocketBase (используется в браузере)
PB_INTERNAL_URL Внутренний URL PocketBase для SSR (обычно http://netrunner-cms:8090 в докере)
NEXT_PUBLIC_PROXY_URL Публичный URL Rust-шлюза (для клиентских fetch, минуя rewrite)
NEXT_PUBLIC_API_URL База API бэкенда (netrunner-backend) для auth-эндпоинтов — /users/me, /auth/telegram. По умолчанию ${NEXT_PUBLIC_ACCOUNT_ORIGIN}/api/v1
NEXT_PUBLIC_ACCOUNT_ORIGIN Origin ЛК (netrunner-backend, сабдомен account.*) — куда редиректить после успешного логина
NEXT_PUBLIC_BOT_USERNAME Юзернейм Telegram-бота для виджета логина
PB_ADMIN_EMAIL / PB_ADMIN_PASS Только для одноразового скрипта upload.mjs (сид данных в PocketBase)

Сборка и деплой

  • Dockerfile — многостадийная сборка: сначала компилирует matrix-engine в WASM (wasm-pack build --target web --release), кладёт результат в public/wasm-matrix, затем собирает Next.js и упаковывает в минимальный node:20-alpine рантайм.
  • proxy-ws/Dockerfile — отдельный образ Rust-шлюза (кэширует зависимости через BuildKit cache mounts, чтобы не пересобирать crates на каждый билд).
  • docker-compose.yml — прод: поднимает три сервиса: landing, netrunner-cms (PocketBase) и ws-proxy, все в одной bridge-сети netrunner-network.
  • docker-compose.dev-remote.yml — dev: тот же набор сервисов на отдельном dev VPS (по аналогии с netrunner-backend, тот же хост — 45.76.161.137 / DNS dev.netrunner-vpn.com), сам лендинг без reverse-proxy (порт 3000 открыт напрямую, обычный HTTP), но смотрит на dev-бэкенд уже через Caddy на https://dev.netrunner-vpn.com (порт 443, не голый :8080) — у netrunner-backend там настоящий TLS, нужный и для Telegram Web Apps (кнопки бота) и Secure-cookie, и для Telegram Login Widget (требует в браузере именно порт 443). Домен, а не IP: Telegram Login Widget требует настоящий домен, зарегистрированный в BotFather под отдельным dev-ботом (BOT_USERNAME для этого стека — обязательная переменная, без дефолта на прод-бота, см. комментарии в файле).
  • .github/workflows/build.yml — на пуш в main (и вручную) собирает и пушит оба образа (netrunner-landing, netrunner-landing-proxy) в GHCR.
  • .github/workflows/deploy.yml — две джобы: deploy-dev срабатывает автоматически сразу после build.yml и обновляет dev VPS (docker-compose.dev-remote.yml); deploy-prod — только вручную (Actions → Run workflow), обновляет прод (docker-compose.yml). Секреты: VPS_IP/SSH_PRIVATE_KEY (прод), DEV_VPS_IP/DEV_SSH_PRIVATE_KEY (dev) — заводятся в Settings → Secrets репозитория.
  • deploy.sh — тот же процесс вручную для прода, для локального запуска без ожидания CI.
  • ecosystem.config.js — конфиг PM2, альтернативный способ запускать next start без Docker (используется, если сервис поднимается процесс-менеджером на самом VPS).

Разовые dev-утилиты не являются частью приложения и не запускаются в проде:

  • collect_code.js — сбрасывает все .ts/.tsx файлы из app/, components/, lib/ в один текстовый файл (удобно для передачи контекста в LLM).
  • upload.mjs — одноразовый скрипт сидирования коллекций downloads_en/downloads_ru в PocketBase тестовыми данными.
S
Description
No description provided
Readme 46 MiB
Languages
TypeScript 53.8%
JavaScript 28.4%
Rust 12.6%
CSS 1.8%
Shell 1.2%
Other 2.2%