Open Source
web
Cosmos Explorer
Интерактивная 3D-модель Солнечной системы и энциклопедия космических миссий — фронт на Next.js 15 + Three.js, бэкенд на Go + Connect-RPC, 281 пререндеренная двуязычная страница и живой трекер МКС
В цифрах
0
Статических страниц
0
Космических миссий
0
Протокола, один эндпоинт
0
Фронтенд-тестов
Проблема
Что я решала
3D-сайты про космос обычно попадают в один из двух лагерей. Либо красивое WebGL-демо без реального контента, без SEO и без единой страницы, которую может проиндексировать поисковик — либо контентная энциклопедия со статикой и нулевой интерактивностью. Я хотела и то, и другое сразу: реалтайм-сцену на Three.js, по которой можно летать, И сотни статически отрендеренных, индексируемых, двуязычных страниц — не держа при этом две кодовые базы или два API, которые разъезжаются.
Мой подход
Как я делала
Next.js 15 App Router в режиме standalone. 3D-сцена — клиентский остров; страницы планет и миссий — SSG + ISR: 281 пререндерится на сборке и обновляется по cache-тегам, а не полным ребилдом. Бэкенд на Go поднимает один Connect-RPC-сервис, который отдаёт gRPC, gRPC-Web и JSON с одного эндпоинта — и браузер, и серверные фетчи Next.js, и любой будущий gRPC-потребитель бьют в один контракт. Контракт — Protobuf-схема: buf генерирует и TS-клиент, и Go-стабы из единой .proto, так что нет ручных DTO, которые надо синхронизировать. WebSocket-хаб стримит позицию МКС раз в секунду на математике SGP4. Всё в Docker: distroless-бэкенд без root, alpine-фронт без root. Деплой — GitHub Actions в GHCR и на VPS с health-gate на 120 с, автооткатом на последний рабочий SHA и прогревом ISR. Фокус сборки: рядом с build-шагом поднимается одноразовый Postgres + API, чтобы фронт пререндерил все 281 страницу против реальных данных — запечёнными прямо в образ.
Выбор технологий
- Connect-RPC (one endpoint, three protocols)— Один сервис говорит на gRPC, gRPC-Web и обычном JSON с одного эндпоинта. Браузер использует сгенерированный Connect-клиент, серверные фетчи Next.js бьют в тот же контракт, и нет отдельного REST-слоя, который надо вести. gRPC-Web без отдельного прокси-сайдкара — то, что меня и подкупило.
- Protobuf + buf codegen— Единственный источник истины — .proto. buf генерирует из неё TypeScript-клиент и Go-стабы, поэтому переименование поля — это одна правка схемы, а не охота по ручным DTO с двух сторон. Контракт физически не может разъехаться между фронтом и бэком.
- Next.js 15 SSG + ISR (281 pages)— 3D-сцене нужен клиент, а энциклопедии — индексируемость и скорость. SSG пререндерит все 281 страницу на сборке; ISR обновляет их по cache-тегу при изменении контента, так что правка одной миссии не запускает полный ребилд. Лучшее от статики и динамики без выбора между ними.
- Three.js r171 + post-processing— EffectComposer с bloom даёт сцене глубину без тяжёлого движка. Орбитальная механика — настоящая математика в художественном масштабе: Земля вращается на читаемом расстоянии, а не точкой в истинном масштабе. Освобождение геометрий и материалов при очистке не даёт долгой сессии течь по GPU-памяти.
Результат
Что получилось
Живёт на cosmos.lavier.tech. 8 планет с орбитальной механикой и bloom, энциклопедия из 128 миссий с фильтрами и поиском, живой трекер МКС по WebSocket и управление временем — паузой и ускорением неба. 281 двуязычная статическая страница с JSON-LD, hreflang, sitemap из БД и OG-изображениями на каждую сущность — быстро и индексируемо, а не чёрный ящик-канвас. PWA с офлайн-режимом. Один Connect-RPC-эндпоинт обслуживает три протокола из единого Protobuf-контракта. 122 фронтенд-теста и Go-бэкенд с детектором гонок — всё зелёное в CI на каждый PR. Полностью автоматический деплой с health-gate и автооткатом — пушишь в main, и он катится сам.