# `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` | Полноэкранная версия гостевой книги с постраничной подгрузкой истории |

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

- **Переводы UI** — `dictionaries/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`), т.к. там
  происходит модерация сообщений и реальные замеры скорости.
