snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
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

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
---
title: Cele mai bune practici
---
Acest document prezintă cele mai bune practici pe care ar trebui să le urmați atunci când lucrați la backend.
## Urmați o abordare modulară
Backend-ul urmează o abordare modulară, care este un principiu fundamental atunci când lucrați cu NestJS. Asigurați-vă că vă împărțiți codul în module reutilizabile pentru a menține o bază de cod curată și organizată.
Fiecare modul ar trebui să encapsuleze o anumită funcționalitate sau caracteristică și să aibă un domeniu de aplicare bine definit. Această abordare modulară permite o clară separare a preocupărilor și elimină complexitățile inutile.
## Expuneți servicii pentru utilizare în module
Creați întotdeauna servicii care au o responsabilitate clară și unică, ceea ce îmbunătățește lizibilitatea și întreținerea codului. Denumirea serviciilor trebuie să fie descriptivă și consecventă.
De asemenea, ar trebui să expuneți serviciile pe care doriți să le utilizați în alte module. Expunerea serviciilor la alte module este posibilă prin sistemul de injecție de dependențe puternic al NestJS și promovează cuplarea slabă între componente.
## Evitați utilizarea tipului `any`
Când declarați o variabilă ca fiind `any`, verificatorul de tip al TypeScript nu realizează niciun control de tip, făcând posibilă atribuirea oricărui tip de valori variabilei. TypeScript utilizează inferența tipului pentru a determina tipul variabilei pe baza valorii. Declarându-l ca `any`, TypeScript nu mai poate deduce tipul. Acest lucru îngreunează depistarea erorilor de tip în timpul dezvoltării, ceea ce duce la erori la rulare și face codul mai greu de întreținut, mai puțin fiabil și mai greu de înțeles pentru ceilalți.
De aceea, totul ar trebui să aibă un tip. Astfel, dacă creați un nou obiect cu un prenume și un nume de familie, ar trebui să creați o interfață sau tip care să conțină un prenume și un nume de familie care să definească forma obiectului pe care îl manipulați.

View File

@@ -0,0 +1,39 @@
---
title: Obiecte personalizate
---
Obiectele sunt structuri care îți permit să stochezi date (înregistrări, atribute și valori) specifice organizației tale. Twenty oferă atât obiecte standard, cât și personalizate.
Obiectele standard sunt obiecte încorporate cu un set de atribute disponibile pentru toți utilizatorii. Exemple de obiecte standard în Twenty includ Companie și Persoană. Obiectele standard au câmpuri standard care sunt disponibile pentru toți utilizatorii Twenty, cum ar fi Company.displayName.
Obiectele personalizate sunt acelea pe care le poți crea pentru a stoca informații unice pentru organizația ta. Nu sunt încorporate; membrii workspace-ului tău pot crea și personaliza obiecte personalizate pentru a deține informații pentru care obiectele standard nu sunt adecvate.
## Schema la nivel înalt
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Schema la nivel înalt" />
</div>
<br />
## Cum funcționează
Obiectele personalizate provin din tabele de metadate care determină forma, numele și tipul obiectelor. Toate aceste informații sunt prezente în baza de date de schema de metadate, constând din tabele:
* **DataSource**: Detalii privind locul unde sunt prezente datele.
* **Object**: Descrie obiectul și îl leagă de un DataSource.
* **Field**: Prezintă câmpurile unui Obiect și le conectează la Obiect.
Pentru a adăuga un obiect personalizat, membrul workspace-ului va interoga API-ul /metadata. Aceasta actualizează metadatele în consecință și calculează o schemă GraphQL pe baza metadatelor, stocând-o în cache-ul GQL pentru utilizare ulterioară.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/add-custom-objects.jpeg" alt="Interoghează API-ul /metadata pentru a adăuga obiecte personalizate" />
</div>
<br />
Pentru a prelua datele, procesul implică realizarea de interogări prin endpoint-ul /graphql și transmiterea lor prin Query Resolver.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Interoghează endpoint-ul /graphql pentru a prelua date" />
</div>

View File

@@ -0,0 +1,46 @@
---
title: Steaguri de caracteristici
---
Steagurile de caracteristici sunt utilizate pentru a ascunde caracteristici experimentale. Pentru Twenty, acestea sunt setate la nivel de spațiu de lucru și nu la nivel de utilizator.
## Adăugarea unui nou steag de caracteristici
În `FeatureFlagKey.ts` adăugați steagul de caracteristici:
```ts
type FeatureFlagKey =
| 'IS_FEATURENAME_ENABLED'
| ...;
```
Adăugați-l și în enumul din `feature-flag.entity.ts`:
```ts
enum FeatureFlagKeys {
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
...
}
```
Pentru a aplica un steag de caracteristici la o funcționalitate de **backend**, utilizați:
```ts
@Gate({
featureFlag: 'IS_FEATURENAME_ENABLED',
})
```
Pentru a aplica un steag de caracteristici la o funcționalitate de **frontend**, utilizați:
```ts
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
```
## Configurați steagurile de caracteristici pentru implementare
Modificați înregistrarea corespunzătoare în tabelul `core.featureFlag`:
| id | cheie | IdSpațiuDeLucru | valoare |
| --------- | ------------------------ | --------------- | ---------- |
| Aleatoriu | `IS_FEATURENAME_ENABLED` | IdSpațiuDeLucru | `adevărat` |

View File

@@ -0,0 +1,125 @@
---
title: Arhitectura Folderului
info: O privire detaliată asupra arhitecturii folderului serverului nostru
---
Structura directorului backend este următoarea:
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
```
## Abilitate
Definește permisiunile și include gestionari pentru fiecare entitate.
## Decoratori
Definește decoratori personalizați în NestJS pentru funcționalitate adăugată.
Vezi [decoratori personalizați](https://docs.nestjs.com/custom-decorators) pentru mai multe detalii.
## Filtre
Include filtre de excepție pentru a gestiona excepțiile care ar putea apărea în punctele finale GraphQL.
## Paznici
Vezi [paznici](https://docs.nestjs.com/guards) pentru mai multe detalii.
## Sănătate
Include o API REST disponibilă public (healthz) care returnează un JSON pentru a verifica dacă baza de date funcționează așa cum este de așteptat.
## Metadate
Definește obiecte personalizate și face disponibilă o API GraphQL (graphql/metadata).
## Spațiu de lucru
Generează și servește o schemă GraphQL personalizată pe baza metadatelor.
### Structura Directorului de Spațiu de Lucru
```
workspace
└───workspace-schema-builder
└───factories
└───graphql-types
└───database
└───interfaces
└───object-definitions
└───services
└───storage
└───utils
└───workspace-resolver-builder
└───factories
└───interfaces
└───workspace-query-builder
└───factories
└───interfaces
└───workspace-query-runner
└───interfaces
└───utils
└───workspace-datasource
└───workspace-manager
└───workspace-migration-runner
└───utils
└───workspace.module.ts
└───workspace.factory.spec.ts
└───workspace.factory.ts
```
Rădăcina directorului workspace include `workspace.factory.ts`, un fișier care conține funcția `createGraphQLSchema`. Această funcție generează schema specifică spațiului de lucru folosind metadatele pentru a personaliza o schemă pentru spațiile de lucru individuale. Prin separarea construcției de schema și rezolvatori, folosim funcția `makeExecutableSchema`, care combină aceste elemente discrete.
Această strategie nu este doar despre organizare, ci ajută și la optimizare, cum ar fi caching-ul definițiilor de tip generate pentru a îmbunătăți performanța și scalabilitatea.
### Constructor Schema Spațiu de Lucru
Generează schema GraphQL și include:
#### Fabrici:
Constructori specializați pentru a genera construcții legate de GraphQL.
* Tipul.fabrică traduce metadatele câmpului în tipuri GraphQL folosind `TypeMapperService`.
* Tipul-definiție.fabrică creează obiecte GraphQL de intrare sau ieșire derivate din `objectMetadata`.
#### Tipuri GraphQL
Include enumerări, inputuri, obiecte și scalari și servește ca blocuri de construcție pentru construcția schema.
#### Interfețe și Definiții de Obiect
Conține planurile pentru entitățile GraphQL și include atât tipuri predefinite, cât și personalizate, precum `MONEY` sau `URL`.
#### Servicii
Conține serviciul responsabil pentru asocierea FieldMetadataType cu scalarul GraphQL corespunzător sau modificatorii de interogare.
#### Stocare
Include clasa `TypeDefinitionsStorage` care conține definiții de tip reutilizabile, prevenind duplicarea tipurilor GraphQL.
### Constructor Rezolvator Spațiu de Lucru
Creează funcții de rezolvare pentru interogarea și modificarea schemei GraphQL.
Fiecare fabrică din acest director este responsabilă pentru producerea unui tip de rezolvator distinct, cum ar fi `FindManyResolverFactory`, proiectată pentru aplicare adaptabilă în diverse tabele.
### Executor Întrebări Spațiu de Lucru
Rulează interogările generate pe baza de date și parcurge rezultatul.

View File

@@ -0,0 +1,41 @@
---
title: Coada de mesaje
---
Cozile facilitează desfășurarea operațiunilor asincrone. Acestea pot fi folosite pentru a efectua sarcini de fundal precum trimiterea unui e-mail de bun venit la înregistrare.
Fiecare caz de utilizare va avea propria clasă de coadă extinsă din `MessageQueueServiceBase`.
În prezent, acceptăm doar `bull-mq`[bull-mq](https://bullmq.io/) ca driver de coadă.
## Pași pentru a crea și utiliza o coadă nouă
1. Adăugați un nume pentru noua coadă sub enum `MESSAGE_QUEUES`.
2. Furnizează implementarea de tip factory a cozii, folosind numele cozii ca token de dependență.
3. Injectează coada pe care ai creat-o în modulul/serviciul necesar, folosind numele cozii ca token de dependență.
4. Adăugați o clasă lucrător cu injectare bazată pe simbol, la fel ca producătorul.
### Exemplu de utilizare
```ts
class Resolver {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {}
async onSomeAction() {
//business logic
await this.queue.add(someData);
}
}
//async worker
class CustomWorker {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {
this.initWorker();
}
async initWorker() {
await this.queue.work(async ({ id, data }) => {
//worker logic
});
}
}
```

View File

@@ -0,0 +1,87 @@
---
title: Comenzi Backend
icon: terminal
---
## Comenzi utile
Aceste comenzi ar trebui să fie executate din dosarul packages/twenty-server.
Din orice alt dosar poți rula `npx nx {command} twenty-server` (sau `npx nx run twenty-server:{command}`).
### Configurare de la prima utilizare
```
npx nx database:reset twenty-server # setup the database with dev seeds
```
### Pornirea serverului
```
npx nx run twenty-server:start
```
### Lint
```
npx nx run twenty-server:lint # pass --fix to fix lint errors
```
### Test
```
npx nx run twenty-server:test:unit # run unit tests
npx nx run twenty-server:test:integration # run integration tests
```
Notă: poți rula `npx nx run twenty-server:test:integration:with-db-reset` în cazul în care trebuie să resetezi baza de date înainte de a rula testele de integrare.
### Resetarea bazei de date
Dacă vrei să resetezi și să configurezi baza de date, poți rula comanda următoare:
```bash
npx nx run twenty-server:database:reset
```
### Migrații
#### Pentru obiectele din schematizările de Bază/Metadate (TypeORM)
```bash
npx nx run twenty-server:database:migrate:generate
```
## Tehnologii Utilizate
Twenty folosește în principal NestJS pentru backend.
Prisma a fost primul ORM pe care l-am folosit. Dar pentru a permite utilizatorilor să creeze câmpuri și obiecte personalizate, un nivel inferior a făcut mai mult sens, deoarece trebuie să avem un control detaliat. Proiectul folosește acum TypeORM.
Iată cum arată acum stack-ul tehnologic.
**Nucleu**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
**Bază de date**
* [Postgres](https://www.postgresql.org/)
**Integrări terțe**
* [Sentry](https://sentry.io/welcome/) pentru urmărirea erorilor
**Testare**
* [Jest](https://jestjs.io/)
**Instrumente**
* [Yarn](https://yarnpkg.com/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
**Dezvoltare**
* [AWS EKS](https://aws.amazon.com/eks/)

View File

@@ -0,0 +1,85 @@
---
title: Aplicația Zapier
---
Sincronizează fără efort Twenty cu peste 3000 de aplicații folosind [Zapier](https://zapier.com/). Automatizează sarcinile, stimulează productivitatea și îmbunătățește relațiile cu clienții!
## Despre Zapier
Zapier este un instrument care îți permite să automatizezi fluxurile de lucru conectând aplicațiile pe care echipa ta le folosește zilnic. Conceptul de bază al Zapier este fluxurile de automatizare, numite Zaps, care includ triggeri și acțiuni.
Poți afla mai multe despre cum funcționează Zapier [aici](https://zapier.com/how-it-works).
## Configurare
### Pasul 1: Instalează pachetele Zapier
```bash
cd packages/twenty-zapier
yarn
```
### Pasul 2: Autentificare cu CLI-ul
Folosește acreditările Zapier pentru a te autentifica folosind CLI-ul:
```bash
zapier login
```
### Pasul 3: Setează variabilele de mediu
Din folderul `packages/twenty-zapier`, rulează:
```bash
cp .env.example .env
```
Rulează aplicația local, accesează [http://localhost:3000/settings/api-webhooks](http://localhost:3000/settings/api-webhooks) și generează o cheie API.
Înlocuiește valoarea **YOUR_API_KEY** din fișierul `.env` cu cheia API pe care tocmai ai generat-o.
## Dezvoltare
<Warning>
Asigură-te că rulezi `yarn build` înainte de orice comandă `zapier`.
</Warning>
### Test
```bash
yarn test
```
### Lint
```bash
yarn format
```
### Urmărește și compilează pe măsură ce editezi codul
```bash
yarn watch
```
### Validează aplicația Zapier
```bash
yarn validate
```
### Desfășoară aplicația Zapier
```bash
yarn deploy
```
### Listează toate comenzile CLI Zapier
```bash
zapier
```