Описание проблемы и структуры проекта
При работе над образовательной платформой Mathsido использовалась типичная конфигурация Turborepo: корневой репозиторий, в котором находятся несколько приложений и пакетов. Основное приложение — Next.js 16 — размещалось в папке apps/app, а вспомогательные модули (types, utils, ui) находились в packages. Стандартный набор файлов Next.js (next.config.js, package.json, src) располагался именно в каталоге apps/app.
mathsido/
├── apps/
│ └── app/ ← Next.js приложение
│ ├── next.config.js
│ ├── package.json
│ └── src/
├── packages/
│ ├── types/
│ ├── utils/
│ └── ui/
├── package.json ← корневой workspace
├── pnpm-workspace.yaml
└── turbo.json
Сборка в Appwrite Sites завершалась успешно: все страницы компилировались, статические файлы генерировались, процесс отмечался зелёным статусом. Однако после этого этапа деплой «застревал» на этапе Finalizing и оставался в этом состоянии от нескольких часов до нескольких дней. Сайт оставался недоступным, а логи не показывали явных ошибок.
Как работает пайплайн сборки в Appwrite Sites
Понимание внутренних шагов развертывания критически важно для поиска причины. При публикации проекта Appwrite Sites запускает несколько последовательных стадий:
- Подготовка окружения – установка Node.js, кеширование зависимостей, настройка переменных.
- Выполнение команды сборки – запуск пользовательского скрипта (например,
pnpm build:app), который создаёт.nextи другие артефакты. - SSR‑бандлинг – внутренний скрипт Appwrite, отвечающий за подготовку серверного кода к работе в их serverless‑раннере.
- Упаковка артефактов – формирование zip‑архива с готовым приложением.
- Дистрибуция по Edge‑сети – размещение артефактов в глобальной сети CDN.
- Создание скриншота – генерация превью сайта.
Шаг SSR‑бандлинг является «чёрным ящиком» для пользователя: именно он подготавливает файлы server.js и next.config.js в формате, ожидаемом runtime Appwrite. Если эти файлы не находятся в ожидаемом месте, процесс не может завершиться, и развертывание остаётся в состоянии «Finalizing».
Почему монорепозиторий ломает SSR‑бандлинг
Appwrite делает предположение, что корень проекта, где запускается сборка, одновременно является корнем Next.js‑приложения. При обычных одно-репозиториях next.config.js и server.js находятся в директории, из которой вызывается npm run build. В монорепозитории Turborepo это не так: сборка запускается из корня репозитория, а реальное приложение живёт глубже — в apps/app.
В результате внутренний скрипт Appwrite ищет конфигурацию по пути <workspace_root>/next.config.js и не находит её. Аналогично, файл server.js, генерируемый Next.js, оказывается в <workspace_root>/apps/app/.next/server.js, а Appwrite ожидает его в <workspace_root>/server.js. Отсутствие этих файлов приводит к бесконечному ожиданию завершения SSR‑бандлинга.
Точное решение: переопределение путей для SSR‑бандлинга
Для исправления необходимо явно указать Appwrite, где искать конфигурацию и серверный бандл. Это достигается добавлением небольшого скрипта‑обёртки в корневой package.json и корректировкой команды сборки.
Шаг 1. Добавить скрипт prepare:ssr
{
"scripts": {
"prepare:ssr": "cp apps/app/next.config.js . && cp -r apps/app/.next/server . && mv server ./server.js"
}
}
cp apps/app/next.config.js .копируетnext.config.jsв корень репозитория.cp -r apps/app/.next/server .перемещает сгенерированный серверный каталог.mv server ./server.jsпереименовывает основной файл в ожидаемое имя.
Шаг 2. Обновить основную команду сборки
Если изначальная команда выглядела так:
pnpm build:app
То её следует заменить на последовательность:
pnpm build:app && pnpm prepare:ssr
Или объединить в один скрипт:
{
"scripts": {
"build:app:ssr": "pnpm build:app && pnpm prepare:ssr"
}
}
Шаг 3. Настроить Appwrite Sites
В настройках проекта Appwrite Sites указываем новую команду сборки, например pnpm run build:app:ssr. После этого платформа найдёт необходимые файлы в корневой директории, успешно выполнит SSR‑бандлинг и перейдёт к шагу упаковки.
Проверка результата
После внедрения скриптов деплой проходит без задержек:
- Этап Build Command Execution завершается успешно.
- SSR‑Bundling находит
next.config.jsиserver.jsв корне, генерирует корректный bundle. - Finalizing завершается за несколько секунд, и сайт становится доступным через Edge‑сеть Appwrite.
Важно помнить, что копирование файлов в корень не влияет на работу самого приложения в режиме разработки, потому что next dev всё равно запускается из apps/app. Данный подход только оптимизирует процесс CI/CD в Appwrite Sites.
Общие рекомендации для монорепозиториев
- Явно указывайте пути к конфигурационным файлам, если используете серверные платформы, ожидающие их в корне проекта.
- Разделяйте сборку клиентской и серверной части: клиентская сборка может оставаться в подкаталоге, а серверные артефакты копировать в корень только в процессе CI.
- Тестируйте пайплайн локально: запустите скрипт
prepare:ssrпосле сборки и проверьте наличиеserver.jsв корне, чтобы убедиться, что Appwrite получит ожидаемую структуру.
Эти простые шаги позволяют интегрировать Turborepo‑монорепозитории с Appwrite Sites без необходимости менять архитектуру проекта или отказываться от преимуществ монорепозитория.