Перейти к содержанию

Диагностика и договор выпуска Next.js-приложения

Оглавление курса · Скачать все состояния проекта.

Учебная задача: Зафиксировать окружение, события ошибок и границы подготовленного примера.

Входное состояние: Полный локальный путь заявок и ограниченная таблица. Выходное состояние: Подготовлены точки диагностики и условия выпуска без выполненного размещения. Продолжаем «Смену» — учебную диспетчерскую заявок мастерской. Нужны основы JavaScript, TypeScript, React и HTTP. Используем App Router, Next 16.4.0, React 19.3.0 и Node.js не ниже 20.9.0. Основные версии закреплены; диапазоны остальных пакетов ещё не разрешены установкой и lockfile отсутствует. Полный каталог lesson-20 в архиве соответствует окончанию этой главы. Выбирайте один снимок целиком: перенос отдельных файлов из будущих этапов меняет его зависимости и правила данных.

Исходники подготовлены редакционным чтением. Пакеты, сервер, сборка, миграции, примеры и внешнее размещение не выполнялись. Ответы страницы и изменения данных ниже являются ожидаемыми, example_execution: not_run.

«Смена» теперь имеет связанный путь: пользователь входит, получает разрешённый список, создаёт заявку и меняет статус. Перед передачей проекта важно описать, что обеспечивает исходник и что должна предоставить выбранная среда. Добавим точки диагностики Sentry и договор выпуска. Эта глава не объявляет систему развёрнутой: сборка, миграции, отправка событий и внешнее размещение в работе не выполнялись.

Основные версии зафиксированы: Next 16.4.0, React 19.3.0 и Node не ниже 20.9.0. В пакете есть дополнительные диапазоны Clerk, Drizzle, RHF, Zod и Sentry; lockfile ещё не получен установкой. Перед рабочим выпуском потребуется разрешить зависимости и подтвердить конкретный набор в выбранной среде. Чтение совместимых документированных API помогает подготовить проект, но не заменяет фактическое подтверждение сборки.

Сервер, браузер и границы ошибок

Сбой может возникнуть в браузерном обработчике, серверном построении или серверном действии. Поэтому диагностика имеет разные точки подключения. instrumentation.ts экспортирует register и загружает соответствующую конфигурацию для Node или edge. onRequestError использует обработчик Sentry для серверных ошибок. Файл не является местом получения рабочих заявок и не должен превращаться в журнал их полного содержания.

В браузере используется instrumentation-client.ts, а не историческое имя sentry.client.config.ts. Инициализация получает публичный DSN и включается только при непустом значении. DSN описывает адрес назначения событий и не является серверным auth token загрузки карт исходников. Смешение этих реквизитов нарушило бы границу конфигурации. В архиве оба DSN пусты, поэтому действующее внешнее подключение не заявлено.

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

Содержимое события требует отдельного решения

Установлено sendDefaultPii: false. В beforeSend удаляются user и основные части request: data, cookies, headers. Это уменьшает раскрываемый набор, но не доказывает отсутствия чувствительного текста в любом исключении или breadcrumb. Нельзя записывать описание заявки, токен или cookie в строку ошибки, рассчитывая, что один флаг всё очистит. До включения реальной диагностики нужен согласованный договор содержания и хранения событий.

В примере трассировка имеет нулевую долю, а session replay не подключён. Это позволяет сосредоточиться на ошибках без неописанной записи пользовательского пути. Настройка source maps и автоматическая загрузка в выпуске также не добавлены: отсутствует withSentryConfig и подтверждённая процедура загрузки. Поэтому не обещаем красивые строки исходника для любого события. Такой шаг потребовал бы отдельной конфигурации проекта и секретного токена вне публичных файлов.

База и личность переживают выпуск

PostgreSQL должна иметь постоянное хранение и резервную копию. История миграций Drizzle генерируется, читается и применяется отдельно; исходная схема не является уже существующей таблицей. Строка DATABASE_URL передаётся серверу и окружению инструментов. Смена исходников не должна уничтожать рабочие данные. Локальный INSERT из промежуточного урока не является процедурой наполнения рабочей базы.

Pool ограничивает соединения каждого экземпляра, а не всей платформы. При размещении на Vercel или другом совместимом Node-хостинге выбираются корень проекта, окружение, доступ к базе, HTTPS и масштабирование. Эти значения зависят от реальной инфраструктуры. Архив не содержит выдуманного рабочего домена и не заявляет, что переменные уже сохранены в панели хостинга.

Clerk должен иметь ключи и разрешённые адреса именно выбранной среды. Доверенный администратор назначает publicMetadata.role; обычная форма приложения такой возможности не предоставляет. Проверки личности и объекта находятся в слое данных и действиях независимо от Proxy. Публичные учебные catalog и api/demo сохраняют только фикстуры, а временные открытые комментарии удалены с момента подключения базы.

Функциональные ограничения

Таблица показывает максимум двести строк, а её страницы относятся к этому набору. Опрос не является push-механизмом. Создание не имеет ключа идемпотентности, поэтому два отдельных запроса могут создать две заявки. Изменение статуса не проверяет версию и допускает последнюю запись. Удаление admin жёсткое и не реализует восстановление. Эти границы не следует скрывать общим словом «полный»: они помогают осмысленно продолжить проект.

Проверка новой среды должна подтверждать не только главную страницу, но и матрицу полномочий. Сотрудник не должен читать и менять чужую заявку через прямой адрес или действие; менеджер не должен получить удаление; неизвестная роль остаётся employee. Подготовленный код позволяет проследить эти условия, но здесь не утверждается выполненный сценарий. Все результаты остаются ожидаемыми, example_execution: not_run.

Для самостоятельного разбора найдите различие между настройкой события, отправленным событием и подтверждённым событием внешнего сервиса. Затем перечислите, что нужно для восстановления базы и что для повторного выпуска исходников. Файл RELEASE.md собирает эти условия рядом с последним снимком, чтобы следующий участник мог продолжить работу с ясными предпосылками и ограничениями.

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

Полные файлы этого шага

Следующие файлы показаны полностью. Остальные согласованные компоненты и настройки находятся в том же каталоге архива; сохраняйте их вместе при самостоятельном прохождении.

instrumentation.ts

import * as Sentry from "@sentry/nextjs";
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") await import("./sentry.server.config");
  if (process.env.NEXT_RUNTIME === "edge") await import("./sentry.edge.config");
}
export const onRequestError = Sentry.captureRequestError;

instrumentation-client.ts

import * as Sentry from "@sentry/nextjs";
Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN, tracesSampleRate: 0, sendDefaultPii: false, beforeSend(event) { delete event.user; if (event.request) { delete event.request.data; delete event.request.cookies; delete event.request.headers; } return event; } });
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;

sentry.server.config.ts

import * as Sentry from "@sentry/nextjs";
Sentry.init({ dsn: process.env.SENTRY_DSN, enabled: !!process.env.SENTRY_DSN, tracesSampleRate: 0, sendDefaultPii: false, beforeSend(event) { if (event.request) { delete event.request.data; delete event.request.cookies; delete event.request.headers; } delete event.user; return event; } });

app/global-error.tsx

"use client";
import { useEffect } from "react";
import * as Sentry from "@sentry/nextjs";
export default function GlobalError({ error, retry }: { error: Error & { digest?: string }; retry: () => void }) {
  useEffect(() => { Sentry.captureException(error); }, [error]);
  return <html lang="ru"><body><h1>Не удалось открыть «Смену»</h1><p>Повторите запрос позже.</p><button onClick={() => retry()}>Повторить</button></body></html>;
}

RELEASE.md

# Договор выпуска «Смены»

Execution: not_run. Зависимости, примеры, сборка, миграции, Sentry и размещение не запускались.

Node >=20.9; Next 16.4.0; React 19.3.0. Остальные диапазоны заданы в package.json, lockfile отсутствует. Разрешение зависимостей и совместимость необходимо проверить в выбранной среде перед выпуском.

Постоянная PostgreSQL-база, резервная копия, отдельная история прочитанных миграций Drizzle. DATABASE_URL передаётся серверу и отдельно командам Drizzle, конфигурация сама .env не загружает. Не использовать демонстрационную базу как рабочую.

Ключи Clerk соответствуют выбранной среде и домену. publicMetadata.role задаёт доверенный администратор через серверные инструменты Clerk; обычному клиенту запись роли не предоставлена. Проверки личности и владельца выполняются в данных и действиях, независимо от Proxy. /catalog и /api/demo содержат только статические публичные фикстуры. Учебные открытые demo-comments удалены с этапа 13.

Next обслуживается совместимой Node-платформой с HTTPS. Для Vercel или другого хостинга отдельно выбирают корень проекта, окружение, URL базы, ключи среды и ограничения соединений. В этой работе внешнее размещение не выполняется. Следить за max Pool в каждом экземпляре; серверное масштабирование умножает число соединений.

Sentry DSN пустые по умолчанию; init включается только при их наличии. Отправка событий и загрузка source maps не подтверждены; withSentryConfig и автоматическая загрузка карт здесь не настроены. Фильтрация request/user уменьшает объём данных, но не доказывает отсутствие чувствительного содержимого в исключении или breadcrumbs. До включения согласуйте очистку событий и политику хранения. Не записывать тексты заявок, токены и cookie в ошибки.

Ограничения: выборка максимум 200 строк, клиентская пагинация внутри неё; опрос не является push; повторная отправка создания не идемпотентна; изменение статуса последнее побеждает, контроль версии не добавлен; удаление admin без восстановления. Это учебная диспетчерская, а не подтверждённая рабочая система обслуживания клиентов.

Документация и исходники

Договор этой главы раскрыт в официальной документации. Сопоставьте её API с выбранной версией и с объяснённым источником каждого значения. Архив «Смены» содержит двадцать полных состояний и условия самостоятельной подготовки. Ожидаемый результат оценивается по текущей главе: поздние проверки владельца, база и внешние ключи не являются неявно выполненными предпосылками раннего учебного состояния.