Files
netrunner-landing/app
nineap 383077e1e6 Метрики /metrics для Next.js и ws-proxy (центральный Prometheus)
proxy-ws (Rust/axum) — тот же паттерн, что api.rs в netrunner-backend:
PrometheusBuilder + middleware, считающий http_requests_total/
http_request_duration_seconds по методу/пути/статусу.

Next.js — app/api/metrics/route.ts на prom-client (явный
runtime="nodejs", т.к. Edge Runtime не тянет perf_hooks/process.*),
отдаёт default-метрики процесса (CPU/память/event loop lag).

Оба порта (3000/3001) перестают быть 127.0.0.1-only — их должен
скрейпить центральный Prometheus на VPS с данными, обязательно
зафайрволить там на его IP (см. комментарии в docker-compose.yml).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 23:21:43 +07:00
..
2026-07-07 18:49:09 +07:00

app/ — маршруты Next.js App Router

Весь пользовательский контент живёт под динамическим сегментом app/[lang]/, кроме нескольких файлов верхнего уровня, которые Next.js требует держать вне локализованных сегментов (not-found.tsx, loading.tsx, globals.css, favicon.ico).

Как работает i18n через [lang]

  1. Корневой proxy.ts (Next.js middleware) перехватывает каждый запрос. Если путь уже начинается с /en или /ru — пропускает его как есть. Если локали в пути нет (например, /) — определяет язык браузера через заголовок Accept-Language (Negotiator + @formatjs/intl-localematcher) и редиректит на /en... или /ru....
  2. Дальше уже сам Next.js резолвит запрос в app/[lang]/..., а params.lang передаётся во все page.tsx/layout.tsx этого сегмента.
  3. generateStaticParams в проекте не используется — страницы под [lang] не пре-генерируются статически на билде для en/ru, а рендерятся динамически (server-side) по каждому запросу, с точечным ISR-кэшированием там, где это нужно (см. revalidate в запросах к PocketBase в Faq.tsx, BlogPreview.tsx, blog/page.tsx).
  4. Строковый контент (заголовки, лейблы, кнопки) берётся не из PocketBase, а из JSON-словарей dictionaries/en.json / dictionaries/ru.json, которые грузятся через getDictionary(lang) из lib/dictionaries.ts (только на сервере, файл помечен import "server-only").

Паттерн page.tsx + *-client.tsx

Почти каждый раздел состоит из пары файлов:

  • page.tsx — серверный async-компонент. Получает params.lang, грузит словарь (getDictionary) и/или данные из PocketBase (lib/pb.ts), передаёт их вниз как пропсы.
  • *-client.tsx — клиентский компонент ("use client") с интерактивностью: анимации на framer-motion, локальное состояние, realtime-подписки PocketBase, запросы к Rust-шлюзу proxy-ws и т.д.

Разделение оправдано тем, что серверная часть не тянет в клиентский бандл лишний JS (словари, PocketBase SDK для первого запроса), а клиентская часть свободно использует хуки и браузерные API.

Есть и раздел без клиентской пары — blog и blog/[slug], а также Faq.tsx/ BlogPreview.tsx в components/sections/: там контент статичен после загрузки (no client-only interactivity), поэтому они остаются серверными компонентами целиком.

Разделы

Маршрут Файлы Назначение
/ page.tsx Главная: статично рендерит Hero+Features, а Pricing/FAQ/TerminalGuestbook/BlogPreview подгружает через next/dynamic (code-splitting тяжёлых интерактивных секций, см. комментарий в файле)
/auth page.tsx + auth-client.tsx Авторизация через Telegram Login Widget против бэкенда на account.netrunner-vpn.com (/users/me, /auth/telegram, credentials: "include"); сессия — только HttpOnly cookie с Domain=.netrunner-vpn.com, лендинг ничего не хранит сам (ни localStorage, ни токен в URL). После успеха — полный переход на {ACCOUNT_ORIGIN}/profile (ЛК физически в другом сервисе, не в этом репо)
/blog page.tsx Список опубликованных постов из PocketBase (posts_{lang}), ISR-кэш на час
/blog/[slug] page.tsx Отдельный пост блога, контент рендерится как HTML (dangerouslySetInnerHTML), без кэша (cache: "no-store")
/docs page.tsx + docs-client.tsx "Хранилище" документов (documents_{lang}) с поиском и фильтром по категориям, читалка на весь экран
/download page.tsx + download-client.tsx Клиенты VPN по платформам из PocketBase (downloads_{lang}), ссылки на прямое скачивание/магазины
/nodes page.tsx + nodes-client.tsx Список VPN-серверов со статусом/пингом/загрузкой — сейчас данные моковые, реального API ещё нет
/speedtest page.tsx Обёртка вокруг components/sections/SpeedtestClient.tsx — измерение скорости через Rust-шлюз proxy-ws
/terminal page.tsx + terminal-client.tsx Полноэкранная версия гостевой книги с постраничной подгрузкой истории

Откуда берутся данные

  • Переводы UIdictionaries/en.json / dictionaries/ru.json через lib/dictionaries.ts.
  • Динамический контент (посты, FAQ, тарифы, документы, платформы загрузки, комментарии гостевой книги) — PocketBase через синглтон lib/pb.ts. Коллекции обычно разделены по языку суффиксом (posts_en/posts_ru, faqs_en/faqs_ru и т.д.).
  • Гостевая книга и спидтест отправляют/читают трафик не напрямую в PocketBase, а через Rust-шлюз proxy-ws (см. /api/proxy/* в next.config.ts), т.к. там происходит модерация сообщений и реальные замеры скорости.