Перейти к содержимому
← Все работы

Tong Yulduzi

Республиканская детская и подростковая газета вывела печатное издание в онлайн. Я довёл проект от пустой базы до работающего продукта и передал редакции, которая ведёт его сама.

Ситуация

«Tong Yulduzi» — республиканская газета для детей и подростков в Узбекистане. Сама газета продолжает выходить на бумаге. Чего не было — так это онлайновой половины: места, где читатель найдёт статью, места, где будет жить архив выпусков, и места, где журналисты действительно работают.

То есть задача — два продукта на одной базе. Публичный сайт, который должен быть быстрым и не падать, и редакционный инструмент, в котором пишут, редактируют, ставят в расписание и публикуют. Я сделал оба и развёртывание под ними.

Схема

Бэкенд — FastAPI поверх PostgreSQL, модели SQLAlchemy и миграции Alembic. Схема держит новости, категории, пользователей, медиа, галереи, теги и выпуски газеты, со связующими таблицами, которые позволяют одной статье иметь много изображений и много тегов.

Несколько решений, которые оказались важнее остальных:

  • Тело статьи хранится как JSONB, а не HTML. Структурированный контент
  • Выпуск — полноценная сущность. У печатной газеты есть номера, поэтому архив
  • Ролей мало. Автор, редактор и администратор — потому что именно они есть в

Поиск по архиву

Читатель ищет статью, которую помнит наполовину. Это задача полнотекстового поиска, и я оставил её внутри PostgreSQL, а не добавлял поисковый сервер, за который клиенту пришлось бы платить и который надо было бы обслуживать.

Колонки tsvector с GIN-индексами, ранжирование через ts_rank. Настоящей работой была настройка: я смотрел, что набирают редакторы и читатели, и правил веса, пока нужная статья не стала выходить первой — а не просто самая свежая.

Ускорение, с цифрами

Страницы грузились медленнее, чем должны были. Две причины, разобранные по отдельности:

  • Самые читаемые страницы пересчитывали один и тот же результат для каждого
  • Нескольким запросам не хватало индексов, и они делали последовательное

Я замерял время до и после каждого изменения, а не выкатывал оба сразу в надежде на лучшее. Если меняешь две вещи и становится быстрее — ты не узнал, какая из них помогла.

Фронтенд

Обе поверхности — Next.js и TypeScript, собранные по согласованным макетам и близко совпадающие по отступам, типографике и цвету.

Что определило большую часть работы: почти вся аудитория приходит с телефона. Поэтому читательский сайт делался сначала под маленький экран, и редакционный инструмент тоже должен был оставаться пригодным на нём — журналисты сдают материал оттуда, где оказались.

Развёртывание и скучные вещи, которые важны

CI прогоняет pytest на одноразовой базе, собирает Docker-образы и деплоит. Если health check после деплоя не проходит, происходит автоматический откат на предыдущий образ, и читатели не видят упавший сайт.

Резервные копии делаются каждую ночь, зашифрованными, и хранятся вне сервера. Важнее другое: я провёл полную репетицию восстановления в чистое окружение. Бэкап, который ни разу не восстанавливали, — это гипотеза, а не бэкап, и здесь речь об архиве газеты, то есть о её истории.

Всё это работает как один стек Docker Compose за Nginx на одном небольшом VPS. Бюджет газеты — не бюджет стартапа, и архитектура это отражает намеренно.

Репозиторий закрытый, он принадлежит клиенту. Сайт открытый.

Tong Yulduzi — Salohiddin Yo'ldoshev