Учебник веб-разработки
Разделы учебника
На этой странице

Практикум

Практикум: от первого изменения до интеграции CMS

Оглавление

Упражнения ниже — задания для читателя. Они не реализованы появлением этой главы. Проводите их в отдельной Git-ветке и с локальными учебными данными. Каждое завершённое задание оформляйте отдельным коммитом с результатом проверки в описании.

1. Найти путь данных одной новости

Цель: проследить данные через реальные границы системы.

Создайте учебную News в Content Manager, задайте slug и publishedOn. Найдите соответствующие query/populate в CMS client, Zod schema в contracts, adapter, Server Component и tRPC procedure. Сравните draft и опубликованный ответ REST.

Результат: можете объяснить, почему Publish меняет страницу без rebuild, почему read-only token находится только на сервере и почему неизвестный slug отличается от недоступной CMS. Не исправляйте ошибки добавлением JSON fallback.

2. Добавить событие и проверить даты

Цель: сохранить смысл времени при разных UTC offsets.

Создайте Event в CMS с startsAt и без endsAt. Publish и проверьте одно время вместо диапазона. Добавьте конец, republish. Сравните запись 2025 и 2026 годов, затем два ISO timestamps с разными offsets. Объясните сортировку по Date.parse и отображение через Europe/Moscow. Не подставляйте полночь/конец, которых не было в источнике.

3. Изменить тему Mantine

Цель: отделить токены темы от геометрии конкретной страницы.

Измените один оттенок палитры или default button radius в packages/ui/src/theme.ts. Проверьте, какие компоненты изменились, а какие остались с цветом из соседних *.module.css и общих CSS variables. Согласуйте тему/CSS, если хотите глобального результата.

Результат: можете объяснить два источника цвета, не заменяя все размеры inline styles. Вручную проверьте мобильное меню, keyboard focus и контраст текста.

Вопрос: зачем ColorSchemeScript расположен в head, а provider — внутри body?

4. Добавить безопасную процедуру tRPC

Цель: изучить общий вход, серверный caller и HTTP-клиент.

Добавьте news.byCategory с входом { category: string }, валидируемым Zod. Верните отфильтрованный массив, не добавляя mutation или доступ к CMS. Напишите серверный тест: существующая категория, неизвестная, неправильный тип.

Результат: вызов работает через caller и tRPC client, а неверный вход отвергается до бизнес-фильтрации. Клиент получает тип новой процедуры без отдельного интерфейса.

Вопрос: где добавить права доступа, если процедура станет отдавать закрытые записи?

5. Ужесточить учебный runtime-контракт

Цель: увидеть разницу между типом и проверкой данных.

Прочитайте действующий контракт webhook и его тесты. Создайте отдельную учебную схему для entry.publish: обязательный documentId, model только news | event. Не подменяйте ею production-схему: инвалидирование всего кеша допускает другие модели, а обработчик также принимает события медиа.

В серверном тесте сравните действующую и учебную схемы на корректном payload, entry: {}, неверной модели и событии media.update. Используйте результаты safeParse, не приводите внешние данные через as к желаемому типу.

Результат: можете объяснить, какой вход отвергнут каждой схемой и почему ужесточение контракта требует согласования отправителя и получателя. Очередь и отдельный consumer для этого упражнения не нужны.

6. Изучить расписание Strapi

Изучите встроенный cron. Не включайте его до появления конкретной задачи.

7. Исследовать повторы webhook

Цель: различать безопасный повтор инвалидирования и повтор произвольного эффекта.

В существующем серверном тесте webhook выполните один и тот же авторизованный payload дважды. Проверьте успешные ответы и повторные вызовы инвалидирования. Смоделируйте ошибку инвалидирования и убедитесь, что она не превращается в 200. Используйте учебные значения и контролируемые зависимости теста, без живой CMS.

Результат: повторное уведомление может снова сбросить кеш, не создавая новую запись CMS. Отсутствие собственного механизма retry в этом тесте не означает гарантированную доставку webhook. Не приписывайте обработчику очередь или exactly-once.

Углубление: после главы о транзакциях спроектируйте отдельную учебную операцию с ключом идемпотентности. Разберите потерянный ответ, одновременный повтор и рестарт; укажите, что должно сохраняться атомарно. Этот механизм не реализован появлением упражнения и не добавляется к production-webhook автоматически.

8. Проверить runtime-контракт CMS

Цель: проверить runtime-контракт внешнего REST ответа.

В CMS client test добавьте fixture с неверной датой, недопустимым Blocks link или неожиданным media origin. Добейтесь явного CmsUnavailableError всего ответа. Затем смоделируйте отказ второй страницы pagination: первая не должна стать «полным списком». Сравните гарантии TypeScript и Zod.

Результат: transport boundary покрыт поведением, тест не зависит от live CMS.

9. Подключить Strapi как реальный источник

Цель: подтвердить редакторский вертикальный сценарий уже работающей интеграции.

Через обычный Content Manager: draft → publish → edit/republish → unpublish. Проверьте список, detail, metadata и фотографию без новой сборки. Не заменяйте этот процесс mock-тестом. Затем временно остановите локальную CMS: сравните уже закешированную страницу с /api/content-health, который должен вернуть 503. Верните CMS. Автоматических UI-тестов не добавляйте.

Результат: записи и изображения показывают настоящий state CMS, token не виден в browser data, unpublish убирает detail. Укажите отдельно, какие шаги реально проверены руками, какие покрыты серверными tests и что осталось непроверенным.

10. Проверить резервную копию и восстановление

Цель: получить проверяемую копию, а не просто файл dump.

Создайте тестовую запись и media в локальном Strapi. Снимите копию. Восстановите базу в отдельную learning_restore и файлы в отдельный volume/экземпляр CMS. Не подключайте production-приложение к учебной базе.

Результат: запись и файл открываются в восстановленной CMS; вы знаете, какие секреты и ключи требуются дополнительно к SQL. Запишите время восстановления и версию релиза. Не удаляйте оригинальные тома ради доказательства восстановления.

Вопрос: как проверить совместимость резервной копии с версией CMS?

11. Усовершенствовать deploy readiness

Проверьте отличие ответа /api/health от чтения настоящего контента через /api/content-health. В локальной среде исследуйте поведение при недоступной CMS.

12. Проследить SSG → кеш → ISR

Цель: отличить генерацию страницы от её обновления.

Прочитайте scripts/check-static-site.mjs и найдите проверки prerender manifest, cache HIT и webhook; их полное выполнение относится к CI. На локальной учебной CMS создайте новость с новым slug: убедитесь, что адрес появляется без rebuild. Сначала откройте отсутствующий адрес, затем опубликуйте запись с этим slug и проверьте сброс закешированного 404.

Результат: можете объяснить роль generateStaticParams, тега cms, revalidatePath и страховочной часовой ревалидации. Не отключайте SSG для обхода ошибки CMS и не смешивайте React cache с межзапросным кешем Next.js.

13. Собрать страницу состояния на UI kit

Цель: собрать интерфейс из темы, примитивов и CSS Module.

Прочитайте app/not-found.tsx. Измените пояснение и вторую ссылку, сохранив семантический h1 и HTTP 404. Проверьте произвольный URL и отсутствующий slug CMS, клавиатурный фокус, ширину 320 px и режим без JavaScript. Зафиксируйте отдельно ограничение ISR fallback текущего Next.js, описанное в главе SSG.

Результат: объясняете, что задаёт тема, что — CSS Module, и почему ошибка сети не должна превращаться в сообщение об удалённой странице.

Итоговые сценарии учебника

Следующие сценарии связывают написанные главы. Выполняйте по одному, в собственной ветке/временной среде. Для каждого сохраните commit или набор временных файлов, входные данные, ожидаемое и фактическое наблюдение. «Не запускал» — отдельный статус, а не успешный результат. Внешние сервисы используются только в учебных копиях.

A. Проверенный контент → адаптивное расписание

Материалы: ADT, FP, HTML/CSS, композиция.

Возьмите fixture из серверных CMS tests, проследите schema → adapter → props расписания. В отдельной fixture испортите дату: ответ должен быть отвергнут на границе, а не сортироваться как NaN. Для корректных данных проверьте сортировку, группировку и отсутствие выдуманного endsAt. Соберите учебную story на UIKit, без импорта CMS-клиента в пакет UI. Проверьте 320 px, увеличенный текст и Tab.

Приёмка: неверный вход не попадает в props; корректный отображает время Europe/Moscow и однозначные подписи. Укажите отдельно серверную проверку и ручной layout.

B. Один фильтр и четыре модели состояния

Выполните сравнительную лабораторию. Один контракт, expected и порядок actions сохраняются во всех вариантах. Проверьте повтор reset и cleanup. Расширьте опыт независимыми экземплярами, сопоставьте draft поля с подтверждённым URL-фильтром и Back/Forward.

Приёмка: одинаковый список и объяснение подписок, отсутствие утечки состояния между экземплярами. Выберите для задачи один механизм и обоснуйте его сложность.

C. Draft → publish → webhook → новый HTML

Выполните задания 1, 9 и 12 на собственной CMS, следуя жизненному циклу контента. Начните с отсутствующего slug, затем создайте draft, опубликуйте, измените и снимите с публикации. Запишите HTML/detail/metadata/media на каждом шаге, webhook response и состояние открытой вкладки. Ошибка CMS не должна выглядеть как удалённая запись.

Приёмка: новый slug без rebuild, сброс прежнего 404 и объяснение, почему 200 webhook не обещает мгновенное обновление каждой уже открытой вкладки. HTTP-статус fallback сверяйте с ограничениями SSG/ISR.

D. Контракт API и потерянный ответ

Выполните четыре adapters, пагинацию и транзакционный журнал. Сначала сравните read/update/invalid/missing/forbidden/version conflict. Потом зафиксируйте операцию в БД, отбросьте ответ и повторите тот же key; сравните другой payload с тем же key и две конкурентные сессии.

Приёмка: общий бизнес-результат при разных протокольных ошибках; одна новая version и сохранённый ответ после повтора. In-memory adapter не является проверкой устойчивости журнала после рестарта БД.

E. Одна агрегация в JS, SQL и MapReduce

Выполните аналитический практикум и локальную модель MapReduce на одинаковых events. Сравните count/sum/average, пустой набор и разбиения. Повторите одну партицию: покажите, почему ассоциативность не устраняет дубли. Затем добавьте позднее событие к закрытому event-time окну и объясните policy коррекции/отклонения.

Приёмка: одинаковые итоги без дублей, контрпример среднего средних, явные допуски float и границы локальной модели распределения.

F. Колонки/JSONB и exact/ANN

Выполните SQL/JSONB и ANN. Выберите три частых запроса и два инварианта. Решите, какие поля должны быть колонками, что остаётся JSONB и какие индексы следуют из запросов. На фиксированном наборе векторов сравните exact top-k и HNSW с/без редкого фильтра.

Приёмка: CHECK/FK, фактические планы, recall@k и latency на одном наборе. Наличие CREATE INDEX без его использования не считается ANN-результатом.

G. Свежесть и конкурентный кеш

Выполните лабораторию гонок, затем сценарий CMS из C. Для браузера, Router Cache, серверных данных и HTML выпишите ключ, источник истины и событие обновления. Сравните холодный/прогретый запрос, потерянный webhook, новый slug и недоступность CMS. Не включайте Redis в сайт ради этого опыта; его cache-aside изучается отдельно.

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

H. Отказ зависимости, релиз и восстановление

Выполните лабораторию релиза, диагностику и задание 10 по восстановлению. Различите process health и content readiness. Для релиза соберите свидетельства точного ref/digest/CI result; production-образы собираются штатным CI. Restore выполняется в отдельную учебную базу и media copy.

Приёмка: отказ CMS обнаруживается content-health, причины не подменяются общим «сервер сломан», восстановленные запись и файл открываются. Image rollback не выдаётся за откат schema/data; публикация требует отдельного разрешённого этапа.

DevOps и наблюдаемость

Прочитайте вводную и практическую главу. Найдите путь релиза от commit до HTTP smoke. Объясните, почему pg_dump и откат web-образа решают разные задачи. Создайте отдельное учебное правило на vector(0), проверьте pending/firing и восстановление на vector(1). Затем сравните ноль с отсутствием временного ряда. Критерий: вы можете объяснить каждый переход состояния, не останавливая production-сервис. Подключение Telegram — необязательное упражнение.

Контрольные вопросы

  1. Почему contracts нужен build перед dev?
  2. Почему tRPC caller не использует URL /api/trpc?
  3. Почему import type важен для клиентского кода и native Node TypeScript?
  4. Почему 200 webhook не означает, что новый HTML уже сгенерирован?
  5. Почему .env и named volume живут дольше Docker-контейнера?
  6. Почему смена .env не меняет пароль существующей роли PostgreSQL?
  7. Почему Docker restart не применяет новое environment?
  8. Почему robots.txt не защищает закрытые данные?
  9. Что восстановится при image rollback, а что останется новым?
  10. Каких проверок не выполняет зелёный pnpm check?

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

Практика доставки через PR и релизную линию

По главе Git создайте отдельный worktree/ветку в учебном repository. Добавьте осмысленный серверный тест, добейтесь RED→GREEN и обновите changelog, сохранив ручной текст PR. Переместите target и объясните, почему старый результат CI нельзя использовать для интеграции.

Затем создайте release-ветку с immutable base tag, подготовьте draft notes и сравните четыре digest staging и кандидата production. На изолированной копии измените модель CMS: promotion должен остановиться до запуска кандидата. Верните совместимую модель и воспроизведите отказ smoke после переключения: прежние routes, данные и приложения должны восстановиться.

Результат: можете различить draft/published/applied/live, доказать отсутствие пересборки при promotion и объяснить, почему охлаждается точная staging generation, а production не участвует в LRU. Эти упражнения не выполняются на public production.