Fix self host application (#18292)

- Fixes self host application
- add new telemetry information
- add serverId to identify a server instance
- remove .twenty from git tracking
- tree-shake "twenty-sdk" usage in built logic functions and front
components
- fix "twenty-sdk" version usage
- fix twenty-zapier cli

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
martmull
2026-03-02 12:06:05 +01:00
committed by GitHub
parent 1b67ba6a75
commit d021f7e369
48 changed files with 5809 additions and 404 deletions
@@ -24,10 +24,14 @@ export const copyYarnEngineAndBuildDependencies = async (
const { NODE_OPTIONS: _nodeOptions, ...cleanEnv } = process.env;
try {
await execFilePromise(process.execPath, [localYarnPath], {
cwd: buildDirectory,
env: cleanEnv,
});
await execFilePromise(
process.execPath,
[localYarnPath, 'workspaces', 'focus', '--all', '--production'],
{
cwd: buildDirectory,
env: cleanEnv,
},
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
const errorMessage =
@@ -44,6 +44,7 @@ import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/works
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
import { isWorkEmail } from 'src/utils/is-work-email';
import { TelemetryEventType } from 'src/engine/core-modules/telemetry/telemetry-event.type';
@Injectable()
// eslint-disable-next-line twenty/inject-workspace-repository
@@ -357,18 +358,19 @@ export class SignInUpService {
? await queryRunner.manager.save(UserEntity, userCreated)
: await this.userRepository.save(userCreated);
const serverUrl = this.twentyConfigService.get('SERVER_URL');
this.workspaceEventEmitter.emitCustomBatchEvent(
this.workspaceEventEmitter.emitCustomBatchEvent<TelemetryEventType>(
USER_SIGNUP_EVENT_NAME,
[
{
workspaceId: savedUser.currentWorkspace?.id,
userWorkspaceId: savedUser.currentUserWorkspace?.id,
userId: savedUser.id,
userEmail: newUserWithPicture.email,
userFirstName: newUserWithPicture.firstName,
userLastName: newUserWithPicture.lastName,
locale: newUserWithPicture.locale,
serverUrl,
serverUrl: this.twentyConfigService.get('SERVER_URL'),
serverId: this.twentyConfigService.get('SERVER_ID'),
},
],
undefined,
@@ -1,9 +1,6 @@
import { isDefined } from 'twenty-shared/utils';
import type {
DatabaseEventPayload,
ObjectRecordEvent,
} from 'twenty-shared/database-events';
import type { ObjectRecordEvent } from 'twenty-shared/database-events';
import { type LogicFunctionTriggerJobData } from 'src/engine/core-modules/logic-function/logic-function-trigger/jobs/logic-function-trigger.job';
import { type LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
@@ -34,7 +31,7 @@ export const transformEventBatchToEventPayloads = ({
});
for (const event of filteredEvents) {
const payload: DatabaseEventPayload = { ...batchEventInfo, ...event };
const payload = { ...batchEventInfo, ...event };
result.push({
logicFunctionId: logicFunction.id,
@@ -0,0 +1,11 @@
export type TelemetryEventType = {
workspaceId?: string;
userWorkspaceId?: string;
userId: string;
userEmail?: string;
userFirstName?: string;
userLastName?: string;
locale?: string;
serverUrl: string;
serverId: string;
};
@@ -2,12 +2,16 @@ import { Injectable } from '@nestjs/common';
import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { USER_SIGNUP_EVENT_NAME } from 'src/engine/api/graphql/workspace-query-runner/constants/user-signup-event-name.constants';
import { TelemetryEventType } from 'src/engine/core-modules/telemetry/telemetry-event.type';
type CreateEventInput = {
action: string;
payload: object;
type TelemetrySignUpEvent = {
action: typeof USER_SIGNUP_EVENT_NAME;
events: TelemetryEventType[];
};
type TelemetryEventPayload = TelemetrySignUpEvent;
@Injectable()
export class TelemetryService {
constructor(
@@ -15,32 +19,24 @@ export class TelemetryService {
private readonly secureHttpClientService: SecureHttpClientService,
) {}
async create(
createEventInput: CreateEventInput,
userId: string | null | undefined,
workspaceId: string | null | undefined,
) {
async publish(payload: TelemetryEventPayload) {
if (!this.twentyConfigService.get('TELEMETRY_ENABLED')) {
return { success: true };
}
const data = {
action: createEventInput.action,
timestamp: new Date().toISOString(),
version: '1',
payload: {
userId: userId,
workspaceId: workspaceId,
...createEventInput.payload,
},
};
try {
const httpClient = this.secureHttpClientService.getHttpClient({
baseURL: 'https://twenty-telemetry.com/api/v2',
});
await httpClient.post(`/selfHostingEvent`, data);
await Promise.all(
payload.events.map((event) =>
httpClient.post(`/selfHostingEvent`, {
action: payload.action,
...event,
}),
),
);
} catch {
return { success: false };
}
@@ -1023,6 +1023,16 @@ export class ConfigVariables {
@IsOptional()
SERVER_URL = 'http://localhost:3000';
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.SERVER_CONFIG,
description:
'Unique identifier for this server instance, generated as UUID v4 during database seeding',
type: ConfigVariableType.STRING,
isEnvOnly: true,
})
@IsOptional()
SERVER_ID: string;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.SERVER_CONFIG,
description: 'Base URL for public domains',