# 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](matrix-engine/README.md) и [proxy-ws/README.md](proxy-ws/README.md). Подробности по фронтенду — в [app/README.md](app/README.md), [components/README.md](components/README.md) и [lib/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`. ## Разработка ```bash pnpm install # WASM-движок собирается отдельно (нужен Rust + wasm-pack), результат кладётся в public/wasm-matrix ./build-wasm.sh pnpm dev ``` Открыть [http://localhost:3000](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 тестовыми данными.