2b3b2362db
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
193 lines
12 KiB
Plaintext
193 lines
12 KiB
Plaintext
---
|
|
title: الاتصالات
|
|
description: اسمح لتطبيقك بالتصرف نيابةً عن المستخدم في خدمات الجهات الخارجية عبر OAuth.
|
|
icon: plug
|
|
---
|
|
|
|
الاتصالات هي بيانات اعتماد يحتفظ بها المستخدم لخدمة خارجية (Linear وGitHub وSlack، ...). يحدّد تطبيقك **كيف** يتم الحصول على تلك بيانات الاعتماد — **موفّر اتصال** — ويستخدمها وقت التشغيل لإجراء استدعاءات مُصادَقة إلى واجهة برمجة تطبيقات الطرف الثالث.
|
|
|
|
حاليًا لا يُدعَم سوى OAuth 2.0. ستندمج الأنواع المستقبلية من بيانات الاعتماد (رموز الوصول الشخصية، مفاتيح API، المصادقة الأساسية) مع نفس الواجهة — التطبيقات التي تستخدم بالفعل `defineConnectionProvider({ type: 'oauth', ... })` لن تحتاج إلى الترحيل.
|
|
|
|
<AccordionGroup>
|
|
|
|
<Accordion title="defineConnectionProvider" description="عرّف كيفية الحصول على اتصالات تطبيقك">
|
|
|
|
يصف موفّر الاتصال عملية المصافحة الخاصة بـ OAuth التي يحتاجها تطبيقك. ينقر المستخدم على "إضافة اتصال" في إعدادات تطبيقك، ويُكمل شاشة موافقة المزوّد، ثم يتم إنشاء صف `ConnectedAccount` في مساحة عمله.
|
|
|
|
يتطلّب الإعداد العملي **ملفّين** — موفّر الاتصال، وتصريح `serverVariables` مطابق في `defineApplication` يحتفظ ببيانات اعتماد عميل OAuth.
|
|
|
|
```ts src/connection-providers/linear-connection.ts
|
|
import { defineConnectionProvider } from 'twenty-sdk/define';
|
|
|
|
export default defineConnectionProvider({
|
|
universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f',
|
|
name: 'linear',
|
|
displayName: 'Linear',
|
|
icon: 'IconBrandLinear',
|
|
type: 'oauth',
|
|
oauth: {
|
|
authorizationEndpoint: 'https://linear.app/oauth/authorize',
|
|
tokenEndpoint: 'https://api.linear.app/oauth/token',
|
|
scopes: ['read', 'write'],
|
|
// These must match keys in `defineApplication.serverVariables` below.
|
|
clientIdVariable: 'LINEAR_CLIENT_ID',
|
|
clientSecretVariable: 'LINEAR_CLIENT_SECRET',
|
|
// Optional: defaults to 'json'. Some providers (Linear, Slack) want
|
|
// 'form-urlencoded' for the token request.
|
|
tokenRequestContentType: 'form-urlencoded',
|
|
// Optional: defaults to true. Disable only if the provider rejects PKCE.
|
|
usePkce: false,
|
|
// Optional: extra query params on the authorize URL.
|
|
// authorizationParams: { prompt: 'consent' },
|
|
// Optional: provider's RFC 7009 token revocation endpoint, called on disconnect.
|
|
// revokeEndpoint: 'https://example.com/oauth/revoke',
|
|
},
|
|
});
|
|
```
|
|
|
|
```ts src/application.config.ts
|
|
import { defineApplication } from 'twenty-sdk/define';
|
|
|
|
export default defineApplication({
|
|
universalIdentifier: '...',
|
|
displayName: 'Linear',
|
|
description: 'Connect Linear to Twenty.',
|
|
// OAuth client credentials live on the app registration (one OAuth app per
|
|
// Twenty server, configured by the admin) — not per-workspace. Declare them
|
|
// as serverVariables so the admin can fill them in once for all installs.
|
|
serverVariables: {
|
|
LINEAR_CLIENT_ID: {
|
|
description: 'OAuth client ID from your Linear OAuth application.',
|
|
isSecret: false,
|
|
isRequired: true,
|
|
},
|
|
LINEAR_CLIENT_SECRET: {
|
|
description: 'OAuth client secret from your Linear OAuth application.',
|
|
isSecret: true,
|
|
isRequired: true,
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
النقاط الرئيسية:
|
|
|
|
* `name` هي سلسلة المعرّف الفريدة المستخدمة في `listConnections({ providerName })` (بصيغة kebab-case، ويجب أن تطابق `^[a-z][a-z0-9-]*$`).
|
|
* `displayName` يظهر في علامة تبويب إعدادات كل تطبيق وفي قائمة أدوات الذكاء الاصطناعي.
|
|
* `clientIdVariable` / `clientSecretVariable` هي **أسماء**، وليست قيماً — ويجب أن تطابق المفاتيح المصرَّح بها في `defineApplication.serverVariables`. يُدخِل مسؤول الخادم القيم الفعلية `client_id` و`client_secret` عبر واجهة تسجيل التطبيق، ولا تُضمَّن أبدًا في مستودعك.
|
|
* استخدم `serverVariables` (وليس `applicationVariables`) — بيانات اعتماد OAuth على مستوى الخادم، ويوجد تطبيق OAuth واحد لكل خادم Twenty.
|
|
* إلى أن يتم ملء كلا `serverVariables`، تعرض علامة تبويب إعدادات كل تطبيق تلميح "بحاجة إلى مسؤول الخادم" ويكون زر "إضافة اتصال" معطّلًا.
|
|
* `type: 'oauth'` هي القيمة الوحيدة المدعومة حاليًا. المميِّز متوافق مع الإصدارات المستقبلية: الأنواع المستقبلية (`'pat'`، `'api-key'`، ...) ستضيف كُتل تهيئة فرعية جديدة إلى جانب `oauth`.
|
|
|
|
عنوان URL لردّ النداء الخاص بـ OAuth الذي يحتاج موفّرك إلى إضافته إلى قائمة السماح هو:
|
|
|
|
```
|
|
https://<your-twenty-server>/auth/apps/callback
|
|
```
|
|
|
|
</Accordion>
|
|
|
|
<Accordion title="listConnections / getConnection" description="استخدم الاتصالات من دالة منطقية">
|
|
|
|
داخل معالج دالة منطقية، تُرجِع `listConnections({ providerName })` صفوف `ConnectedAccount` الخاصة بهذا التطبيق للمزوّد المحدَّد، مع رموز وصول محدَّثة.
|
|
|
|
```ts src/logic-functions/handlers/create-linear-issue-handler.ts
|
|
import { listConnections } from 'twenty-sdk/logic-function';
|
|
|
|
export const createLinearIssueHandler = async (input: {
|
|
teamId?: string;
|
|
title?: string;
|
|
}) => {
|
|
if (!input.teamId || !input.title) {
|
|
return { success: false, error: 'teamId and title are required' };
|
|
}
|
|
|
|
const connections = await listConnections({ providerName: 'linear' });
|
|
|
|
// Workspace-shared credentials win when present; fall back to the first
|
|
// user-visibility one. For HTTP-route triggers you typically pick the
|
|
// request user's connection via event.userWorkspaceId instead.
|
|
const connection =
|
|
connections.find((c) => c.visibility === 'workspace') ?? connections[0];
|
|
|
|
if (!connection) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
'Linear is not connected. Open the app settings and click "Add connection".',
|
|
};
|
|
}
|
|
|
|
// Use connection.accessToken to call the third-party API.
|
|
const response = await fetch('https://api.linear.app/graphql', {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${connection.accessToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`,
|
|
}),
|
|
});
|
|
|
|
return { success: response.ok };
|
|
};
|
|
```
|
|
|
|
يحتوي كل اتصال على:
|
|
|
|
| الحقل | الوصف |
|
|
| ----------------- | ------------------------------------------------------------------------------------------------- |
|
|
| `id` | معرّف صف فريد؛ مرّره إلى `getConnection(id)` لإعادة جلب واحد فقط |
|
|
| `visibility` | `'user'` (خاص بعضو واحد في مساحة العمل) أو `'workspace'` (مشترك مع جميع الأعضاء) |
|
|
| `scopes` | أذونات OAuth الممنوحة من قِبل المزوّد الأصلي (مختلفة عن `visibility` — ولا علاقة لها به) |
|
|
| `userWorkspaceId` | معرّف userWorkspace للمالك — مفيد لاختيار "اتصال مستخدم الطلب" في مشغّلات مسارات HTTP |
|
|
| `accessToken` | رمز وصول OAuth حديث (يُحدَّث تلقائيًا إذا انتهت صلاحيته) |
|
|
| `name` / `handle` | الاسم المعروض للاتصال (يُستمد تلقائيًا عند ردّ نداء OAuth، وقابل لإعادة التسمية من قِبل المستخدم) |
|
|
| `authFailedAt` | يُضبط عند فشل أحدث عملية تحديث؛ يجب على المستخدم إعادة الاتصال |
|
|
|
|
النقاط الرئيسية:
|
|
|
|
* مرّر `{ providerName }` للتصفية حسب المزوّد؛ واحذفه للحصول على كل الاتصالات التي يملكها هذا التطبيق عبر جميع المزوّدين.
|
|
* يقوم الخادم بتحديث رمز الوصول بشفافية قبل الإرجاع. يرى معالجك دائمًا رمزًا صالحًا للاستخدام (أو سيكون `authFailedAt` مُعيّنًا).
|
|
* `getConnection(id)` هي المعادِل لصف واحد.
|
|
|
|
</Accordion>
|
|
|
|
<Accordion title="رؤية خاصة بالمستخدم مقابل المشاركة على مستوى مساحة العمل" description="كيف يختار المستخدمون بين بيانات اعتماد خاصة ومشتركة">
|
|
|
|
عند نقر المستخدم "إضافة اتصال"، سيُطلب منه اختيار مستوى الرؤية:
|
|
|
|
* **لي فقط** — بيانات الاعتماد خاصة بالمستخدم الذي قام بالاتصال. ستتمكّن أي دالة منطقية تُستدعى بالنيابة عنه (مشغّل مسار HTTP مع `isAuthRequired: true`) من رؤيتها؛ أمّا مشغّلات cron وأحداث قاعدة البيانات فلا.
|
|
* **مشتركة على مستوى مساحة العمل** — يمكن لأي عضو في مساحة العمل استخدام بيانات الاعتماد. يمكن لمشغّلات cron/قاعدة البيانات رؤيتها أيضًا، لأنها لا تملك مستخدم طلب.
|
|
|
|
استخدم الخيار المناسب لكل معالج:
|
|
|
|
```ts
|
|
// HTTP-route trigger — prefer the request user's own connection.
|
|
const conn =
|
|
connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ??
|
|
connections.find((c) => c.visibility === 'workspace');
|
|
|
|
// Cron trigger — no request user; only shared credentials are sensible.
|
|
const conn = connections.find((c) => c.visibility === 'workspace');
|
|
```
|
|
|
|
يُسمح بوجود اتصالات متعددة لكل (مستخدم، مزوّد)، لذا يمكن للمستخدم نفسه امتلاك "Linear شخصي" و"Linear للعمل" جنبًا إلى جنب.
|
|
|
|
</Accordion>
|
|
|
|
<Accordion title="إعداد المزوّد لمرة واحدة" description="سجّل تطبيق OAuth الخاص بك لدى خدمة الطرف الثالث">
|
|
|
|
بالنسبة لكل موفّر اتصال، يحتاج مسؤول الخادم أولًا إلى تسجيل تطبيق OAuth لدى الطرف الثالث.
|
|
|
|
1. انتقل إلى إعدادات المطوّر لدى المزوّد (مثل https://linear.app/settings/api/applications/new).
|
|
2. عيّن **Redirect URI** إلى `\<SERVER_URL>/auth/apps/callback`.
|
|
3. انسخ **Client ID** و**Client Secret** المُنشأين.
|
|
4. افتح التطبيق المُثبَّت في Twenty كمسؤول خادم → عيّن القيم على `serverVariables` المقابلة.
|
|
5. بعد ذلك، يمكن لأعضاء مساحة العمل إضافة الاتصالات من قسم **الاتصالات** الخاص بكل تطبيق.
|
|
|
|
</Accordion>
|
|
|
|
</AccordionGroup>
|