Files
upstream-twenty/packages/twenty-docs/l/ru/developers/extend/apps/config/install-hooks.mdx
upstream-mirror e7da97194a
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

189 lines
18 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Установочные хуки
description: Выполняйте логику во время жизненного цикла установки, обновления или удаления — выполняйте начальное заполнение данными, создавайте резервные копии записей, проверяйте корректность обновления, очищайте внешние ресурсы.
icon: wrench
---
Установочные хуки — это специальные логические функции, которые выполняются во время жизненного цикла установки, обновления или удаления. Они используют то же окружение выполнения обработчика, что и обычные [logic functions](/l/ru/developers/extend/apps/logic/logic-functions), но объявляются с помощью собственных функций определения и существуют вне обычной модели триггеров (HTTP, cron, события базы данных). Установочные хуки получают `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` имеет значение `undefined` при новой установке); хук удаления получает `UninstallPayload` (`{ version?: string }` — версия, которая удаляется).
Каждое приложение может определить **не более одного** хука каждого типа (предустановочный, постустановочный, хук удаления). Сборка манифеста завершится ошибкой, если будет обнаружено более одного хука любого типа.
```
┌─────────────────────────────────────────────────────────────┐
│ install flow │
│ │
│ upload package → [pre-install] → metadata migration → │
│ generate SDK → [post-install] │
│ │
│ old schema visible new schema visible │
└─────────────────────────────────────────────────────────────┘
```
## Краткий обзор
| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Запуски | До миграции метаданных — **предыдущая** схема и данные остаются нетронутыми | После миграции и генерации SDK — **новая** схема уже применяется |
| Выполнение | Всегда синхронное; блокирует установку | Асинхронно по умолчанию (постановка в очередь, 3 повторные попытки); синхронный режим по желанию через `shouldRunSynchronously: true` |
| При ошибке | Установка **прерывается** до любых изменений схемы | Асинхронный режим: до 3 повторных попыток. Синхронный режим: вызывающая сторона получает `POST_INSTALL_ERROR` (изменения схемы **не** откатываются) |
| Типичное использование | Создать резервную копию или исправить данные, которые были бы потеряны при миграции; отклонить рискованное обновление, выбросив исключение | Наполнить данными по умолчанию, настроить рабочее пространство, зарегистрировать внешние ресурсы |
**Общее практическое правило:** по умолчанию используйте post-install. Обращайтесь к pre-install только тогда, когда сама миграция разрушительна и вам нужно перехватить предыдущее состояние, прежде чем оно исчезнет.
| Вы хотите... | Использовать |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Инициализировать данные, настроить рабочее пространство, зарегистрировать внешние ресурсы | `post-install` |
| Длительные операции, которые не должны блокировать ответ установки | `post-install` (асинхронный режим по умолчанию, с повторными попытками воркера) |
| Быстрая настройка, на которую вызывающая сторона полагается сразу после завершения установки | `post-install` с `shouldRunSynchronously: true` |
| Прочитать или сохранить данные, которые предстоящая миграция может потерять | `pre-install` |
| Отклонить обновление, которое повредит существующие данные | `pre-install` (бросьте исключение из обработчика) |
| Согласование при каждом обновлении | Любой хук с `shouldRunOnVersionUpgrade: true` |
## Общее поведение для обоих хуков
* Конфиг — это конфиг `defineLogicFunction` без настроек триггера, но с `shouldRunOnVersionUpgrade`.
* **Когда запускается**: по умолчанию только при чистой установке. Установите `shouldRunOnVersionUpgrade: true`, чтобы также запускать его при обновлениях. Используйте `previousVersion` / `newVersion`, чтобы разветвлять логику в зависимости от пути обновления.
* **Идемпотентность имеет значение**: асинхронный post-install может быть повторно выполнен, и любой хук будет запускаться повторно при обновлениях, если включён `shouldRunOnVersionUpgrade`.
* Обычное окружение logic-функций (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) инъецируется, поэтому вы можете вызывать Twenty API с токеном своего приложения.
* Хук автоматически прикрепляется к манифесту приложения на этапе сборки (`preInstallLogicFunction` / `postInstallLogicFunction`) — ничего не нужно указывать в [`defineApplication()`](/l/ru/developers/extend/apps/config/application).
* Значение `timeoutSeconds` по умолчанию — 300, чтобы позволить выполнять более длительные задачи настройки, такие как инициализация данных.
* **Не выполняется в dev-режиме**: `yarn twenty dev` пропускает процесс установки и синхронизирует файлы напрямую, поэтому хуки там никогда не запускаются. Вместо этого запускайте их вручную:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
```
<AccordionGroup>
<Accordion title="definePostInstallLogicFunction" description="Выполняется после применения миграции метаданных рабочего пространства">
Запускается после завершения установки приложения: метаданные синхронизированы, SDK-клиент сгенерирован, новая схема доступна для запросов. Пример — инициализировать запись по умолчанию при новой установке:
```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
if (previousVersion) return; // fresh installs only
const client = new CoreApiClient();
await client.mutation({
createPostCard: {
__args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
id: true,
},
});
};
export default definePostInstallLogicFunction({
universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
name: 'post-install',
description: 'Seeds a welcome post card after install.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: false,
shouldRunSynchronously: false,
handler,
});
```
Флаг `shouldRunSynchronously` управляет моделью выполнения:
* `false` *(по умолчанию)* — ставится в очередь сообщений (`retryLimit: 3`) и выполняется воркером. Ответ установки возвращается, как только задача поставлена в очередь. **Используйте для длительных операций** — инициализация больших наборов данных, медленные сторонние API.
* `true` — выполняется непосредственно в процессе установки. Запрос на установку блокируется до завершения обработчика; выброшенная ошибка возвращается вызывающей стороне как `POST_INSTALL_ERROR` (без повторных попыток). **Используйте для быстрых операций, которые обязательно должны завершиться до ответа.** На этом этапе миграция уже применена, поэтому сбой не откатывает изменения схемы — он только сообщает об ошибке.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="Выполняется до применения миграции метаданных рабочего пространства">
Запускается до миграции метаданных, в контексте **предыдущей** схемы — подходящее место, чтобы создать резервную копию данных, которые были бы потеряны при миграции, или отклонить рискованное обновление. Перед выполнением сервер запускает чисто добавочную «урезанную синхронизацию», которая регистрирует только pre-install функцию новой версии; всё остальное — объекты, поля и данные предыдущей версии — остаётся нетронутым во время выполнения вашего обработчика.
Pre-install всегда **синхронный** и блокирует установку. Если обработчик генерирует исключение, установка прерывается до применения каких-либо изменений схемы — рабочее пространство остаётся на предыдущей версии в согласованном состоянии. Это сделано намеренно: pre-install — ваш последний шанс отказать в рискованном обновлении.
Пример — скопировать значения устаревшего поля перед тем, как миграция его удалит:
```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) {
return;
}
const client = new CoreApiClient();
const { postCards } = await client.query({
postCards: {
__args: { filter: { notes: { isNot: null } } },
edges: { node: { id: true, notes: true } },
},
});
// Copy legacy `notes` into `description` before the migration drops the
// column. If this fails, the upgrade aborts and the workspace stays on v1.
for (const { node } of postCards.edges) {
await client.mutation({
updatePostCard: {
__args: { id: node.id, data: { description: node.notes } },
id: true,
},
});
}
};
export default definePreInstallLogicFunction({
universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
name: 'pre-install',
description: 'Backs up legacy notes into description before the v2 migration.',
timeoutSeconds: 300,
shouldRunOnVersionUpgrade: true,
handler,
});
```
</Accordion>
</AccordionGroup>
## Хук удаления
`defineUninstallLogicFunction` объявляет хук, который выполняется, когда пользователь удаляет ваше приложение. Он выполняется **до того**, как метаданные, данные и код приложения будут удалены — после запуска миграции удаления уже нечему будет выполняться — поэтому ваш обработчик все еще может запрашивать объекты и записи приложения. Используйте его для очистки внешних ресурсов: отмены выделения ресурсов API, удаления оставшихся ботов, отзыва вебхуков.
Заметки:
* Хук работает по принципу «по возможности»: он выполняется синхронно, но сбой только логируется и **никогда не блокирует удаление** — очистка не должна делать удаление приложения невозможным.
* Он получает `UninstallPayload` (`{ version?: string }` — версия, которая удаляется).
* Он **не** выполняется при откате неуспешной новой установки — приложение так и не было полностью установлено.
* Хук не может выполниться после того, как приложение удалено, поэтому внешняя очистка, которая зависит от данных приложения (например, ID ботов, сохраненные в записях), должна выполняться здесь, а не во внешнем запланированном задании.
* Как и установочные хуки, он **не выполняется в режиме разработки** — вместо этого запустите его вручную:
```bash filename="Terminal"
yarn twenty dev:function:exec --uninstall
```
```ts src/logic-functions/uninstall.ts
import { defineUninstallLogicFunction, type UninstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';
const handler = async (_payload: UninstallPayload): Promise<void> => {
const client = new CoreApiClient();
const { meetingBots } = await client.query({
meetingBots: { edges: { node: { id: true, externalBotId: true } } },
});
// Delete the provider-side bots so nothing keeps recording after uninstall.
for (const { node } of meetingBots.edges) {
await fetch(`https://api.recorder.example/bots/${node.externalBotId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.RECORDER_API_KEY}` },
});
}
};
export default defineUninstallLogicFunction({
universalIdentifier: 'b2c3d4e5-6789-01bc-def0-234567890abc',
name: 'uninstall',
description: 'Deletes remaining recorder bots when the app is uninstalled.',
timeoutSeconds: 300,
handler,
});
```