Перейти к содержимому
Версия 1.0 ещё не выпущена; проект находится на стадии pre-MVP. См. возможности и ограничения версии 1.0.

Расширение бэкенда

Для настройки генерируемого репозитория не нужно отключаться. Точки расширения в зонах owned и shared доступны при сохранённом подключении к Dee Wan. Отключайтесь, только чтобы остановить управляемую регенерацию. Автономный бэкенд: обзор описывает генерируемый бэкенд, а детали реализации перечисляют его зависимости.

  1. Сгенерируйте проект локально: npm run backend:generate или npm run backend:deliver. Раздел Настройки → Код и публикация не компилирует исходники (source_build_runtime_unavailable).
  2. Клонируйте репозиторий владельца.
  3. Меняйте файлы в зонах owned и shared, начиная с src/owned/backend.ts для API.
  4. Выполните проверки артефакта, создайте коммит и отправьте изменения.
  5. Разверните именно этот коммит через deploy-from-repository.yml.
  6. Позже запустите регенерацию из Dee Wan. Owned/shared-изменения сохранятся. Изменённые generated-файлы остановят её.
  7. Отключайтесь, только если владелец хочет прекратить управляемую регенерацию.

Матрица аутентификации GitHub объясняет полномочия для доставки. Руководство по отключению описывает проверки независимости. Настройка не требует отключения.

Что можно менять при сохранённом подключении?

Заголовок раздела «Что можно менять при сохранённом подключении?»
Изменение Где Поведение при регенерации
Включение/отключение операции, hooks, override с тем же контрактом src/owned/backend.ts Сохраняется. Удалённые ID, конфликты настроек и изменения состава групп вызывают отказ при проверке/конструировании. Generated-защита остаётся вне owned-политики.
Декларативные API-маршруты Тот же реестр, route() Сохраняются. Совпадение нормализованных путей приводит к отказу. Корректные схемы включаются в OpenAPI и проверяют вход/выход.
Raw Hono src/owned/routes.ts Сохраняется, монтируется последним. Не заменяет generated-пути, включая отключённые операции. Для прямых маршрутов нет гарантии отсутствия коллизий и автоматического описания в OpenAPI.
Общая бизнес-логика Другие файлы src/owned/ Сохраняются побайтно. Импортируйте из реестра или raw-маршрутов.
Свои таблицы и миграции SQL/runner в src/owned/, свой workflow Исходники и посторонние таблицы/строки сохраняются. Миграцию запускайте явно. Она не добавляется автоматически в generated Prisma schema или migration runner. Не меняйте generated-таблицы.
Зависимости и выбор версий Корневой и apps/site/package.json Дополняющее слияние сохраняет ваши значения. Добавляет недостающие обязательные зависимости/overrides. Выбранная версия должна удовлетворять контрактам сборки/runtime.
Привязки Wrangler и секреты Общие root/site wrangler.jsonc, свой workflow и хранилище секретов Общие исходники сохраняются. Дополнительные локальные привязки не создаются и не копируются автоматически в wrangler.deploy.jsonc. Build/deploy-конфиги остаются generated. Не коммитьте секреты.
Свои CI workflows .github/workflows/<your-name>.yml Сохраняются, кроме двух generated-имён.
Astro head/body и слоты apps/site/src/owned/Head.astro, BodyEnd.astro, slots/ Сохраняются. Generated-layout импортирует их.
Обёртки и замены секций apps/site/src/owned/sections/ Сохраняются. Generated-контракты секций продолжают проверять типы вашей реализации.
Интеграции и islands apps/site/src/owned/config.mjs, owned-компоненты, зависимости сайта Сохраняются. Добавьте интеграцию вместе с зависимостями, затем проверьте/соберите Astro.
Свои страницы apps/site/src/pages/owned/ Сохраняются и обслуживаются в /owned/. Generated-страницы/redirects не могут занять это пространство.
Generated-файлы и строки контента Схема, клиенты/маршруты/защита, снимки, данные страниц, таблицы контента Не редактируйте, пока подключение сохранено. Проверки отпечатков исходников, хешей снимков и наборов строк обнаружат расхождения и остановят регенерацию. Меняйте модели и контент в CMS или используйте зону owned.

README.md, .nvmrc, .npmrc, .gitignore и существующие общие Wrangler-файлы сохраняются. deploy-from-repository.yml генерируется, а в артефакте целого сайта вместе с ним — dee-wan-build.yml (у артефакта --model нет управляемой CMS-сборки сайта). Остальные ваши workflows являются shared. История SQL-миграций сохраняется. Не переписывайте применённую миграцию.

Путь Зона
prisma/schema.prisma generated
graduation.manifest.json generated
src/app.ts generated
src/worker.ts generated
src/security.ts generated
src/owned/backend.ts owned
src/owned/routes.ts owned
src/backend-customization.ts generated
.dee-wan-customization.json generated
src/serve-cli.ts generated
migrations/0002_graduation_ledger.sql generated
scripts/regenerate.mjs generated
scripts/emit-migration.mjs generated
scripts/file-manifest.mjs generated
scripts/portable-client.mjs generated
scripts/configure-deployment.mjs generated
tsconfig.json generated
wrangler.example.jsonc generated
wrangler.build.jsonc generated
scripts/load-snapshot.mjs generated
data/published-snapshot.json generated
.github/workflows/dee-wan-build.yml generated
.github/workflows/deploy-from-repository.yml generated
.github/workflows/anything-else.yml shared
package.json shared
package-lock.json shared
.npmrc shared
.nvmrc shared
wrangler.jsonc shared
.gitignore shared
README.md shared
apps/site/src/owned/config.mjs owned
apps/site/src/pages/owned/thanks.astro owned
apps/site/package.json shared
apps/site/package-lock.json shared
apps/site/.npmrc shared
apps/site/wrangler.jsonc shared
apps/site/src/lib/pages.json generated

src/owned/backend.ts создаётся только при отсутствии. Регенерация не переписывает существующий реестр. npm run customization:check восстанавливает отсутствующий реестр без пользовательских настроек и проверяет каталог. Сгенерированное приложение импортирует его. Поведение не зависит от порядка регистрации маршрутов.

Примеры ниже предполагают модель Article. Используйте фактические ID и поля Prisma из src/backend-customization.ts. Каждый пример — самостоятельный вариант реестра. Для нескольких возможностей объедините записи в одном defineBackend().

import { defineBackend } from '../backend-customization'
export default defineBackend({
operations: {
'Article.findMany': {
before: [(c) => { c.header('x-content-policy', 'owned') }],
},
},
})

before и after — обработчики этапов выполнения запроса. Они не являются обработчиками жизненного цикла Prisma, транзакциями, блоками finally или функциями, вызываемыми после фиксации транзакции. Окончательный ответ или исключение останавливает оставшуюся часть этапа. При ошибке вызов after не гарантирован; его выполнение не подтверждает фиксацию транзакции.

import { defineBackend } from '../backend-customization'
export default defineBackend({
operations: {
'Article.findMany': {
override: async ({ input, args, context, prisma, core }) => {
void input
void args
void context
void prisma
return [...await core()].reverse()
},
},
},
})

input и args содержат одни и те же разобранные аргументы Prisma. context содержит проверенные данные субъекта доступа (principal). Через prisma доступен только delegate целевой операции с применённой защитой: прямой SQL, исходный клиент и другие модели или операции недоступны. Для постраничного чтения доступны защищённые findMany и count. core() выполняет сгенерированную реализацию с тем же состоянием запроса и проверок доступа. Сам вызов не создаёт транзакцию и не организует повторную попытку. Вызывайте его один раз, если повторное выполнение не требуется намеренно.

import { defineBackend } from '../backend-customization'
export default defineBackend({
operations: {
'Article.findMany': {
override: async () => [],
},
},
})

Замена возвращает пустой массив по сгенерированному контракту списка и не вызывает core(). Аутентификация, полномочия и разрешённые guards остаются обязательными. Для другой структуры ответа, другого набора статусов или пути нужен новый декларативный маршрут. Типы выводятся из generated-аргументов/результатов Prisma. После изменения или удаления модели либо операции устаревшие ссылки выявляются при проверке типов или создании приложения.

import { defineBackend } from '../backend-customization'
export default defineBackend({
operations: { 'Article.delete': { enabled: false } },
})

enabled: true сохраняет сгенерированную операцию. Это не позволяет включить маршрут, для которого генератор не создал защиту. Отключённые пути остаются зарезервированными. Массив деклараций одного ID может объединять hooks. Несколько overrides, противоречивые enable-настройки или disable вместе с override вызывают отказ.

import { defineBackend } from '../backend-customization'
export default defineBackend({
groups: [
{
name: 'content-reads',
select: 'reads',
accepted: ['Article.findMany', 'Article.findManyPaginated', 'Article.findUnique'],
before: [(c) => { c.header('x-content-read', 'owned') }],
},
{
name: 'content-writes',
select: 'writes',
accepted: ['Article.create', 'Article.update', 'Article.delete'],
after: [(c) => { c.header('x-content-write', 'owned') }],
},
],
})

Пример предполагает, что Article — единственная модель без переводов/связующих таблиц. select: ['Article.findMany', 'Article.findUnique'] выбирает явные ID. Селекторы reads/writes охватывают весь каталог сгенерированных операций, включая модели только для чтения. Имена групп уникальны. Hooks выполняются в порядке объявления групп.

accepted фиксирует точный набор. customization:check записывает раскрытые ID в .dee-wan-customization.json. Добавление/удаление модели или операции вызывает отказ, если состав изменился. Проверьте точный предлагаемый список в ошибке, измените accepted нужной группы в owned-реестре и повторите регенерацию/проверку. Не редактируйте сгенерированный файл состояния и не вычисляйте принимаемый набор фильтрацией текущего каталога: иначе будущие изменения будут приниматься без проверки.

Группа также может содержать типизированные overrides: { 'Article.findMany': async ({ core }) => core() } для ID из принятого набора. Второй override из другой группы или настроек операции вызывает отказ. Порядок объявления не даёт одному override приоритета над другим.

Variant hooks задаются через operations['Article.findMany'].variants.reader.before/after или generated-вариант editor. Они используют существующий механизм вариантов генератора. Неизвестные варианты и попытки задать owned-формы или router-level защиту отвергаются. Второго диспетчера вариантов нет.

Порядок: платформенная аутентификация/полномочия, разрешение guard, group before, operation before, variant before, затем generated/override handler. После обработчика: variant after, operation after, group after, внешняя защита ответа/ошибки. Окончательный ответ или ошибка сохраняет предусмотренное вышележащими обработчиками досрочное завершение. Hooks должны сохранять документированную форму запроса/результата.

import { defineBackend, route } from '../backend-customization'
export default defineBackend({
routes: [route({
method: 'POST',
path: '/echo',
access: { mode: 'service', operation: 'create' },
request: {
type: 'object',
properties: { text: { type: 'string' } },
required: ['text'],
additionalProperties: false,
},
response: {
type: 'object',
properties: { text: { type: 'string' } },
required: ['text'],
additionalProperties: false,
},
handler: async ({ input }) => ({ text: input.text }),
})],
})

Путь задаётся относительно /api. Методы: GET, POST, PUT, PATCH, DELETE. access.operation — read/create/update/delete. Необязательный access.model указывает сгенерированную модель. Без модели токену чтения нужна область *. Обработчик получает проверенные типизированные данные, подтверждённого субъекта доступа и параметры пути. Исходный клиент ему не передаётся.

Объявляйте каждый пользовательский маршрут через route(). Скопированные или собранные вручную описания маршрутов вызывают отказ при конструировании. Схемы запроса/ответа фиксируются при объявлении, поэтому последующая мутация не меняет валидацию или OpenAPI.

Схемы поддерживают string/enum, конечное число, безопасное целое, boolean, null, массивы и закрытые объекты с обязательными полями. Неподдерживаемые ключи отклоняются, чтобы OpenAPI не описывал правила, которые на деле не проверяются. GET query-поля должны быть строками. Остальные методы разбирают JSON body. Неверный вход возвращает 400, результат вне схемы — 500.

Совпадение метода и нормализованного пути между сгенерированными и декларативными маршрутами приводит к отказу, даже если сгенерированная операция отключена. Повторные/конечные слеши нормализуются. Wildcard, кодированные пути, query/fragment и traversal запрещены. Пути, различающиеся только именем параметра, и пересечения статических путей с параметризованными тоже отклоняются. Совпадение со сгенерированным маршрутом разрешается только через переопределение операции. Корректные декларативные схемы включаются в /api/openapi.json, доступный после аутентификации; прямые маршруты Hono — нет.

Императивные интеграции сохраняйте в src/owned/routes.ts. Они монтируются после generated/declarative-маршрутов и зарезервированных generated-путей. На прямых маршрутах выполняются внешняя аутентификация и защита от раскрытия данных в ошибках. Для них нет декларативной гарантии отсутствия коллизий, автоматической проверки схем или сгенерированного описания OpenAPI.

Совместимый mount по-прежнему определяет модель по префиксу пути. Raw-путь без модели требует wildcard read scope. Явно применяйте .guard(shape, variant) к Prisma-запросам. Расширение клиента само по себе не защищает произвольные вызовы. Предпочитайте декларативные метаданные доступа, если путь не представляет generated-модель.

Политика определяет анонимный, сессионный или сервисный доступ. Для модели без настройки действует service. Токены чтения хранятся в виде хешей с ограничением по моделям в ACCESS_CONFIG. Маршруты без модели и /api/openapi.json требуют wildcard scope. Сервисная операция записи без настроенной аутентификации для записи возвращает 503 writes_unavailable, отсутствие учётных данных — 401, отказ полномочий — 403. Необязательный Better Auth предоставляет сессии локальному runtime и Worker. Локальный runtime и Worker принимают один и тот же сервисный bearer API_BEARER_TOKEN.

Нормализация/скрытие внутренних данных в ошибках остаётся вне overrides и hooks. Для новой формы ответа нужен новый маршрут. Дополнительная авторизация относится к бизнес-логике и не отменяет платформенные проверки.

Измените apps/site/src/owned/Head.astro:

<meta name="color-scheme" content="light dark" />

Новую страницу добавьте в apps/site/src/pages/owned/thanks.astro. Для island добавьте интеграцию/зависимости фреймворка и настройте apps/site/src/owned/config.mjs. Generated-конфигурация Astro импортирует её. Head.astro, BodyEnd.astro, слоты и wrappers/overrides секций — независимые точки расширения. Мост предпросмотра остаётся необязательным в extensions.ts.

Оба package-манифеста сохраняют значения dependencies/overrides и добавляют недостающие обязательные записи. В корневом манифесте также добавляются отсутствующие команды в раздел scripts. Существующие команды остаются вашими. Generated build/deploy workflows запускают проверку кастомизации напрямую, даже если сохранена прежняя owned-команда typecheck. Выбор package manager и lifecycle-политики остаётся вашим. Несовместимая версия не проходит проверку/сборку. В корневом манифесте фиксируются версии инструментов генератора; диапазоны версий сайта закреплены его lock-файлом. Если манифест не удаётся прочитать, сборка останавливается. После изменений используйте проверенный установщик.

Храните бизнес-логику, собственный SQL и migration runners в src/owned/. Запускайте свои миграции из собственного workflow. Generated Prisma-модели описывают только CMS-контент: новая таблица приложения не создаёт модель или endpoint автоматически. Сохраняйте порядок миграций и план отката.

При регенерации сравниваются отпечатки сгенерированных файлов, переносятся файлы owned/shared, добавляются миграции и обновляется сгенерированный контент. Изменённые generated-файлы/снимки вызывают отказ. Когда загружается изменившийся снимок, он заменяет строки каждой генерируемой контентной таблицы: строка, которой нет в снимке, удаляется, кто бы её ни записал. Ваши собственные таблицы не входят в снимок и сохраняют свои строки.

Команде npm run regenerate нужны определения сайта в definitions.json и GENERATION_SOURCE_VERSION. Управляемый CI получает версию от CMS и скачивает определения. Как получить их самому, показано в Локальная перегенерация. Когда файл и переменная подготовлены:

Окно терминала
node scripts/install-dependencies.mjs
GENERATION_SOURCE_VERSION=<version> npm run regenerate
npm run migrate:emit
npm run typecheck
node scripts/install-dependencies.mjs apps/site
npm run --prefix apps/site check
npm run --prefix apps/site build
npm run build
npm run files:manifest

Закоммитьте/отправьте проверенный репозиторий и запустите deploy-from-repository.yml для точного git_sha. Настройте CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, API_BEARER_TOKEN, PUBLIC_HOSTNAME, как описано в разделе ресурсы и конфигурация Cloudflare. Управляемая регенерация вызывает тот же workflow после коммита обновлений.

Для локального сервера используйте db:init, build:serve, serve:local с локальными настройками доступа и bearer-токеном записи, как описано в сгенерированном README. npm run dev запускает Wrangler локально. До развёртывания проверьте отказ без аутентификации, разрешённые чтения/записи, guard-отказы, overrides и скрытие деталей ошибок. Старый коммит кода — не резервная копия базы: откат должен учитывать совместимость миграций и медиа.

Необязательная аутентификация и доступ артефакта

Заголовок раздела «Необязательная аутентификация и доступ артефакта»

В разделе Настройки → Код и публикация → Параметры разработчика → Сгенерированный бэкенд включите Better Auth один раз для бэкенда. Выберите нужные способы входа: Пароль, Ссылка для входа (Resend), Google или GitHub. По умолчанию он выключен: зависимость, runtime, маршруты, таблицы, миграция, настройки провайдеров и секреты Better Auth не создаются. Аккаунты CMS, сессии, приглашения, хеши паролей и ключи провайдеров не переносятся. Прикладная модель User отделена от таблиц идентификации артефакта.

Режим доступа Кто может отправить запрос
public Явный типизированный анонимный principal без учётных данных.
session Действующая сессия Better Auth этого артефакта.
service API_BEARER_TOKEN или scoped-токен, разрешающий чтение.
session_or_service Сессия артефакта либо сервисная аутентификация.

Для модели без настройки явно действует service. Операция без override наследует режим модели. Используются точные ID: User.findMany, User.findManyPaginated, User.findUnique, User.create, User.update, User.delete. Удалённые или переименованные модели/операции вызывают отказ валидации. Сгенерированные типы тоже отклоняют устаревшие ID. Доступность операций остаётся в owned-реестре: политика доступа не включает выключенную операцию. Политики требуют сессию независимо от способа входа.

Аутентификация и проверка полномочий выполняются до hooks и overrides. Guards, существующие границы владения и защита от раскрытия данных в ошибках обязательны. Публичная операция создания отклоняет системные поля, владельца, арендатора, роли, администратора, пароль, верификацию и связи провайдера, включая вложенные значения. Она создаёт прикладные данные. Регистрация идёт только через маршруты Better Auth. Raw-маршруты используют service. Декларативные маршруты обязаны указать access.mode и access.operation. Модель необязательна. OpenAPI показывает режим каждой операции.

Необязательный корень установки Пакет Блок Точная версия
root, только при включении better-auth dependencies 1.6.25

При включении создаются src/better-auth.ts, проверка провайдеров, существующие правила ограничений/блокировки входа и отдельная миграция инфраструктурных таблиц _artifact_auth_*. У них нет CRUD-маршрутов. Node/SQLite и Worker/D1 используют одинаковые методы, ограничения пароля, проверенные учётные записи OAuth, хешированные токены и отзыв сессий через БД. В обеих средах сессия ограничена семью днями, а для запросов, изменяющих данные, проверяется origin. Access/refresh/ID-токены провайдеров удаляются. Ответы не раскрывают внутренние ошибки провайдера.

npm run auth:init создаёт новый случайный секрет в игнорируемом .dev.vars, сохраняя уже существующий секрет артефакта. Node загружает файл через node --env-file=.dev.vars src/serve.local.mjs, Wrangler — автоматически. Сначала выполните db:init. ARTIFACT_ORIGIN — origin API, например локальный http://127.0.0.1:8787. Удалённый origin требует HTTPS. Развёртывание берёт его из проверенного целевого hostname, а не из CMS.

Выбранный метод Обязательные runtime-значения Целевой callback
Password BETTER_AUTH_SECRET Маршруты пароля под /api/auth
Magic link BETTER_AUTH_SECRET, RESEND_API_KEY, MAIL_FROM <ARTIFACT_ORIGIN>/api/auth/magic-link/verify
Google BETTER_AUTH_SECRET, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET <ARTIFACT_ORIGIN>/api/auth/callback/google
GitHub BETTER_AUTH_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET <ARTIFACT_ORIGIN>/api/auth/callback/github

Зарегистрируйте callbacks в OAuth-приложениях владельца артефакта. Resend требует подтверждённый домен отправителя. Ключи нужны только выбранным методам. Их отсутствие останавливает preflight до развёртывания. Имена Actions secrets получают префикс ARTIFACT_, обходя зарезервированный GitHub префикс GITHUB_. При публикации система создаёт новый секрет артефакта и шифрует его целевым публичным ключом. Секреты, уже существующие под целевыми именами, сохраняются без ротации. npm run backend:deliver читает ключи провайдеров только из переменных окружения ARTIFACT_*. Значения секретов не попадают в исходники, манифесты, входные данные генерации, дайджесты, отслеживаемые переменные, логи и журнал развёртывания.

Регенерация сохраняет owned/shared-файлы. Для включения auth в существующем shared-пакете и смены методов нужна повторная генерация, обновляющая зависимости и bindings вызывающего workflow. Выключить аутентификацию в существующем целевом каталоге нельзя: используйте новый каталог, сохранив учётные записи и историю миграций прежнего. Изменения доступа учитываются в дайджестах исходных данных генерации и манифесте.

verify:deployment учитывает политику: публичное чтение проверяется анонимно, сервисное — bearer-токеном, session-only требует действующую целевую сессию в ARTIFACT_VERIFY_SESSION_COOKIE. Без неё проверка возвращает verify_session_required и не объявляет непроведённую проверку успешной. При первом развёртывании инициализируйте целевой бэкенд, зарегистрируйтесь через его auth-маршруты и повторите проверку с сессией. Не сохраняйте краткоживущую сессию в исходниках или материалах проверки. Проверка в браузере и приёмка рабочего развёртывания остаются отдельными этапами.

Определите запросы, которые принимают ваши маршруты, в разделе варианты запросов и guard-правила.


Стабильная версия ещё не выпущена (pre-MVP)
Коммит исходного кода: e207e63239a627fbd2ae9c414ef7c6eb19fcfe5a (с локальными изменениями)
Дата сборки (UTC): 2026-10-21T09:28:24.214Z
Коммит для обновления: не определён: в рабочей копии есть изменения. Примечания к выпуску