a3a6a55051
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
189 lines
13 KiB
Plaintext
189 lines
13 KiB
Plaintext
---
|
|
title: Hook-uri de instalare
|
|
description: Rulați logică în timpul ciclului de viață de instalare, actualizare sau dezinstalare — populați date, faceți backup pentru înregistrări, validați actualizarea, curățați resursele externe.
|
|
icon: wrench
|
|
---
|
|
|
|
Hook-urile de instalare sunt funcții logice speciale care rulează în timpul ciclului de viață de instalare, actualizare sau dezinstalare. Acestea folosesc același runtime de handler ca și [funcțiile logice](/l/ro/developers/extend/apps/logic/logic-functions) obișnuite, dar sunt declarate cu propriile lor funcții de definire și există în afara modelului obișnuit de declanșatori (HTTP, cron, evenimente de bază de date). Hook-urile de instalare primesc un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` este `undefined` la o instalare nouă); hook-ul de dezinstalare primește un `UninstallPayload` (`{ version?: string }` — versiunea care este eliminată).
|
|
|
|
Fiecare aplicație poate defini **cel mult unul** din fiecare hook (pre-instalare, post-instalare, dezinstalare). Construirea manifestului va genera o eroare dacă se detectează mai mult de unul de orice tip.
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ install flow │
|
|
│ │
|
|
│ upload package → [pre-install] → metadata migration → │
|
|
│ generate SDK → [post-install] │
|
|
│ │
|
|
│ old schema visible new schema visible │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Dintr-o privire
|
|
|
|
| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
|
|
| ---------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Rulări | Înainte de migrarea metadatelor — schema și datele **anterioare** sunt încă intacte | După migrare și generarea SDK — schema **nouă** este aplicată |
|
|
| Execuție | Întotdeauna sincronă; blochează instalarea | Async în mod implicit (pus în coadă, 3 reîncercări); execuție sincronă opțională prin `shouldRunSynchronously: true` |
|
|
| La eșec | Instalarea este **întreruptă** înainte de orice modificare a schemei | Async: reîncercată de până la 3 ori. Sync: apelantul primește `POST_INSTALL_ERROR` (modificările de schemă **nu** sunt anulate) |
|
|
| Utilizare tipică | Faceți backup sau reparați date pe care o migrare le-ar pierde; refuzați un upgrade riscant aruncând o eroare | Populați date implicite, configurați workspace-ul, înregistrați resurse externe |
|
|
|
|
**Regulă generală:** folosiți implicit post-install. Apelați la pre-install doar când migrarea în sine este distructivă și trebuie să interceptați starea anterioară înainte să dispară.
|
|
|
|
| Doriți să... | Folosiți |
|
|
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
| Populați date, configurați workspace-ul, înregistrați resurse externe | `post-install` |
|
|
| Muncă de durată care nu ar trebui să blocheze răspunsul la instalare | `post-install` (mod async implicit, cu reîncercări ale workerului) |
|
|
| Configurare rapidă de care apelantul are nevoie imediat după ce instalarea se încheie | `post-install` cu `shouldRunSynchronously: true` |
|
|
| Citiți sau faceți backup datelor pe care migrarea iminentă le-ar pierde | `pre-install` |
|
|
| Respingeți o actualizare care ar corupe datele existente | `pre-install` (aruncați din handler) |
|
|
| Reconciliere la fiecare upgrade | Oricare hook cu `shouldRunOnVersionUpgrade: true` |
|
|
|
|
## Comportament partajat de ambele hook-uri
|
|
|
|
* Configurația este o configurație `defineLogicFunction` minus setările de declanșare, plus `shouldRunOnVersionUpgrade`.
|
|
* **Când rulează**: doar la instalări noi, în mod implicit. Setați `shouldRunOnVersionUpgrade: true` pentru a rula și la upgrade-uri. Folosiți `previousVersion` / `newVersion` pentru a ramifica în funcție de calea de upgrade.
|
|
* **Idempotența contează**: post-install asincron poate fi reîncercat, iar oricare hook rulează din nou la upgrade-uri când `shouldRunOnVersionUpgrade` este activat.
|
|
* Mediul obișnuit al funcțiilor logice (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) este injectat, astfel încât puteți apela API-ul Twenty cu tokenul aplicației voastre.
|
|
* Hook-ul este atașat automat la manifestul aplicației la build (`preInstallLogicFunction` / `postInstallLogicFunction`) — nu este nevoie să fie referențiat în [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
|
|
* Valoarea implicită pentru `timeoutSeconds` este 300 pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor.
|
|
* **Nu este executat în modul dev**: `yarn twenty dev` sare peste fluxul de instalare și sincronizează fișierele direct, astfel încât hook-urile nu rulează acolo. Declanșați-le manual în schimb:
|
|
|
|
```bash filename="Terminal"
|
|
yarn twenty dev:function:exec --postInstall
|
|
yarn twenty dev:function:exec --preInstall
|
|
```
|
|
|
|
<AccordionGroup>
|
|
<Accordion title="definePostInstallLogicFunction" description="Rulează după ce migrarea metadatelor workspace-ului este aplicată">
|
|
|
|
Rulează după ce aplicația voastră a terminat instalarea: metadate sincronizate, clientul SDK generat, noua schemă poate fi interogată. Exemplu — populează o înregistrare implicită la instalări noi:
|
|
|
|
```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,
|
|
});
|
|
```
|
|
|
|
Flag-ul `shouldRunSynchronously` controlează modelul de execuție:
|
|
|
|
* `false` *(implicit)* — pus în coada de mesaje (`retryLimit: 3`) și rulat de un worker. Răspunsul la instalare este returnat imediat ce jobul este pus în coadă. **Folosiți pentru muncă de durată** — popularea unor seturi mari de date, API-uri lente ale terților.
|
|
* `true` — executat inline în timpul fluxului de instalare. Cererea de instalare este blocată până când handlerul se termină; o eroare aruncată este expusă apelantului ca `POST_INSTALL_ERROR` (fără reîncercări). **Folosiți pentru muncă rapidă, care trebuie să fie finalizată înainte de răspuns.** Migrarea a fost deja aplicată în acest punct, astfel încât un eșec nu anulează modificările de schemă — doar expune eroarea.
|
|
|
|
</Accordion>
|
|
<Accordion title="definePreInstallLogicFunction" description="Rulează înainte ca migrarea metadatelor workspace-ului să fie aplicată">
|
|
|
|
Rulează înainte de migrarea metadatelor, pe schema **anterioară** — locul potrivit pentru a face backup datelor pe care o migrare le-ar pierde sau pentru a refuza un upgrade riscant. Înainte de execuție, serverul rulează un „sync redus”, pur aditiv, care înregistrează doar funcția de pre-instalare a versiunii noi; tot restul — obiectele, câmpurile și datele versiunii anterioare — rămâne neatins atunci când rulează handlerul.
|
|
|
|
Pre-install este întotdeauna **sincron** și blochează instalarea. Dacă handlerul aruncă o eroare, instalarea este întreruptă înainte de orice modificare a schemei — workspace-ul rămâne la versiunea anterioară într-o stare consistentă. Acest lucru este intenționat: pre-install este ultima dvs. șansă de a refuza o actualizare riscantă.
|
|
|
|
Exemplu — copiați valorile unui câmp vechi înainte ca migrarea să îl elimine:
|
|
|
|
```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>
|
|
|
|
## Hook de dezinstalare
|
|
|
|
`defineUninstallLogicFunction` declară un hook care rulează atunci când un utilizator dezinstalează aplicația ta. Acesta este executat **înainte** ca metadatele, datele și codul aplicației să fie eliminate — odată ce migrarea de ștergere rulează, nu mai rămâne nimic de executat — astfel încât handler-ul tău poate încă interoga obiectele și înregistrările aplicației. Folosește-l pentru curățarea resurselor externe: deprovisionarea resurselor API, ștergerea boților rămași, revocarea webhook-urilor.
|
|
|
|
Notițe:
|
|
|
|
* Hook-ul funcționează după principiul „best-effort“: rulează sincron, dar o eroare este înregistrată și **nu blochează niciodată dezinstalarea** — curățarea nu trebuie să facă imposibilă eliminarea unei aplicații.
|
|
* Acesta primește `UninstallPayload` (`{ version?: string }` — versiunea care este eliminată).
|
|
* Nu rulează atunci când o instalare nouă eșuată este anulată — aplicația nu a terminat niciodată instalarea.
|
|
* Hook-ul nu poate rula după ce aplicația a dispărut, astfel încât curățarea externă care depinde de datele aplicației (de ex. ID-uri de boți stocate în înregistrări) trebuie făcută aici, nu într-un job programat extern.
|
|
* La fel ca hook-urile de instalare, **nu este executat în modul de dezvoltare (dev mode)** — declanșează-l manual în schimb:
|
|
|
|
```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,
|
|
});
|
|
```
|