Files
twenty/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx
T
github-actions[bot] ae2c8978d1 i18n - docs translations (#23258)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-07-24 13:12:20 +02:00

189 lines
16 KiB
Plaintext

---
title: خطافات التثبيت
description: تشغيل المنطق أثناء دورة حياة التثبيت أو الترقية أو إلغاء التثبيت — مثل تعبئة البيانات الأوّلية، ونسخ السجلات احتياطيًا، والتحقق من الترقية، وتنظيف الموارد الخارجية.
icon: wrench
---
خطافات التثبيت هي دوال منطقية خاصة تعمل أثناء دورة حياة التثبيت أو الترقية أو إلغاء التثبيت. تستخدم نفس وقت تشغيل المعالج مثل [دوال المنطق](/l/ar/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. الجأ إلى ما قبل التثبيت فقط عندما يكون الترحيل نفسه هدّامًا وتحتاج إلى التقاط الحالة السابقة قبل أن تزول.
| ترغب في... | استخدام |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
| بذر البيانات، تهيئة مساحة العمل، تسجيل موارد خارجية | `post-install` |
| عمل طويل الأمد لا ينبغي أن يحجب استجابة التثبيت | `post-install` (الوضع غير المتزامن الافتراضي، مع محاولات إعادة من العامل) |
| إعداد سريع يعتمد عليه المستدعي مباشرةً بعد عودة التثبيت | `post-install` مع `shouldRunSynchronously: true` |
| قراءة البيانات أو نسخها احتياطيًا والتي قد يفقدها الترحيل القادم | `pre-install` |
| رفض ترقية قد تُفسد البيانات الحالية | `pre-install` (ارمِ من المعالج) |
| تنفيذ مواءمة في كل ترقية | أي من الخطافين مع `shouldRunOnVersionUpgrade: true` |
## السلوك المشترك بين كلا الخطافين
* إعداد التهيئة هو إعداد `defineLogicFunction` نفسه مطروحًا منه إعدادات المشغّل، مضافًا إليه `shouldRunOnVersionUpgrade`.
* **موعد تشغيله**: في عمليات التثبيت الجديدة فقط، افتراضيًا. عيِّن `shouldRunOnVersionUpgrade: true` لتشغيله أيضًا عند الترقيات. استخدم `previousVersion` / `newVersion` للتفرع حسب مسار الترقية.
* **أهمية اللاّتغيّر (Idempotency)**: قد يُعاد تشغيل post-install غير المتزامن، وأيٌّ من الخطافين يُعاد تشغيله عند الترقيات عندما يكون `shouldRunOnVersionUpgrade` مفعّلًا.
* يتم حقن بيئة دوال المنطق المعتادة (`APPLICATION_ID`، و`APP_ACCESS_TOKEN`، و`API_URL`)، لذا يمكنك استدعاء Twenty API باستخدام رمز التطبيق الخاص بك.
* يُربَط الخطّاف تلقائيًا بملف بيان التطبيق وقت الإنشاء (`preInstallLogicFunction` / `postInstallLogicFunction`) — لا حاجة للإشارة إليه في [`defineApplication()`](/l/ar/developers/extend/apps/config/application).
* القيمة الافتراضية لـ `timeoutSeconds` هي 300 للسماح بمهام إعداد أطول مثل بذر البيانات.
* **غير منفَّذ في نمط التطوير**: يتخطى `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`) ويُشغِّله عامل. تعود استجابة التثبيت بمجرد وضع المهمة في قائمة الانتظار. **يُستخدم للأعمال طويلة الأمد** — بذر مجموعات بيانات كبيرة، وواجهات برمجة تطبيقات بطيئة لأطراف ثالثة.
* `true` — يُنفَّذ مضمَّنًا أثناء تدفق التثبيت. يحجب طلب التثبيت حتى ينتهي المعالج؛ يظهر الخطأ الذي يتم رميه كـ `POST_INSTALL_ERROR` للمستدعي (بدون محاولات إعادة). **يُستخدم للأعمال السريعة التي يجب إتمامها قبل الاستجابة.** تم تطبيق الترحيل بالفعل في هذه المرحلة، لذا لا يؤدي الفشل إلى التراجع عن تغييرات المخطط — بل يُظهِر الخطأ فقط.
</Accordion>
<Accordion title="definePreInstallLogicFunction" description="تعمل قبل تطبيق ترحيل البيانات الوصفية لمساحة العمل">
يعمل قبل ترحيل البيانات الوصفية، مقابل المخطط **السابق** — المكان المناسب لنسخ البيانات احتياطيًا التي قد يفقدها الترحيل، أو لرفض ترقية خطِرة. قبل التنفيذ، يُشغِّل الخادم مزامنة ذات طابع إضافي فقط "pared-down sync" تُسجِّل دالة ما قبل التثبيت للإصدار الجديد فقط؛ أما كل ما عدا ذلك — كائنات الإصدار السابق وحقوله وبياناته — فيبقى دون مساس عندما يعمل المعالج.
ما قبل التثبيت دائمًا **متزامن** ويحجب عملية التثبيت. إذا رمى المعالج استثناءً، تُلغى عملية التثبيت قبل أي تغيير في المخطط — وتبقى مساحة العمل على الإصدار السابق بحالة متّسقة. هذا مقصود: ما قبل التثبيت هو فرصتك الأخيرة لرفض ترقية تنطوي على مخاطر.
مثال — نسخ قيم حقل قديم قبل أن يُسقِطه الترحيل:
```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 }` — الإصدار الذي تتم إزالته).
* لا يتم تشغيله عند التراجع عن عملية تثبيت جديدة فاشلة — حيث لم يكتمل تثبيت التطبيق مطلقًا.
* لا يمكن للخطاف أن يعمل بعد إزالة التطبيق، لذلك يجب أن يتم هنا أي تنظيف خارجي يعتمد على بيانات التطبيق (مثلًا: معرّفات الروبوتات المخزنة في السجلات)، وليس في مهمة مجدولة خارجية.
* ومثل خطافات التثبيت، **لا يتم تنفيذه في وضع التطوير (dev mode)** — بدلًا من ذلك، قم بتشغيله يدويًا:
```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,
});
```