diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 8a1f9d38eb1..9809608d27d 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1458,7 +1458,7 @@ export type EventLogRecord = { properties?: Maybe; recordId?: Maybe; timestamp: Scalars['DateTime']; - userWorkspaceId?: Maybe; + userId?: Maybe; }; export enum EventLogTable { @@ -6465,7 +6465,7 @@ export type UpdateWorkspaceMemberRoleMutationVariables = Exact<{ }>; -export type UpdateWorkspaceMemberRoleMutation = { __typename?: 'Mutation', updateWorkspaceMemberRole: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, roles?: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean }> | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } }; +export type UpdateWorkspaceMemberRoleMutation = { __typename?: 'Mutation', updateWorkspaceMemberRole: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, userWorkspaceId?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, roles?: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean }> | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } }; export type UpsertFieldPermissionsMutationVariables = Exact<{ upsertFieldPermissionsInput: UpsertFieldPermissionsInput; @@ -6498,7 +6498,7 @@ export type UpsertRowLevelPermissionPredicatesMutation = { __typename?: 'Mutatio export type GetRolesQueryVariables = Exact<{ [key: string]: never; }>; -export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string }>, apiKeys: Array<{ __typename?: 'ApiKeyForRole', id: string, name: string, expiresAt: string, revokedAt?: string | null }>, permissionFlags?: Array<{ __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> | null, fieldPermissions?: Array<{ __typename?: 'FieldPermission', objectMetadataId: string, fieldMetadataId: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null, id: string, roleId: string }> | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> }; +export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, userWorkspaceId?: string | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string }>, apiKeys: Array<{ __typename?: 'ApiKeyForRole', id: string, name: string, expiresAt: string, revokedAt?: string | null }>, permissionFlags?: Array<{ __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> | null, fieldPermissions?: Array<{ __typename?: 'FieldPermission', objectMetadataId: string, fieldMetadataId: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null, id: string, roleId: string }> | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> }; export type CreateApprovedAccessDomainMutationVariables = Exact<{ input: CreateApprovedAccessDomainInput; @@ -6570,7 +6570,7 @@ export type BillingSubscriptionFragmentFragment = { __typename?: 'BillingSubscri export type CurrentBillingSubscriptionFragmentFragment = { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, hasPassword: boolean, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', id: string, permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, isGoogleAuthBypassEnabled: boolean, isMicrosoftAuthBypassEnabled: boolean, isPasswordAuthBypassEnabled: boolean, subdomain: string, customDomain?: string | null, hasValidEnterpriseKey: boolean, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, fastModel: string, smartModel: string, aiAdditionalInstructions?: string | null, autoEnableNewAiModels: boolean, disabledAiModelIds?: Array | null, enabledAiModelIds?: Array | null, useRecommendedModels: boolean, isTwoFactorAuthenticationEnforced: boolean, trashRetentionDays: number, eventLogRetentionDays: number, editableProfileFields?: Array | null, workspaceCustomApplication?: { __typename?: 'Application', id: string } | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, billingEntitlements: Array<{ __typename?: 'BillingEntitlement', key: BillingEntitlementKey, value: boolean }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, hasPassword: boolean, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, userWorkspaceId?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, userWorkspaceId?: string | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', id: string, permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, isGoogleAuthBypassEnabled: boolean, isMicrosoftAuthBypassEnabled: boolean, isPasswordAuthBypassEnabled: boolean, subdomain: string, customDomain?: string | null, hasValidEnterpriseKey: boolean, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, fastModel: string, smartModel: string, aiAdditionalInstructions?: string | null, autoEnableNewAiModels: boolean, disabledAiModelIds?: Array | null, enabledAiModelIds?: Array | null, useRecommendedModels: boolean, isTwoFactorAuthenticationEnforced: boolean, trashRetentionDays: number, eventLogRetentionDays: number, editableProfileFields?: Array | null, workspaceCustomApplication?: { __typename?: 'Application', id: string } | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, billingEntitlements: Array<{ __typename?: 'BillingEntitlement', key: BillingEntitlementKey, value: boolean }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } }; export type WorkspaceUrlsFragmentFragment = { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }; @@ -6589,7 +6589,7 @@ export type DeleteUserWorkspaceMutation = { __typename?: 'Mutation', deleteUserF export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; -export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, hasPassword: boolean, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', id: string, permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, isGoogleAuthBypassEnabled: boolean, isMicrosoftAuthBypassEnabled: boolean, isPasswordAuthBypassEnabled: boolean, subdomain: string, customDomain?: string | null, hasValidEnterpriseKey: boolean, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, fastModel: string, smartModel: string, aiAdditionalInstructions?: string | null, autoEnableNewAiModels: boolean, disabledAiModelIds?: Array | null, enabledAiModelIds?: Array | null, useRecommendedModels: boolean, isTwoFactorAuthenticationEnforced: boolean, trashRetentionDays: number, eventLogRetentionDays: number, editableProfileFields?: Array | null, workspaceCustomApplication?: { __typename?: 'Application', id: string } | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, billingEntitlements: Array<{ __typename?: 'BillingEntitlement', key: BillingEntitlementKey, value: boolean }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, hasPassword: boolean, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, userWorkspaceId?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, userWorkspaceId?: string | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', id: string, permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null, rowLevelPermissionPredicates?: Array<{ __typename?: 'RowLevelPermissionPredicate', id: string, fieldMetadataId: string, objectMetadataId: string, operand: RowLevelPermissionPredicateOperand, subFieldName?: string | null, workspaceMemberFieldMetadataId?: string | null, workspaceMemberSubFieldName?: string | null, rowLevelPermissionPredicateGroupId?: string | null, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, value?: any | null }> | null, rowLevelPermissionPredicateGroups?: Array<{ __typename?: 'RowLevelPermissionPredicateGroup', id: string, parentRowLevelPermissionPredicateGroupId?: string | null, logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator, positionInRowLevelPermissionPredicateGroup?: number | null, roleId: string, objectMetadataId: string }> | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, isGoogleAuthBypassEnabled: boolean, isMicrosoftAuthBypassEnabled: boolean, isPasswordAuthBypassEnabled: boolean, subdomain: string, customDomain?: string | null, hasValidEnterpriseKey: boolean, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, fastModel: string, smartModel: string, aiAdditionalInstructions?: string | null, autoEnableNewAiModels: boolean, disabledAiModelIds?: Array | null, enabledAiModelIds?: Array | null, useRecommendedModels: boolean, isTwoFactorAuthenticationEnforced: boolean, trashRetentionDays: number, eventLogRetentionDays: number, editableProfileFields?: Array | null, workspaceCustomApplication?: { __typename?: 'Application', id: string } | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, billingEntitlements: Array<{ __typename?: 'BillingEntitlement', key: BillingEntitlementKey, value: boolean }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } }; export type ViewFieldFragmentFragment = { __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }; @@ -6945,9 +6945,9 @@ export type GetWorkspaceInvitationsQuery = { __typename?: 'Query', findWorkspace export type DeletedWorkspaceMemberQueryFragmentFragment = { __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }; -export type PartialWorkspaceMemberQueryFragmentFragment = { __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }; +export type PartialWorkspaceMemberQueryFragmentFragment = { __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, userWorkspaceId?: string | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }; -export type WorkspaceMemberQueryFragmentFragment = { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }; +export type WorkspaceMemberQueryFragmentFragment = { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, userWorkspaceId?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }; export type ActivateWorkspaceMutationVariables = Exact<{ input: ActivateWorkspaceInput; @@ -7642,6 +7642,7 @@ export const WorkspaceMemberQueryFragmentFragmentDoc = gql` avatarUrl locale userEmail + userWorkspaceId timeZone dateFormat timeFormat @@ -7658,6 +7659,7 @@ export const PartialWorkspaceMemberQueryFragmentFragmentDoc = gql` } avatarUrl userEmail + userWorkspaceId } `; export const DeletedWorkspaceMemberQueryFragmentFragmentDoc = gql` diff --git a/packages/twenty-front/src/modules/settings/roles/types/RoleWithPartialMembers.ts b/packages/twenty-front/src/modules/settings/roles/types/RoleWithPartialMembers.ts index b1eabe0eb20..4fd759c907a 100644 --- a/packages/twenty-front/src/modules/settings/roles/types/RoleWithPartialMembers.ts +++ b/packages/twenty-front/src/modules/settings/roles/types/RoleWithPartialMembers.ts @@ -10,7 +10,6 @@ export type PartialWorkspaceMember = Omit< | 'calendarStartDay' | 'createdAt' | 'updatedAt' - | 'userId' >; export type RoleWithPartialMembers = Omit & { diff --git a/packages/twenty-front/src/modules/workspace-member/graphql/fragments/partialWorkspaceMemberQueryFragment.ts b/packages/twenty-front/src/modules/workspace-member/graphql/fragments/partialWorkspaceMemberQueryFragment.ts index 7ad9061b4e9..9caf70842d8 100644 --- a/packages/twenty-front/src/modules/workspace-member/graphql/fragments/partialWorkspaceMemberQueryFragment.ts +++ b/packages/twenty-front/src/modules/workspace-member/graphql/fragments/partialWorkspaceMemberQueryFragment.ts @@ -9,5 +9,6 @@ export const PARTIAL_WORKSPACE_MEMBER_QUERY_FRAGMENT = gql` } avatarUrl userEmail + userWorkspaceId } `; diff --git a/packages/twenty-front/src/modules/workspace-member/graphql/fragments/workspaceMemberQueryFragment.ts b/packages/twenty-front/src/modules/workspace-member/graphql/fragments/workspaceMemberQueryFragment.ts index 304507e8f52..03674b52f79 100644 --- a/packages/twenty-front/src/modules/workspace-member/graphql/fragments/workspaceMemberQueryFragment.ts +++ b/packages/twenty-front/src/modules/workspace-member/graphql/fragments/workspaceMemberQueryFragment.ts @@ -11,6 +11,7 @@ export const WORKSPACE_MEMBER_QUERY_FRAGMENT = gql` avatarUrl locale userEmail + userWorkspaceId timeZone dateFormat timeFormat diff --git a/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogFilters.tsx b/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogFilters.tsx index 63a9c1d9b57..681884963d0 100644 --- a/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogFilters.tsx +++ b/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogFilters.tsx @@ -1,11 +1,11 @@ -import styled from '@emotion/styled'; -import { useLingui } from '@lingui/react/macro'; import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { Select } from '@/ui/input/components/Select'; import { TextInput } from '@/ui/input/components/TextInput'; -import { IconUser, IconBox, useIcons } from 'twenty-ui/display'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; +import styled from '@emotion/styled'; +import { useLingui } from '@lingui/react/macro'; +import { IconBox, IconUser, useIcons } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; import { EventLogTable } from '~/generated-metadata/graphql'; diff --git a/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogResultsTable.tsx b/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogResultsTable.tsx index ccff842f58c..0ce3bc8db2c 100644 --- a/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogResultsTable.tsx +++ b/packages/twenty-front/src/pages/settings/security/event-logs/components/EventLogResultsTable.tsx @@ -42,8 +42,8 @@ const DEFAULT_COLUMNS: ColumnConfig[] = [ { id: 'event', label: msg`Event`, minWidth: 100, defaultWidth: 200 }, { id: 'timestamp', label: msg`Timestamp`, minWidth: 100, defaultWidth: 150 }, { - id: 'userWorkspaceId', - label: msg`User Workspace`, + id: 'userId', + label: msg`User`, minWidth: 100, defaultWidth: 150, }, @@ -59,8 +59,8 @@ const OBJECT_EVENT_COLUMNS: ColumnConfig[] = [ { id: 'event', label: msg`Event`, minWidth: 100, defaultWidth: 180 }, { id: 'timestamp', label: msg`Timestamp`, minWidth: 100, defaultWidth: 130 }, { - id: 'userWorkspaceId', - label: msg`User Workspace`, + id: 'userId', + label: msg`User`, minWidth: 100, defaultWidth: 130, }, @@ -318,7 +318,7 @@ export const EventLogResultsTable = ({ {beautifyPastDateRelativeToNow(record.timestamp)} - {record.userWorkspaceId ?? '-'} + {record.userId ?? '-'} {showObjectEventColumns && ( <> {record.recordId ?? '-'} diff --git a/packages/twenty-front/src/pages/settings/security/event-logs/graphql/queries/getEventLogs.ts b/packages/twenty-front/src/pages/settings/security/event-logs/graphql/queries/getEventLogs.ts index da0faa513f7..c0ac6a8de9f 100644 --- a/packages/twenty-front/src/pages/settings/security/event-logs/graphql/queries/getEventLogs.ts +++ b/packages/twenty-front/src/pages/settings/security/event-logs/graphql/queries/getEventLogs.ts @@ -6,7 +6,7 @@ export const GET_EVENT_LOGS = gql` records { event timestamp - userWorkspaceId + userId properties recordId objectMetadataId diff --git a/packages/twenty-server/src/database/clickHouse/migrations/001-create-workspace-event-table.sql b/packages/twenty-server/src/database/clickHouse/migrations/001-create-workspace-event-table.sql index f73fd11e0c2..a422f42eebd 100644 --- a/packages/twenty-server/src/database/clickHouse/migrations/001-create-workspace-event-table.sql +++ b/packages/twenty-server/src/database/clickHouse/migrations/001-create-workspace-event-table.sql @@ -7,4 +7,5 @@ CREATE TABLE IF NOT EXISTS workspaceEvent `properties` JSON ) ENGINE = MergeTree - ORDER BY (workspaceId, event, userId, timestamp); \ No newline at end of file + ORDER BY (workspaceId, timestamp, event, userId) + TTL timestamp + INTERVAL 3 YEAR DELETE; diff --git a/packages/twenty-server/src/database/clickHouse/migrations/002-create-pageview-table.sql b/packages/twenty-server/src/database/clickHouse/migrations/002-create-pageview-table.sql index 3b77e2f4990..14c9a6594c5 100644 --- a/packages/twenty-server/src/database/clickHouse/migrations/002-create-pageview-table.sql +++ b/packages/twenty-server/src/database/clickHouse/migrations/002-create-pageview-table.sql @@ -1,10 +1,11 @@ CREATE TABLE IF NOT EXISTS pageview ( - `name` LowCardinality(String) NOT NULL, - `timestamp` DateTime64(3) NOT NULL, + `name` LowCardinality(String) NOT NULL, + `timestamp` DateTime64(3) NOT NULL, `userId` String DEFAULT '', `workspaceId` String DEFAULT '', `properties` JSON ) ENGINE = MergeTree - ORDER BY (workspaceId, name, userId, timestamp); \ No newline at end of file + ORDER BY (workspaceId, timestamp, name, userId) + TTL timestamp + INTERVAL 3 YEAR DELETE; diff --git a/packages/twenty-server/src/database/clickHouse/migrations/003-create-object-event-table.sql b/packages/twenty-server/src/database/clickHouse/migrations/003-create-object-event-table.sql index d20ae2864aa..6b6f9bdd1b7 100644 --- a/packages/twenty-server/src/database/clickHouse/migrations/003-create-object-event-table.sql +++ b/packages/twenty-server/src/database/clickHouse/migrations/003-create-object-event-table.sql @@ -1,13 +1,14 @@ CREATE TABLE IF NOT EXISTS objectEvent ( - `event` LowCardinality(String) NOT NULL, - `timestamp` DateTime64(3) NOT NULL, - `userId` String DEFAULT '', - `workspaceId` String NOT NULL, - `recordId` String NOT NULL, - `objectMetadataId` String NOT NULL, - `properties` JSON, - `isCustom` Boolean DEFAULT FALSE, + `event` LowCardinality(String) NOT NULL, + `timestamp` DateTime64(3) NOT NULL, + `userId` String DEFAULT '', + `workspaceId` String NOT NULL, + `recordId` String NOT NULL, + `objectMetadataId` String NOT NULL, + `properties` JSON, + `isCustom` Boolean DEFAULT FALSE ) ENGINE = MergeTree - ORDER BY (workspaceId, event, userId, timestamp); \ No newline at end of file + ORDER BY (workspaceId, timestamp, event, userId) + TTL timestamp + INTERVAL 3 YEAR DELETE; diff --git a/packages/twenty-server/src/database/clickHouse/migrations/004-optimize-workspace-event-table.sql b/packages/twenty-server/src/database/clickHouse/migrations/004-optimize-workspace-event-table.sql deleted file mode 100644 index bae517bbbb8..00000000000 --- a/packages/twenty-server/src/database/clickHouse/migrations/004-optimize-workspace-event-table.sql +++ /dev/null @@ -1,26 +0,0 @@ --- Optimize workspaceEvent table for time-series queries --- ClickHouse doesn't allow changing ORDER BY, so we create a new table and migrate data - --- Step 1: Create new table with optimized structure -CREATE TABLE IF NOT EXISTS workspaceEvent_v2 -( - `event` LowCardinality(String) NOT NULL, - `timestamp` DateTime64(3) NOT NULL, - `userWorkspaceId` String DEFAULT '', - `workspaceId` String NOT NULL, - `properties` JSON -) - ENGINE = MergeTree - ORDER BY (workspaceId, timestamp, event, userWorkspaceId) - TTL timestamp + INTERVAL 3 YEAR DELETE; - --- Step 2: Migrate existing data (userWorkspaceId will be empty for historical data) -INSERT INTO workspaceEvent_v2 -SELECT event, timestamp, '' as userWorkspaceId, workspaceId, properties -FROM workspaceEvent; - --- Step 3: Atomic swap (EXCHANGE is atomic and instant) -EXCHANGE TABLES workspaceEvent AND workspaceEvent_v2; - --- Step 4: Drop old table (now named workspaceEvent_v2 after swap) -DROP TABLE IF EXISTS workspaceEvent_v2; diff --git a/packages/twenty-server/src/database/clickHouse/migrations/005-optimize-pageview-table.sql b/packages/twenty-server/src/database/clickHouse/migrations/005-optimize-pageview-table.sql deleted file mode 100644 index 4f23c8b94b3..00000000000 --- a/packages/twenty-server/src/database/clickHouse/migrations/005-optimize-pageview-table.sql +++ /dev/null @@ -1,26 +0,0 @@ --- Optimize pageview table for time-series queries --- ClickHouse doesn't allow changing ORDER BY, so we create a new table and migrate data - --- Step 1: Create new table with optimized structure -CREATE TABLE IF NOT EXISTS pageview_v2 -( - `name` LowCardinality(String) NOT NULL, - `timestamp` DateTime64(3) NOT NULL, - `userWorkspaceId` String DEFAULT '', - `workspaceId` String DEFAULT '', - `properties` JSON -) - ENGINE = MergeTree - ORDER BY (workspaceId, timestamp, name, userWorkspaceId) - TTL timestamp + INTERVAL 3 YEAR DELETE; - --- Step 2: Migrate existing data (userWorkspaceId will be empty for historical data) -INSERT INTO pageview_v2 -SELECT name, timestamp, '' as userWorkspaceId, workspaceId, properties -FROM pageview; - --- Step 3: Atomic swap -EXCHANGE TABLES pageview AND pageview_v2; - --- Step 4: Drop old table -DROP TABLE IF EXISTS pageview_v2; diff --git a/packages/twenty-server/src/database/clickHouse/migrations/006-optimize-object-event-table.sql b/packages/twenty-server/src/database/clickHouse/migrations/006-optimize-object-event-table.sql deleted file mode 100644 index bbe58c24b06..00000000000 --- a/packages/twenty-server/src/database/clickHouse/migrations/006-optimize-object-event-table.sql +++ /dev/null @@ -1,29 +0,0 @@ --- Optimize objectEvent table for time-series queries and object filtering --- ClickHouse doesn't allow changing ORDER BY, so we create a new table and migrate data - --- Step 1: Create new table with optimized structure -CREATE TABLE IF NOT EXISTS objectEvent_v2 -( - `event` LowCardinality(String) NOT NULL, - `timestamp` DateTime64(3) NOT NULL, - `userWorkspaceId` String DEFAULT '', - `workspaceId` String NOT NULL, - `recordId` String NOT NULL, - `objectMetadataId` String NOT NULL, - `properties` JSON, - `isCustom` Boolean DEFAULT FALSE -) - ENGINE = MergeTree - ORDER BY (workspaceId, timestamp, objectMetadataId, recordId, event, userWorkspaceId) - TTL timestamp + INTERVAL 3 YEAR DELETE; - --- Step 2: Migrate existing data (userWorkspaceId will be empty for historical data) -INSERT INTO objectEvent_v2 -SELECT event, timestamp, '' as userWorkspaceId, workspaceId, recordId, objectMetadataId, properties, isCustom -FROM objectEvent; - --- Step 3: Atomic swap -EXCHANGE TABLES objectEvent AND objectEvent_v2; - --- Step 4: Drop old table -DROP TABLE IF EXISTS objectEvent_v2; diff --git a/packages/twenty-server/src/database/clickHouse/seeds/fixtures.ts b/packages/twenty-server/src/database/clickHouse/seeds/fixtures.ts index 89c2336e699..e5c8271bbf7 100644 --- a/packages/twenty-server/src/database/clickHouse/seeds/fixtures.ts +++ b/packages/twenty-server/src/database/clickHouse/seeds/fixtures.ts @@ -5,13 +5,19 @@ import { CUSTOM_DOMAIN_ACTIVATED_EVENT } from 'src/engine/core-modules/audit/uti import { CUSTOM_DOMAIN_DEACTIVATED_EVENT } from 'src/engine/core-modules/audit/utils/events/workspace-event/custom-domain/custom-domain-deactivated'; import { type GenericTrackEvent } from 'src/engine/core-modules/audit/utils/events/workspace-event/track'; -export const fixtures: Array = [ +export type ObjectEventFixture = GenericTrackEvent & { + recordId: string; + objectMetadataId: string; + isCustom?: boolean; +}; + +export const workspaceEventFixtures: Array = [ { type: 'track', event: CUSTOM_DOMAIN_ACTIVATED_EVENT, timestamp: '2024-10-24T15:55:35.177', version: '1', - userWorkspaceId: '20202020-3957-45c9-be39-337dc4d9100a', + userId: '20202020-3957-45c9-be39-337dc4d9100a', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', properties: {}, }, @@ -20,35 +26,44 @@ export const fixtures: Array = [ event: CUSTOM_DOMAIN_DEACTIVATED_EVENT, timestamp: '2024-10-24T15:55:35.177', version: '1', - userWorkspaceId: '20202020-3957-45c9-be39-337dc4d9100a', + userId: '20202020-3957-45c9-be39-337dc4d9100a', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', properties: {}, }, +]; + +export const objectEventFixtures: Array = [ { type: 'track', event: OBJECT_RECORD_CREATED_EVENT, timestamp: '2024-10-24T15:55:35.177', version: '1', - userWorkspaceId: '20202020-3957-45c9-be39-337dc4d9100a', + userId: '20202020-3957-45c9-be39-337dc4d9100a', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', properties: {}, + recordId: '20202020-c21e-4ec2-873b-de4264d89025', + objectMetadataId: '20202020-1f76-4e46-b33b-58a70e007ba0', }, { type: 'track', event: OBJECT_RECORD_UPDATED_EVENT, timestamp: '2024-10-24T15:55:35.177', version: '1', - userWorkspaceId: '20202020-3957-45c9-be39-337dc4d9100a', + userId: '20202020-3957-45c9-be39-337dc4d9100a', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', properties: {}, + recordId: '20202020-c21e-4ec2-873b-de4264d89025', + objectMetadataId: '20202020-1f76-4e46-b33b-58a70e007ba0', }, { type: 'track', event: OBJECT_RECORD_DELETED_EVENT, timestamp: '2024-10-24T15:55:35.177', version: '1', - userWorkspaceId: '20202020-3957-45c9-be39-337dc4d9100a', + userId: '20202020-3957-45c9-be39-337dc4d9100a', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', properties: {}, + recordId: '20202020-c21e-4ec2-873b-de4264d89025', + objectMetadataId: '20202020-1f76-4e46-b33b-58a70e007ba0', }, ]; diff --git a/packages/twenty-server/src/database/clickHouse/seeds/run-seeds.ts b/packages/twenty-server/src/database/clickHouse/seeds/run-seeds.ts index 797d58de4d6..f69e3bf3975 100644 --- a/packages/twenty-server/src/database/clickHouse/seeds/run-seeds.ts +++ b/packages/twenty-server/src/database/clickHouse/seeds/run-seeds.ts @@ -2,7 +2,7 @@ import { createClient } from '@clickhouse/client'; import { config } from 'dotenv'; -import { fixtures } from './fixtures'; +import { objectEventFixtures, workspaceEventFixtures } from './fixtures'; config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env', @@ -15,11 +15,21 @@ const client = createClient({ async function seedEvents() { try { - console.log(`⚡ Seeding ${fixtures.length} events...`); + console.log( + `⚡ Seeding ${workspaceEventFixtures.length} workspace events...`, + ); await client.insert({ table: 'workspaceEvent', - values: fixtures, + values: workspaceEventFixtures, + format: 'JSONEachRow', + }); + + console.log(`⚡ Seeding ${objectEventFixtures.length} object events...`); + + await client.insert({ + table: 'objectEvent', + values: objectEventFixtures, format: 'JSONEachRow', }); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts index f0bb1c73f05..bf9d5d08b62 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts @@ -24,7 +24,7 @@ export class TelemetryListener { payload.events.map(async (eventPayload) => { this.auditService .createContext({ - userWorkspaceId: eventPayload.userWorkspaceId, + userId: eventPayload.userId, workspaceId: payload.workspaceId, }) .insertWorkspaceEvent(USER_SIGNUP_EVENT, {}); diff --git a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.spec.ts b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.spec.ts index b2dfdc35052..1fef575dffd 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.spec.ts @@ -4,6 +4,7 @@ import { AuditException, AuditExceptionCode, } from 'src/engine/core-modules/audit/audit.exception'; +import { type UserEntity } from 'src/engine/core-modules/user/user.entity'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuditResolver } from './audit.resolver'; @@ -55,12 +56,12 @@ describe('AuditResolver', () => { const result = await resolver.trackAnalytics( input, { id: 'workspace-1' } as WorkspaceEntity, - 'user-workspace-1', + { id: 'user-1' } as UserEntity, ); expect(auditService.createContext).toHaveBeenCalledWith({ workspaceId: 'workspace-1', - userWorkspaceId: 'user-workspace-1', + userId: 'user-1', }); expect(mockInsertPageviewEvent).toHaveBeenCalledWith('Test Page', {}); expect(result).toBe('Pageview created'); @@ -85,12 +86,12 @@ describe('AuditResolver', () => { const result = await resolver.trackAnalytics( input, { id: 'workspace-2' } as WorkspaceEntity, - 'user-workspace-2', + { id: 'user-2' } as UserEntity, ); expect(auditService.createContext).toHaveBeenCalledWith({ workspaceId: 'workspace-2', - userWorkspaceId: 'user-workspace-2', + userId: 'user-2', }); expect(mockInsertWorkspaceEvent).toHaveBeenCalledWith( 'Custom Domain Activated', @@ -120,12 +121,12 @@ describe('AuditResolver', () => { const result = await resolver.createObjectEvent( input, { id: 'workspace-3' } as WorkspaceEntity, - 'user-workspace-3', + { id: 'user-3' } as UserEntity, ); expect(auditService.createContext).toHaveBeenCalledWith({ workspaceId: 'workspace-3', - userWorkspaceId: 'user-workspace-3', + userId: 'user-3', }); expect(mockInsertObjectEvent).toHaveBeenCalledWith( diff --git a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts index 1d8d47fc63b..01a25421eaa 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts @@ -1,6 +1,7 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation } from '@nestjs/graphql'; +import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { AuditExceptionFilter } from 'src/engine/core-modules/audit/audit-exception-filter'; import { AuditException, @@ -9,10 +10,10 @@ import { import { CreateObjectEventInput } from 'src/engine/core-modules/audit/dtos/create-object-event.input'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; +import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator'; +import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @@ -37,13 +38,9 @@ export class AuditResolver { createAnalyticsInput: CreateAnalyticsInputV2, @AuthWorkspace({ allowUndefined: true }) workspace: WorkspaceEntity | undefined, - @AuthUserWorkspaceId() userWorkspaceId: string | undefined, + @AuthUser({ allowUndefined: true }) user: UserEntity | undefined, ) { - return this.trackAnalytics( - createAnalyticsInput, - workspace, - userWorkspaceId, - ); + return this.trackAnalytics(createAnalyticsInput, workspace, user); } @Mutation(() => Analytics) @@ -52,7 +49,7 @@ export class AuditResolver { @Args() createObjectEventInput: CreateObjectEventInput, @AuthWorkspace() workspace: WorkspaceEntity | undefined, - @AuthUserWorkspaceId() userWorkspaceId: string | undefined, + @AuthUser({ allowUndefined: true }) user: UserEntity | undefined, ) { if (!workspace) { throw new AuditException( @@ -63,7 +60,7 @@ export class AuditResolver { const analyticsContext = this.auditService.createContext({ workspaceId: workspace.id, - userWorkspaceId, + userId: user?.id, }); return analyticsContext.createObjectEvent(createObjectEventInput.event, { @@ -81,11 +78,11 @@ export class AuditResolver { createAnalyticsInput: CreateAnalyticsInputV2, @AuthWorkspace({ allowUndefined: true }) workspace: WorkspaceEntity | undefined, - @AuthUserWorkspaceId() userWorkspaceId: string | undefined, + @AuthUser({ allowUndefined: true }) user: UserEntity | undefined, ) { const analyticsContext = this.auditService.createContext({ workspaceId: workspace?.id, - userWorkspaceId, + userId: user?.id, }); if (isPageviewAnalyticsInput(createAnalyticsInput)) { diff --git a/packages/twenty-server/src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event.ts b/packages/twenty-server/src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event.ts index 441d9511f9b..71d3afb464b 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event.ts @@ -30,7 +30,7 @@ export class CreateAuditLogFromInternalEvent { const auditService = this.auditService.createContext({ workspaceId: workspaceEventBatch.workspaceId, - userWorkspaceId: eventData.userWorkspaceId, + userId: eventData.userId, }); // Since these are object record events, we use createObjectEvent diff --git a/packages/twenty-server/src/engine/core-modules/audit/services/audit.service.ts b/packages/twenty-server/src/engine/core-modules/audit/services/audit.service.ts index f96b06a566c..24b78880dc8 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/services/audit.service.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/services/audit.service.ts @@ -25,14 +25,12 @@ export class AuditService { createContext(context?: { workspaceId?: string | null | undefined; - userWorkspaceId?: string | null | undefined; + userId?: string | null | undefined; }) { const contextFields = context ? { ...(context.workspaceId ? { workspaceId: context.workspaceId } : {}), - ...(context.userWorkspaceId - ? { userWorkspaceId: context.userWorkspaceId } - : {}), + ...(context.userId ? { userId: context.userId } : {}), } : {}; @@ -51,13 +49,27 @@ export class AuditService { properties: TrackEventProperties & { recordId: string; objectMetadataId: string; + isCustom?: boolean; }, - ) => - this.preventIfDisabled(() => + ) => { + const { recordId, objectMetadataId, isCustom, ...restProperties } = + properties; + + return this.preventIfDisabled(() => this.clickHouseService.insert('objectEvent', [ - { ...contextFields, ...makeTrackEvent(event, properties) }, + { + ...contextFields, + ...makeTrackEvent( + event, + restProperties as unknown as TrackEventProperties, + ), + recordId, + objectMetadataId, + isCustom, + }, ]), - ), + ); + }, createPageviewEvent: ( name: string, properties: Partial, diff --git a/packages/twenty-server/src/engine/core-modules/audit/utils/events/workspace-event/track.ts b/packages/twenty-server/src/engine/core-modules/audit/utils/events/workspace-event/track.ts index acd2fe175c2..fef9490c0fc 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/utils/events/workspace-event/track.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/utils/events/workspace-event/track.ts @@ -15,7 +15,7 @@ export type GenericTrackEvent = { properties: any; timestamp: string; version: string; - userWorkspaceId?: string; + userId?: string; workspaceId?: string; }; diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index 7d2daf4a7c0..2d364b3544c 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -3,11 +3,11 @@ import { Args, Context, Mutation, Query } from '@nestjs/graphql'; import { InjectRepository } from '@nestjs/typeorm'; import omit from 'lodash.omit'; +import { PermissionFlagType } from 'twenty-shared/constants'; import { SOURCE_LOCALE } from 'twenty-shared/translations'; import { TwoFactorAuthenticationStrategy } from 'twenty-shared/types'; import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; -import { PermissionFlagType } from 'twenty-shared/constants'; import { ApiKeyTokenInput } from 'src/engine/core-modules/auth/dto/api-key-token.input'; import { AppTokenInput } from 'src/engine/core-modules/auth/dto/app-token.input'; @@ -22,6 +22,7 @@ import { ValidatePasswordResetTokenOutput } from 'src/engine/core-modules/auth/d import { ValidatePasswordResetTokenInput } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.input'; import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; // import { OAuthService } from 'src/engine/core-modules/auth/services/oauth.service'; +import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { ApiKeyService } from 'src/engine/core-modules/api-key/services/api-key.service'; import { AppTokenEntity } from 'src/engine/core-modules/app-token/app-token.entity'; import { AuditService } from 'src/engine/core-modules/audit/services/audit.service'; @@ -70,7 +71,6 @@ import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.ent import { AuthProvider } from 'src/engine/decorators/auth/auth-provider.decorator'; import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; @@ -700,7 +700,7 @@ export class AuthResolver { const auditService = this.auditService.createContext({ workspaceId: workspace.id, - userWorkspaceId: impersonatorUserWorkspace.id, + userId: impersonatorUserWorkspace.user.id, }); auditService.insertWorkspaceEvent(MONITORING_EVENT, { diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts index 9db13451815..743a2e1f3c9 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts @@ -419,7 +419,7 @@ export class AuthService { const analytics = this.auditService.createContext({ workspaceId, - userWorkspaceId: impersonatorUserWorkspaceId, + userId: _impersonatorUserId, }); analytics.insertWorkspaceEvent('Monitoring', { diff --git a/packages/twenty-server/src/engine/core-modules/event-logs/dtos/event-log-result.output.ts b/packages/twenty-server/src/engine/core-modules/event-logs/dtos/event-log-result.output.ts index 357935e1aeb..a1806ea5077 100644 --- a/packages/twenty-server/src/engine/core-modules/event-logs/dtos/event-log-result.output.ts +++ b/packages/twenty-server/src/engine/core-modules/event-logs/dtos/event-log-result.output.ts @@ -11,7 +11,7 @@ export class EventLogRecord { timestamp: Date; @Field(() => String, { nullable: true }) - userWorkspaceId?: string; + userId?: string; @Field(() => GraphQLJSON, { nullable: true }) properties?: Record; diff --git a/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.module.ts b/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.module.ts index bcdfde559e8..2cf8d992904 100644 --- a/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.module.ts +++ b/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.module.ts @@ -1,10 +1,12 @@ /* @license Enterprise */ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { ClickHouseModule } from 'src/database/clickHouse/clickHouse.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; import { GuardRedirectModule } from 'src/engine/core-modules/guard-redirect/guard-redirect.module'; +import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { EventLogsResolver } from './event-logs.resolver'; @@ -16,6 +18,7 @@ import { EventLogsService } from './event-logs.service'; PermissionsModule, BillingModule, GuardRedirectModule, + TypeOrmModule.forFeature([UserWorkspaceEntity]), ], providers: [EventLogsResolver, EventLogsService], exports: [EventLogsService], diff --git a/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.service.ts b/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.service.ts index 77791c5e538..3eb0eddb1c1 100644 --- a/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.service.ts +++ b/packages/twenty-server/src/engine/core-modules/event-logs/event-logs.service.ts @@ -1,14 +1,17 @@ /* @license Enterprise */ import { BadRequestException, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; import { EventLogTable } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; import { ClickHouseService } from 'src/database/clickHouse/clickHouse.service'; import { formatDateForClickHouse } from 'src/database/clickHouse/clickHouse.util'; import { BillingEntitlementKey } from 'src/engine/core-modules/billing/enums/billing-entitlement-key.enum'; import { BillingService } from 'src/engine/core-modules/billing/services/billing.service'; +import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { EventLogsException, @@ -26,7 +29,7 @@ type ClickHouseEventRecord = { event?: string; name?: string; timestamp: string; - userWorkspaceId?: string; + userId?: string; properties?: Record; recordId?: string; objectMetadataId?: string; @@ -47,6 +50,8 @@ export class EventLogsService { constructor( private readonly clickHouseService: ClickHouseService, private readonly billingService: BillingService, + @InjectRepository(UserWorkspaceEntity) + private readonly userWorkspaceRepository: Repository, ) {} async queryEventLogs( @@ -67,7 +72,7 @@ export class EventLogsService { const whereClauses: string[] = ['"workspaceId" = {workspaceId:String}']; const params: Record = { workspaceId }; - this.applyFilters( + await this.applyFilters( whereClauses, params, input.filters, @@ -155,13 +160,13 @@ export class EventLogsService { } } - private applyFilters( + private async applyFilters( whereClauses: string[], params: Record, filters: EventLogFiltersInput | undefined, eventFieldName: string, table: EventLogTable, - ): void { + ): Promise { if (!isDefined(filters)) { return; } @@ -174,8 +179,15 @@ export class EventLogsService { } if (isDefined(filters.userWorkspaceId)) { - whereClauses.push('"userWorkspaceId" = {userWorkspaceId:String}'); - params.userWorkspaceId = filters.userWorkspaceId; + const userWorkspace = await this.userWorkspaceRepository.findOne({ + where: { id: filters.userWorkspaceId }, + select: ['userId'], + }); + + if (isDefined(userWorkspace)) { + whereClauses.push('"userId" = {userId:String}'); + params.userId = userWorkspace.userId; + } } if (isDefined(filters.dateRange?.start)) { @@ -222,7 +234,7 @@ export class EventLogsService { return { event: eventName, timestamp: new Date(record.timestamp), - userWorkspaceId: record.userWorkspaceId, + userId: record.userId, properties: record.properties, recordId: record.recordId, objectMetadataId: record.objectMetadataId, diff --git a/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts b/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts index f1e1454f0f8..0292e73d045 100644 --- a/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts +++ b/packages/twenty-server/src/engine/core-modules/impersonation/services/impersonation.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { PermissionFlagType } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; -import { PermissionFlagType } from 'twenty-shared/constants'; import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface'; @@ -141,7 +141,7 @@ export class ImpersonationService { ) { const auditService = this.auditService.createContext({ workspaceId: impersonatorUserWorkspace.workspace.id, - userWorkspaceId: impersonatorUserWorkspace.id, + userId: impersonatorUserWorkspace.userId, }); auditService.insertWorkspaceEvent(MONITORING_EVENT, { diff --git a/packages/twenty-server/test/integration/audit/suites/clickHouse-workspace-event-registration.integration-spec.ts b/packages/twenty-server/test/integration/audit/suites/clickHouse-workspace-event-registration.integration-spec.ts index fae8e9d321a..8450b2286c3 100644 --- a/packages/twenty-server/test/integration/audit/suites/clickHouse-workspace-event-registration.integration-spec.ts +++ b/packages/twenty-server/test/integration/audit/suites/clickHouse-workspace-event-registration.integration-spec.ts @@ -68,9 +68,9 @@ describe('ClickHouse Event Registration (integration)', () => { expect(rows.length).toEqual(1); expect(rows[0].properties).toEqual(variables.properties); expect(rows[0].event).toEqual(variables.event); - // workspaceId and userWorkspaceId are empty/undefined for unauthenticated requests + // workspaceId and userId are empty/undefined for unauthenticated requests expect(rows[0].workspaceId ?? '').toEqual(''); - expect(rows[0].userWorkspaceId ?? '').toEqual(''); + expect(rows[0].userId ?? '').toEqual(''); expect(rows[0].timestamp).toHaveLength(23); }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/event-logs/event-logs.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/event-logs/event-logs.integration-spec.ts index 751e01b994c..9b6e4f5e5e9 100644 --- a/packages/twenty-server/test/integration/graphql/suites/event-logs/event-logs.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/event-logs/event-logs.integration-spec.ts @@ -36,7 +36,7 @@ describe('Event Logs (integration)', () => { const pageviewRecords = Array.from({ length: 25 }, (_, i) => ({ workspaceId: testWorkspaceId, - userWorkspaceId: testUserWorkspaceId, + userId: testUserWorkspaceId, name: i % 2 === 0 ? 'settings/profile' : 'objects/companies', timestamp: new Date(now.getTime() - i * 60000) .toISOString() @@ -47,7 +47,7 @@ describe('Event Logs (integration)', () => { const workspaceEventRecords = Array.from({ length: 15 }, (_, i) => ({ workspaceId: testWorkspaceId, - userWorkspaceId: testUserWorkspaceId, + userId: testUserWorkspaceId, event: i % 3 === 0 ? 'user.login' @@ -63,7 +63,7 @@ describe('Event Logs (integration)', () => { const objectEventRecords = Array.from({ length: 20 }, (_, i) => ({ workspaceId: testWorkspaceId, - userWorkspaceId: testUserWorkspaceId, + userId: testUserWorkspaceId, event: i % 2 === 0 ? 'company.created' : 'company.updated', timestamp: new Date(now.getTime() - i * 90000) .toISOString() @@ -127,7 +127,7 @@ describe('Event Logs (integration)', () => { records { event timestamp - userWorkspaceId + userId properties recordId objectMetadataId @@ -462,7 +462,7 @@ describe('Event Logs (integration)', () => { expect(record).toHaveProperty('event'); expect(record).toHaveProperty('timestamp'); - expect(record).toHaveProperty('userWorkspaceId'); + expect(record).toHaveProperty('userId'); expect(record).toHaveProperty('properties'); expect(typeof record.event).toBe('string'); expect(typeof record.timestamp).toBe('string');