Что было до
Студия ведёт полтора десятка продуктов параллельно: Пульсар7, ФП, МП Приборка, Контент-завод, лендинги и другие. Состояние проектов жило в головах и переписках. Одной ссылкой показать клиенту «что строим сейчас, что уже вышло, когда закончится твой проект» было нельзя.
Каждый сервис вёл свой CHANGELOG у себя в репозитории. Наружу эти файлы не попадали, и обновления терялись: работу сделали, а увидеть её было негде.
Обратная связь приходила в личку и в чаты. Человек кидал идею или баг и не получал ответа вообще: ни «приняли», ни «сделали», ни «не возьмём».
Что видит посетитель
Четыре вкладки: Таймлайн, Проекты, Задачи, Идеи и баги. Всё открыто без входа.
- Календарный Gantt по всем продуктам сразу: полоски этапов с явными датами, сегодняшний день отдельным пином, крупные релизы точками прямо на полоске. На мобильном горизонтальный скролл с закреплённой левой колонкой.
- Лента «Что нового» и активность за 30 дней: релизы, стартовавшие и закрытые задачи из всех сервисов одним потоком. На главной недельный дайджест.
- Форма идей и багов без регистрации: одно поле, имя необязательно, пустое становится «Аноним». Лимит 3 сообщения в час с одного адреса, в базе лежит хэш с солью.
- Публичный цикл ответа: 6 состояний от «Приняли» до «Не возьмём». Ответ команды в комментариях помечен бейджем «от AVAT».
- Персональная ссылка на проект клиента: сверху плашка с активным этапом и остатком дней, страница сама доскролливает до нужной строки таймлайна.
По 12 продуктам роадмапа мы сложили реальные часы студии: вышло 426. Второе число, 3 740 часов, это наша собственная экспертная оценка того, сколько тот же объём занял бы у обычной команды. Замера и внешнего аудита за ней нет. Мы показываем её именно как оценку студии по своим же проектам.
Сервисы отчитываются сами
Каждый сервис отправляет своё обновление POST-запросом на /api/releases с Bearer-токеном и сразу появляется в общем таймлайне и в ленте. Ключ бывает двух видов: общий, тогда сервис указывает свой слаг, и персональный, тогда сервис определяется по самому ключу.
Отдельный эндпоинт заводит новый продукт в план в момент старта работ: сервис создаётся вместе с первым этапом на 30 дней. Проект попадает на витрину в день старта.
Релизы можно подтянуть из CHANGELOG других проектов. Админка сканирует файлы сервисов, парсит записи, показывает диффом что нового, отмеченное одной кнопкой уходит в публичную ленту. Из записи берётся первый осмысленный абзац и сжимается до 240 символов: в ленте нужен тизер. Чтение разрешено только из одного каталога, любой другой путь отклоняется.
Как устроено
Next.js 15 на TypeScript, App Router. Почти вся отрисовка серверная, клиентский код только там, где нужен интерактив: форма, фильтры, канбан, ховеры таймлайна. Страницы пересобираются раз в 60 секунд, поэтому витрина свежая в пределах минуты. Данные лежат в PostgreSQL, 12 таблиц. Одна функция собирает всю витрину семью параллельными запросами: страница делает один заход в базу вместо тридцати.
У приложения нет ключа с полным доступом к данным. Доступ разделён на три контура. Чтение витрины идёт публичным ключом с построчными политиками, причём секретные колонки (ключ сервиса, контакт автора сообщения, хэш адреса) публичному ключу не выданы на уровне прав. Запись из админки работает от сессии залогиненного админа, и права проверяет сама база. Публичные и машинные операции вынесены в серверные функции базы: валидация, лимит частоты и проверка API-ключа выполняются внутри неё. Ключ приёма релизов в исходниках не лежит.
Таймлайн строится по тем же задачам, которыми студия живёт в канбане. У задачи есть даты и флаг вехи. Мелочь наружу не идёт: на публику попадает либо помеченная веха, либо задача длиннее 5 дней.
Выкатка идёт push'ем в репозиторий: GitHub Actions собирает Docker-образ, сервер забирает его сам. Серверные секреты в образ не попадают.
Грабли
Главную переделку принесла безопасность. Сначала приложение ходило в базу мастер-ключом, как в обычном туториале. Переписали на три контура. Как только мастер-ключ сняли, стало видно то, что он маскировал.
- Анонимный посетитель мог прочитать колонку с ключом приёма релизов: политика «читать всё» плюс права на таблицу целиком. Закрыли выдачей прав по конкретным колонкам.
- Тем же способом наружу были доступны контакт автора сообщения и хэш его адреса.
- Таблица шаблонов задач жила без включённого RLS и с правом записи для анонима: править и удалять содержимое мог любой.
- Публичное чтение комментариев отвалилось сразу после снятия мастер-ключа. Прав на эту таблицу на сервере просто не было, старый код читал её в обход. Потребовалась отдельная миграция на следующий день.
Ещё три, которые стоит знать
Вывод из первой истории ушёл в правила команды: если что-то работает только под мастер-ключом, права не настроены.
Таблица комментариев полгода существовала только в коде. API в неё писал, в миграциях её не было, первый POST на чистой базе упал бы. Завели задним числом. Обратная сторона жива до сих пор: одна колонка появилась на сервере мимо репозитория, поэтому база с нуля по репозиторию не поднимется без ручной правки.
Два источника правды. Таймлайн рисовался по отдельным «этапам», работа шла в канбане. Студия двигала задачи, публичный таймлайн этого не видел. Задачи сделали первичными, дали им даты, старые этапы импортировали разово.
И UI-аудит, после которого чинили мелочь, которая портит доверие. На экранах уже 900px вкладок навигации не было вообще. Счётчик подписывал «релизов в этом году», а считал все за всё время. Завершённые этапы заливались светло-серым и сливались с фоном: сделанная работа была визуально невидима. Метка «сегодня» была красной и читалась как авария. Статусы кодировались только цветом, добавили иконки для дальтоников.
Частые вопросы
Как показать клиенту, на каком этапе его проект?
У каждого клиента своя ссылка на его проект среди 18 продуктов студии. Сверху плашка с активным этапом и остатком дней. Страница сама доскролливает до нужной строки таймлайна. Вход не нужен, ссылку можно переслать прямо в переписке.
Нужна ли регистрация, чтобы смотреть план и оставлять идеи?
Нет, все четыре вкладки открыты без входа: Таймлайн, Проекты, Задачи, Идеи и баги. Форма тоже работает без регистрации: одно поле, имя необязательно, пустое становится «Аноним». От спама стоит лимит 3 сообщения в час с одного адреса, сам адрес хранится хэшем с солью.
Кто обновляет план: его приходится вести руками?
Сервисы отчитываются сами. Каждый шлёт релиз POST-запросом на /api/releases с Bearer-токеном и сразу появляется в таймлайне и в ленте. Отдельный эндпоинт заводит новый продукт в день старта работ, сразу с первым этапом на 30 дней. Релизы можно ещё и подтянуть из CHANGELOG: админка парсит файлы, берёт первый осмысленный абзац и сжимает его до 240 символов.
Что происходит с идеей или багом после отправки?
У каждого сообщения публичный статус, всего 6 состояний от «Приняли» до «Не возьмём». Автор и любой посетитель видят, куда двинулось дело. Ответ команды в комментариях помечен бейджем «от AVAT». Отказ показывается так же открыто, как и принятая идея.
Не опасно ли выкладывать план разработки наружу?
У приложения нет ключа с полным доступом к базе. Доступ разделён на три контура: публичное чтение с построчными политиками, запись из админки от сессии залогиненного админа, машинные операции через серверные функции базы. Секретные колонки (ключ сервиса, контакт автора, хэш адреса) публичному ключу не выданы на уровне прав. Аудит после снятия мастер-ключа нашёл три дыры в правах, все закрыли миграциями.
