# kuzyak.in (LLM Map) > Персональное портфолио, статистика и «Поток» Павла Кузякина. Построено на Next.js 16, Supabase и Notion. ## 🧭 Краткий обзор - **Стек**: Next.js 16.3.5 (App Router, Turbopack, `cacheComponents` RFC), React 19.3.0, TS 6.0.3, SCSS Modules (публичный сайт), Mantine UI 9 + `@mantine/hooks` + `@mantine/charts` (админка). Мониторинг ошибок: Sentry (`@sentry/nextjs`). Типографика: [`typograf`](https://github.com/typograf/typograf) v7.7+ (автоматическая обработка кавычек, тире, NBSP, висячей пунктуации для всего контента — [`docs/typography.md`](docs/typography.md)). - **Архитектура**: Feature-Sliced Design (FSD). Потоки: `app` -> `widgets` -> `features` -> `entities` -> `shared`. 58 feature-слайсов, 15 виджетов (включая `dev-stats-card`, `page-cross-link`, `admin`, `gta6-countdown`, `vacation-countdown`, `travels`, `flamingo`, `birdfeeder`). - **База данных**: Supabase (Postgres, Auth, Storage). Единственный источник истины схемы — [sql/init.sql](sql/init.sql). - **Node.js**: `22` ([.nvmrc](.nvmrc), CI); Docker-образ — `node:25-alpine`. - **Порт сервера**: `7274` (`npm run dev`, привязан к `0.0.0.0`). - **Тесты**: Vitest v5 + `@vitest/coverage-v8` + Testing Library. CI (`.github/workflows/ci.yml`) — eslint + test на PR в `main`/`develop` и push в `main`; push в `develop` CI не триггерит. Stylelint и typecheck — только в pre-commit хуке Husky. `npm run build` выполняется автоматически при сборке на Vercel. - **CI/CD**: Vercel (авто-деплой) + 4 workflow-файла в `.github/workflows/` — ci, security-audit, threads-token-refresh, webmentions-sync. ## 📂 Документация & Правила - [AGENTS.md](AGENTS.md) — **Критично!** Инструкции для AI-ассистентов, архитектурные ограничения и workflow. - [docs/architecture.md](docs/architecture.md) — Подробное описание слоев FSD и API-клиентов. - [docs/api.md](docs/api.md) — Референс всех REST эндпоинтов. - [docs/integrations.md](docs/integrations.md) — Справочник интеграций (Last.fm, Notion, Google Sheets, Threads, Strava, Groq): секреты, API, POSSE. - [docs/DESIGN.md](docs/DESIGN.md) — Дизайн-система и визуальные стандарты. - [docs/ideas.md](docs/ideas.md) — Бэклог из 77 идей для развития проекта. ## 🏗️ Ключевые модули (src/features) - `stats`: Агрегатор активности (GitHub, WakaTime, Last.fm, Strava, NASA APOD с фоллбэком на случайного кота). `/stats` — DayCard (editorial KPI-сетка; статичные плитки «эмиграция»/«трезвость» рендерятся сразу, KPI-зависимые «книги»/«коммиты» — отдельным `kpiSlot`+`Suspense`, чтобы не ждать друг друга), YearProgress, CodecovIcicle, live-счётчики (`Counter`), `PalindromeCountdown` (unix-timestamp-палиндром), `QuantumIndex` («Полевой журнал» — квантовое число дня ANU QRNG, `GET /api/stats/quantum-index`), `kpi-snapshot.json` с `dynamics` и `git.commitHourHistogram` / `favoriteCommitHour`. `/stats/2026` — Yearly Wrapped (интерактивная скролл-стори итогов года: 5 глав — код, Strava, Notion, Last.fm, шеринг-карточка; иммерсивная тёмная тема хедера и футера). `GET /api/stats/active-now` — счётчик активных посетителей за 5 мин (PostHog, ~45s кэш), виден только админам в футере. - `books`: Библиотека книг (`/stats/library`) на Notion API с пагинацией (до 1000 книг): полки «Читаю сейчас», «Дочитано», «Не дочитал», аналитический блок `ReadingStats` (темп к прошлому году, прогноз до конца года, соотношение художки и нон-фикшн, теплокарта активности) и фиксация дат в часовом поясе Еревана. - `news`: Лента новостей на Notion API (`/news`). - `guestbook`: Гостевая книга с пре-модерацией и защитой от спама (reCAPTCHA). - `cv`: Интерактивное резюме. SEO: `openGraph.type: profile`, JSON-LD `Person` (полный `knowsAbout` из skills), OG PNG `GET /cv/og/` (`renderCvOgImage` + `getCvData()`). **SoT только в БД** `app.cv_data` (`/admin/settings/job`, Zod `cvDataSchema`); статического `cv.json` в репо нет. Публичная `/cv` — SSR через `getCvPageData()` (`src/shared/lib/cv/`) → props в `CVContent` (без client-fetch); «Обновлено» из `cv_last_updated`. Статус поиска работы — бейдж в публичном хедере (`useJobStatusSettings`); на `/cv` статус в заголовке не показывается. Контакты в шапке — контрастные плоские бейджи брендовых цветов сервисов. - `now` и `uses`: Двуязычные контентные страницы на базе унифицированной оболочки `PageShell` (`src/shared/ui/PageShell/`) с серверным сайдбаром `PageAside` (`DevStatsCard`, `PageCrossLink`, `TravelsWidget`), редактированием текстов из админки (`/admin/settings/pages-content`, колонка `pages_content` в `system_settings`, API `GET/PUT /api/pages-content`) и двухколоночным адаптивным скелетоном `PageShellSkeleton`. - `museum`: Коллекция цифровых артефактов. - `weather`: Климатические метрики и графики (Raspberry Pi → Supabase). Страница `/weather`, `/weather/air`. - `meteostation`: Info-страница `/meteostation` о балконной станции (ESP32 + DS18B20 + BME280 + PMS5003 → Supabase → Pi 4 → sensor.community); live-карточки через `getWeatherSummary()`, контент в `src/features/meteostation/`. - `projects`: Showcase open-source проектов (`/projects`, bento-сетка, данные в `src/shared/data/projects.json`). - `voice`: Голосовые сообщения в Telegram через Google TTS. - `admin-*`: Слайсы админки (`admin-analytics`, `admin-guestbook`, `admin-pulse`, `admin-security`, `admin-stream`, `admin-host-metrics`). - `morse-converter`: Конвертер текста в азбуку Морзе со звуком и вибрацией (`/tools/morse`). - `nfc-reader`: Чтение, запись и бэкап NFC-меток через Web NFC API (`/tools/nfc`, только Android + Chrome). - `hire`: Страница «Нанять» (`/hire`, в sitemap). - `resume` / `/experience`: таймлайн карьеры из того же `cv_data`. - Tech Radar `/tech-radar`: opinionated стек сайта (Adopt/Trial/Assess/Hold), не полный CV; framing в meta и lead. - **Поток (`/stream`)**: IndieWeb (h-entry, JSON-LD, webmentions через webmention.io + cron `.github/workflows/webmentions-sync.yml`), POSSE в Threads (дефолт загружается из `syndication_enabled` в интеграциях, авто-обновление токена раз в 30 дней `.github/workflows/threads-token-refresh.yml`), visible toggle в bottom bar; лента фильтруется по `?q=` (поиск), `?tag=` и `?period=` (месяц/день), сайдбар `StreamAside` (календарь, архив, облако тегов, HTML-блок; `system_settings.stream.sidebar`, `/admin/stream/settings`); в редакторе — маркер `:mark[текст]{.green}`. - **Видео (`/video`)**: лента роликов YouTube и файлов медиатеки (таблица `public.videos`), по 20 + «Показать ещё» (`GET /api/videos?offset=N`), РСС `/video/rss`; YouTube — обложка, iframe по клику; управление — `/admin/video` (server actions). Слайсы: `entities/video`, `features/video-feed`. - **Кормушка (ESP32-CAM)**: виджет сайдбара `widgets/birdfeeder` (живой кадр днём, последняя птица ночью) и страница `/projects/birdfeeder` только для залогиненных (гостю 404): большой кадр, пауза, скачивание, полный экран, панель статистики распознавания, адаптивная постоянная галерея фото в Supabase Storage, модальный лайтбокс, описание устройства; оверлей для стримов `/projects/birdfeeder/overlay/` (OBS Browser Source: `card`/`bar`/`ticker`, темы `dark`/`transparent`/`light`, кастомизация через query). Прокси к шлюзу `esp32.kuzyak.in/api/camera/birdfeeder`: `src/app/api/birdfeeder/{route,frame,bird,birds,stats,usage}` — токен шлюза из `system_settings.birdfeeder_widget` подставляется на сервере, URL кадра привязан к 2-секундному окну для склейки на CDN. Общая логика — `features/birdfeeder` (`useBirdfeederStatus`, `useBirdfeederShots`, `useLiveFrame`, `describeBirdfeeder`). Шлюз (≥ `38a9e3a`) распознаёт птиц через AI Gateway: в статусе `ai`, `motionVisitsToday`, `species`, `speciesToday`, в журнале снимков `bird`/`species`/`confidence` (типы в `src/shared/types/status.ts`, необязательные); состояние шлюза, расход распознавания, архив фото, кастомизация стрим-оверлея и справочник API — на странице `/admin/pi/birdfeeder`, во вкладке «Виджеты → Кормушка» — только системные настройки. Страница включается отдельно от виджета (`birdfeeder_widget.page_enabled`), короткий адрес `/birdfeeder` и legacy `/stats/birdfeeder` → 307 на `/projects/birdfeeder`. На `/stats` — глава «Кормушка» (`src/app/stats/_birdfeeder/`): виды, рекорды и графики по часам и дням из `/api/camera/birdfeeder/stats` шлюза. - **Фламинго на главной**: виджет сайдбара `widgets/flamingo` — статус неоновой лампы ESP32 через `GET /api/flamingo` (хук в `features/flamingo`), настройки `system_settings.flamingo_widget`. - `pda` (страница `src/app/pda`, не feature-слайс): Ретро-КПК Palm m500 / Palm OS 3.5 (`/pda`) с живыми данными сайта (`getPdaData`): посты Потока, книги из Notion с прогресс-баром, гостевая книга, интерактивный Shell с терминальными командами, календарь с отметками постов, локальная диагностика Info из `navigator` и персистентные настройки (звук щелчков, подсветка ЖК-экрана). - `not-found`: Синтезирующая страница 404 (ТРИЗ Приём 22 «Обращение вреда в пользу»): нечёткий поиск опечаток по словарю 50+ разделов сайта (`findSuggestedRoutes`), динамическая карточка перехода («Похоже, вы искали: [Раздел]»), сетка трамплинов и квантовый телепорт в скрытые пасхалки, конструктивистская типографика Маяковского. - SEO robots/sitemap — не feature-слайс: UI в `src/app/admin/settings/seo/` (`GET/PUT /api/seo-files`, `POST /api/seo-files/ai-generate`). ## 🛰️ Админ-панель, Raspberry Pi и особенности - `/admin` — Mantine UI 9, light/dark тема через `useMantineColorScheme` (`defaultColorScheme="light"`). Theme-aware токены: `var(--mantine-color-default-border)`, `var(--mantine-color-body)`. Хардкод `dark-N` для поверхностей запрещён. Mantine Modal/Drawer: портал в `.adminRoot`, `--admin-*` дублируются на `body:has([data-admin-layout])`. Sidebar: группа «Аналитика» (Посещаемость, Pulse), ссылка «Настройки» → хаб `/admin/settings`; пункты с `settingsHub` скрыты, кроме `sidebar: true` (сейчас «Настройки», «Меню», «Виджеты», «Поиск работы», «База данных», «Безопасность», «SEO файлы»; в Settings Hub также «Контент страниц») — `src/shared/constants/adminNavigation.ts`. SEO: `/admin/settings/seo` — robots.txt и sitemap (`GET/PUT /api/seo-files`, `POST /api/seo-files/ai-generate` через Groq). Публичный `/robots.txt` — `src/app/robots.txt/route.ts` + `getRobotsTxt()` (`connection()`, без `revalidate`). - `/admin/pi` — дашборд Raspberry Pi: климат, эндпоинты и видеонаблюдение с камеры. Прокси-роуты: `src/app/api/admin/camera/{status,snapshot,snapshots}/route.ts` + `piProxy.ts` (`fetchPiCamera`, таймаут 15s). Имя файла снимка через `?filename=`. Учётные данные Pi — `TEMPERATURE_API_USERNAME` / `TEMPERATURE_API_PASSWORD` (Basic Auth). - `/admin/pi/birdfeeder` — кормушка: живой кадр, последняя птица, буфер SD-карты, архив фото Supabase Storage с AI-уточнением вида, шлюз, расход распознавания, статистика, кастомизация стрим-оверлея и API; доступна админу и при выключенных виджете и странице. - `/maintenance` — full-screen терминальный UI без header/footer; rewrite в `src/proxy.ts` + cookie `kuzyak_maintenance_view`, хук `useIsMaintenanceView`. В proxy — cache-first maintenance (TTL 45s, `getMaintenanceModeCache`) и таймаут Supabase RPC 3s; `/api`, `/admin` и `/monitoring` (Sentry tunnel) пропускаются без проверки. - `/tools/fuck` — пасхалка fullscreen (`noindex`), без header/footer; `src/app/tools/fuck/`, ассет `public/fuck.webp`. - `/offline` — SW-заглушка без header/footer: живой бейдж сети (`useOnlineStatus`), чек-лист, кнопки «Обновить страницу» / «На главную». - **Аутентификация**: `useAuth()` из `@/shared/contexts/AuthContext` — единая точка получения `user`/`session`. AuthProvider оборачивает дерево в `src/app/layout.tsx`. Гейтить UI-элементы по `user`. ## 🚨 Важные соглашения 1. **Стили**: Строго SCSS Modules с использованием `@use`. Проверка: `npm run stylelint` (автофикс `stylelint:fix`). Порядок CSS-свойств — `stylelint-config-idiomatic-order`. 2. **Типизация**: Strict TypeScript (`tsconfig.json`). Валидация внешних данных через Zod v4 (`zod` с `optimizePackageImports` в `next.config.mjs`). 3. **БД**: Изменения вносятся только в `sql/init.sql`. Без инкрементальных миграций. 4. **Коммиты**: Conventional Commits. Husky pre-commit: `npm run lint` (ESLint + Stylelint) + `npm run typecheck`. Обновление [CHANGELOG.md](CHANGELOG.md) обязательно. 5. **Язык**: Комментарии в коде и документация — на русском языке. 6. **API**: `successResponse`/`errorResponse` из `@/shared/utils/apiResponse`, фабрики Supabase из `shared/lib/supabase`. Server self-fetch — `fetchInternalApi` / `resolveOwnOrigin` из [`src/shared/lib/internalFetch.ts`](src/shared/lib/internalFetch.ts) (origin из `host`, не `NEXT_PUBLIC_SITE_URL` в dev; дефолтный таймаут 8s, `timeoutMs`). Внешние fetch: `sharedFetch` / `fetchIpWhoisData` (8s), Pi camera — `fetchPiCamera` (15s, [`piProxy.ts`](src/app/api/admin/camera/piProxy.ts)). 7. **Prod ops**: Деплой на Vercel (авто-деплой `main`, build previews для `develop` и PRs); healthcheck — `GET /api/health` ([`src/app/api/health/route.ts`](src/app/api/health/route.ts)); `proxy.ts` — cache-first maintenance (45s) + таймаут Supabase 3s. 8. **Mantine hooks / Shared hooks**: Для UI-состояний админки предпочитай `@mantine/hooks` (`useDisclosure`, `useLocalStorage`, `useMediaQuery`, `useDebouncedValue`) вместо ручных `useState` + `useEffect`. Для публичных страниц с бесконечным скроллом и поиском используй общий хук `useDebouncedValue` из `@/shared/hooks/useDebouncedValue`. 9. **Next.js 16 gotchas**: `trailingSlash: true` + `skipTrailingSlashRedirect: true` — точка в **последнем** сегменте (`foo.jpg`) ломает динамические API. JSON постов — `GET /api/stream/posts?slug=`. **OG PNG** (паттерн `…/og/`): `/stream/[slug]/og/`, `/weather/og/`, `/weather/air/og/`, `/cv/og/`; legacy `/api/og/*` → 308. Legacy Edge `opengraph-image`: `loadOgFonts` — динамический `import('node:fs/promises')` на Node-ветке. `cacheComponents: true` (RFC) — динамика требует `await connection()` в route handlers. Изображения: next/image, remotePatterns (Notion, Supabase, Wikimedia, picsum, и др.), unoptimized: false, SVG разрешены. 10. **Loading skeletons**: Публичные страницы используют layout-скелетоны с shimmer-анимацией. Базовый миксин в [`src/shared/styles/skeleton.scss`](src/shared/styles/skeleton.scss). Каждый `loading.tsx` повторяет структуру реальной страницы. 11. **ESLint flat config**: `eslint.config.mjs` — единый файл. Правила FSD-границ через `eslint-plugin-boundaries` v7. `import-x/order` с `@/` алиасами и `newlines-between: always`. Типы `import type {...}` — inline в фигурных скобках. 12. **Tooling**: `knip` для dead code (`npm run dead-code`), `tsx` для скриптов, `bundle-analyzer` через `npm run build:analyze`.