diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 67ffdc9966..e9eb790102 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -3069,6 +3069,7 @@ type Query { navigationMenuItems: [NavigationMenuItem!]! navigationMenuItem(id: UUID!): NavigationMenuItem applicationSdkClientChecksums(applicationId: UUID!): SdkClientChecksums + isApplicationStopped(applicationUniversalIdentifier: String!): Boolean! enterprisePortalSession(returnUrlPath: String): String enterpriseCheckoutSession(billingInterval: String): String enterpriseSubscriptionStatus: EnterpriseSubscriptionStatusDTO diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 8f54946ec6..09802f1971 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2723,6 +2723,7 @@ export interface Query { navigationMenuItems: NavigationMenuItem[] navigationMenuItem?: NavigationMenuItem applicationSdkClientChecksums?: SdkClientChecksums + isApplicationStopped: Scalars['Boolean'] enterprisePortalSession?: Scalars['String'] enterpriseCheckoutSession?: Scalars['String'] enterpriseSubscriptionStatus?: EnterpriseSubscriptionStatusDTO @@ -5937,6 +5938,7 @@ export interface QueryGenqlSelection{ navigationMenuItems?: NavigationMenuItemGenqlSelection navigationMenuItem?: (NavigationMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} }) applicationSdkClientChecksums?: (SdkClientChecksumsGenqlSelection & { __args: {applicationId: Scalars['UUID']} }) + isApplicationStopped?: { __args: {applicationUniversalIdentifier: Scalars['String']} } enterprisePortalSession?: { __args: {returnUrlPath?: (Scalars['String'] | null)} } | boolean | number enterpriseCheckoutSession?: { __args: {billingInterval?: (Scalars['String'] | null)} } | boolean | number enterpriseSubscriptionStatus?: EnterpriseSubscriptionStatusDTOGenqlSelection diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index be1d742bd8..87e318286e 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -6105,6 +6105,15 @@ export default { ] } ], + "isApplicationStopped": [ + 3, + { + "applicationUniversalIdentifier": [ + 1, + "String!" + ] + } + ], "enterprisePortalSession": [ 1, { diff --git a/packages/twenty-front/src/generated-admin/graphql.ts b/packages/twenty-front/src/generated-admin/graphql.ts index 3fd11dec6b..55d4107b18 100644 --- a/packages/twenty-front/src/generated-admin/graphql.ts +++ b/packages/twenty-front/src/generated-admin/graphql.ts @@ -439,7 +439,6 @@ export type Mutation = { __typename?: 'Mutation'; addAiProvider: Scalars['Boolean']['output']; addModelToProvider: Scalars['Boolean']['output']; - backfillApplicationInstallation: Scalars['Boolean']['output']; clearMaintenanceMode: Scalars['Boolean']['output']; createDatabaseConfigVariable: Scalars['Boolean']['output']; deleteDatabaseConfigVariable: Scalars['Boolean']['output']; @@ -461,7 +460,6 @@ export type Mutation = { updateDatabaseConfigVariable: Scalars['Boolean']['output']; updateServerAdminAccess: ServerAdmin; updateWorkspaceFeatureFlag: Scalars['Boolean']['output']; - upgradeRegistrationApplications: Scalars['Boolean']['output']; }; @@ -477,11 +475,6 @@ export type MutationAddModelToProviderArgs = { }; -export type MutationBackfillApplicationInstallationArgs = { - applicationRegistrationId: Scalars['String']['input']; -}; - - export type MutationCreateDatabaseConfigVariableArgs = { key: Scalars['String']['input']; value: Scalars['JSON']['input']; @@ -588,12 +581,6 @@ export type MutationUpdateWorkspaceFeatureFlagArgs = { workspaceId: Scalars['UUID']['input']; }; - -export type MutationUpgradeRegistrationApplicationsArgs = { - applicationRegistrationId: Scalars['String']['input']; - batchSize?: InputMaybe; -}; - export type PaginatedApplicationRegistrations = { __typename?: 'PaginatedApplicationRegistrations'; hasMore: Scalars['Boolean']['output']; @@ -1089,13 +1076,6 @@ export type GetModelsDevSuggestionsQueryVariables = Exact<{ export type GetModelsDevSuggestionsQuery = { __typename?: 'Query', getModelsDevSuggestions: Array<{ __typename?: 'ModelsDevModelSuggestion', modelId: string, name: string, inputCostPerMillionTokens: number, outputCostPerMillionTokens: number, cachedInputCostPerMillionTokens?: number | null, cacheCreationCostPerMillionTokens?: number | null, contextWindowTokens: number, maxOutputTokens: number, modalities: Array, supportsReasoning: boolean }> }; -export type BackfillApplicationInstallationMutationVariables = Exact<{ - applicationRegistrationId: Scalars['String']['input']; -}>; - - -export type BackfillApplicationInstallationMutation = { __typename?: 'Mutation', backfillApplicationInstallation: boolean }; - export type SyncMarketplaceCatalogMutationVariables = Exact<{ [key: string]: never; }>; @@ -1115,14 +1095,6 @@ export type UpdateAdminApplicationRegistrationVariableMutationVariables = Exact< export type UpdateAdminApplicationRegistrationVariableMutation = { __typename?: 'Mutation', updateAdminApplicationRegistrationVariable: { __typename?: 'ApplicationRegistrationVariableDTO', id: string, key: string, description: string, isSecret: boolean, isRequired: boolean, isFilled: boolean, createdAt: string, updatedAt: string } }; -export type UpgradeRegistrationApplicationsMutationVariables = Exact<{ - applicationRegistrationId: Scalars['String']['input']; - batchSize?: InputMaybe; -}>; - - -export type UpgradeRegistrationApplicationsMutation = { __typename?: 'Mutation', upgradeRegistrationApplications: boolean }; - export type FindAdminApplicationRegistrationClaimsQueryVariables = Exact<{ applicationRegistrationId: Scalars['String']['input']; }>; @@ -1395,11 +1367,9 @@ export const GetAdminAiUsageByWorkspaceDocument = {"kind":"Document","definition export const GetAiProvidersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAiProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAiProviders"}}]}}]} as unknown as DocumentNode; export const GetModelsDevProvidersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetModelsDevProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getModelsDevProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"modelCount"}},{"kind":"Field","name":{"kind":"Name","value":"npm"}}]}}]}}]} as unknown as DocumentNode; export const GetModelsDevSuggestionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetModelsDevSuggestions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"providerType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getModelsDevSuggestions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"providerType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"providerType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"inputCostPerMillionTokens"}},{"kind":"Field","name":{"kind":"Name","value":"outputCostPerMillionTokens"}},{"kind":"Field","name":{"kind":"Name","value":"cachedInputCostPerMillionTokens"}},{"kind":"Field","name":{"kind":"Name","value":"cacheCreationCostPerMillionTokens"}},{"kind":"Field","name":{"kind":"Name","value":"contextWindowTokens"}},{"kind":"Field","name":{"kind":"Name","value":"maxOutputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"modalities"}},{"kind":"Field","name":{"kind":"Name","value":"supportsReasoning"}}]}}]}}]} as unknown as DocumentNode; -export const BackfillApplicationInstallationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BackfillApplicationInstallation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"applicationRegistrationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"backfillApplicationInstallation"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"applicationRegistrationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"applicationRegistrationId"}}}]}]}}]} as unknown as DocumentNode; export const SyncMarketplaceCatalogDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SyncMarketplaceCatalog"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncMarketplaceCatalog"}}]}}]} as unknown as DocumentNode; export const UpdateAdminApplicationRegistrationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateAdminApplicationRegistration"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateApplicationRegistrationInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateAdminApplicationRegistration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ApplicationRegistrationFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ApplicationRegistrationFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ApplicationRegistration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"galleryImagesUrls"}},{"kind":"Field","name":{"kind":"Name","value":"oAuthClientId"}},{"kind":"Field","name":{"kind":"Name","value":"oAuthRedirectUris"}},{"kind":"Field","name":{"kind":"Name","value":"oAuthScopes"}},{"kind":"Field","name":{"kind":"Name","value":"sourceType"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"latestAvailableVersion"}},{"kind":"Field","name":{"kind":"Name","value":"isListed"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}},{"kind":"Field","name":{"kind":"Name","value":"isPreInstalled"}},{"kind":"Field","name":{"kind":"Name","value":"isConfigured"}},{"kind":"Field","name":{"kind":"Name","value":"ownerWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const UpdateAdminApplicationRegistrationVariableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateAdminApplicationRegistrationVariable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateApplicationRegistrationVariableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateAdminApplicationRegistrationVariable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"isSecret"}},{"kind":"Field","name":{"kind":"Name","value":"isRequired"}},{"kind":"Field","name":{"kind":"Name","value":"isFilled"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; -export const UpgradeRegistrationApplicationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpgradeRegistrationApplications"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"applicationRegistrationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"batchSize"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upgradeRegistrationApplications"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"applicationRegistrationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"applicationRegistrationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"batchSize"},"value":{"kind":"Variable","name":{"kind":"Name","value":"batchSize"}}}]}]}}]} as unknown as DocumentNode; export const FindAdminApplicationRegistrationClaimsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindAdminApplicationRegistrationClaims"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"applicationRegistrationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findAdminApplicationRegistrationClaims"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"applicationRegistrationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"applicationRegistrationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDisplayName"}}]}}]}}]} as unknown as DocumentNode; export const FindAdminApplicationRegistrationInstalledWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindAdminApplicationRegistrationInstalledWorkspaces"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FindApplicationRegistrationInstalledWorkspacesInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findAdminApplicationRegistrationInstalledWorkspaces"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"hasMore"}},{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}}]}}]}}]} as unknown as DocumentNode; export const FindAdminApplicationRegistrationStatsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindAdminApplicationRegistrationStats"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findAdminApplicationRegistrationStats"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeInstalls"}},{"kind":"Field","name":{"kind":"Name","value":"mostInstalledVersion"}},{"kind":"Field","name":{"kind":"Name","value":"versionDistribution"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index e42d347671..4e2a8d6287 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -4454,6 +4454,7 @@ export type Query = { getViews: Array; getWorkspaceCreationDefaults: WorkspaceCreationDefaultsDto; githubClaimAuthorizationUrl: Scalars['String']['output']; + isApplicationStopped: Scalars['Boolean']['output']; lineChartData: LineChartData; listPlans: Array; minimalMetadata: MinimalMetadata; @@ -4818,6 +4819,11 @@ export type QueryGithubClaimAuthorizationUrlArgs = { }; +export type QueryIsApplicationStoppedArgs = { + applicationUniversalIdentifier: Scalars['String']['input']; +}; + + export type QueryLineChartDataArgs = { input: LineChartDataInput; }; @@ -6818,6 +6824,13 @@ export type FindOneApplicationSummaryQueryVariables = Exact<{ export type FindOneApplicationSummaryQuery = { __typename?: 'Query', findOneApplication: { __typename?: 'Application', id: string } }; +export type IsApplicationStoppedQueryVariables = Exact<{ + applicationUniversalIdentifier: Scalars['String']['input']; +}>; + + +export type IsApplicationStoppedQuery = { __typename?: 'Query', isApplicationStopped: boolean }; + export type AuthTokenFragmentFragment = { __typename?: 'AuthToken', token: string, expiresAt: string }; export type AuthTokenPairFragmentFragment = { __typename?: 'AuthTokenPair', accessOrWorkspaceAgnosticToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } }; @@ -8998,6 +9011,7 @@ export const FindOneApplicationDocument = {"kind":"Document","definitions":[{"ki export const FindOneApplicationByUniversalIdentifierDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneApplicationByUniversalIdentifier"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}}]}}]} as unknown as DocumentNode; export const FindOneApplicationNameDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneApplicationName"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode; export const FindOneApplicationSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneApplicationSummary"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; +export const IsApplicationStoppedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"IsApplicationStopped"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"applicationUniversalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isApplicationStopped"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"applicationUniversalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"applicationUniversalIdentifier"}}}]}]}}]} as unknown as DocumentNode; export const AuthorizeAppDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"authorizeApp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"codeChallenge"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"redirectUrl"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"state"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorizeApp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"clientId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"clientId"}}},{"kind":"Argument","name":{"kind":"Name","value":"codeChallenge"},"value":{"kind":"Variable","name":{"kind":"Name","value":"codeChallenge"}}},{"kind":"Argument","name":{"kind":"Name","value":"redirectUrl"},"value":{"kind":"Variable","name":{"kind":"Name","value":"redirectUrl"}}},{"kind":"Argument","name":{"kind":"Name","value":"state"},"value":{"kind":"Variable","name":{"kind":"Name","value":"state"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"redirectUrl"}}]}}]}}]} as unknown as DocumentNode; export const EmailPasswordResetLinkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EmailPasswordResetLink"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"emailPasswordResetLink"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}},{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}}]}}]}}]} as unknown as DocumentNode; export const GenerateApiKeyTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"GenerateApiKeyToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apiKeyId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiresAt"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"generateApiKeyToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"apiKeyId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apiKeyId"}}},{"kind":"Argument","name":{"kind":"Name","value":"expiresAt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expiresAt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/applications/graphql/queries/isApplicationStopped.ts b/packages/twenty-front/src/modules/applications/graphql/queries/isApplicationStopped.ts new file mode 100644 index 0000000000..057c8e16c7 --- /dev/null +++ b/packages/twenty-front/src/modules/applications/graphql/queries/isApplicationStopped.ts @@ -0,0 +1,9 @@ +import { gql } from '@apollo/client'; + +export const IS_APPLICATION_STOPPED = gql` + query IsApplicationStopped($applicationUniversalIdentifier: String!) { + isApplicationStopped( + applicationUniversalIdentifier: $applicationUniversalIdentifier + ) + } +`; diff --git a/packages/twenty-front/src/modules/settings/admin-panel/apps/graphql/mutations/backfillApplicationInstallation.ts b/packages/twenty-front/src/modules/settings/admin-panel/apps/graphql/mutations/backfillApplicationInstallation.ts deleted file mode 100644 index 58ff8c419f..0000000000 --- a/packages/twenty-front/src/modules/settings/admin-panel/apps/graphql/mutations/backfillApplicationInstallation.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { gql } from '@apollo/client'; - -export const BACKFILL_APPLICATION_INSTALLATION = gql` - mutation BackfillApplicationInstallation( - $applicationRegistrationId: String! - ) { - backfillApplicationInstallation( - applicationRegistrationId: $applicationRegistrationId - ) - } -`; diff --git a/packages/twenty-front/src/modules/settings/admin-panel/apps/graphql/mutations/upgradeRegistrationApplications.ts b/packages/twenty-front/src/modules/settings/admin-panel/apps/graphql/mutations/upgradeRegistrationApplications.ts deleted file mode 100644 index a57c8ea15e..0000000000 --- a/packages/twenty-front/src/modules/settings/admin-panel/apps/graphql/mutations/upgradeRegistrationApplications.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { gql } from '@apollo/client'; - -export const UPGRADE_REGISTRATION_APPLICATIONS = gql` - mutation UpgradeRegistrationApplications( - $applicationRegistrationId: String! - $batchSize: Int - ) { - upgradeRegistrationApplications( - applicationRegistrationId: $applicationRegistrationId - batchSize: $batchSize - ) - } -`; diff --git a/packages/twenty-front/src/pages/settings/admin-panel/SettingsAdminApplicationRegistrationGeneralToggles.tsx b/packages/twenty-front/src/pages/settings/admin-panel/SettingsAdminApplicationRegistrationGeneralToggles.tsx index 4d0b49e554..551198a500 100644 --- a/packages/twenty-front/src/pages/settings/admin-panel/SettingsAdminApplicationRegistrationGeneralToggles.tsx +++ b/packages/twenty-front/src/pages/settings/admin-panel/SettingsAdminApplicationRegistrationGeneralToggles.tsx @@ -1,28 +1,15 @@ import { Section } from 'twenty-ui/layout'; -import { AppTooltip, Card, TooltipDelay } from 'twenty-ui/surfaces'; +import { Card } from 'twenty-ui/surfaces'; import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; -import { - IconArrowBarToDown, - IconPinned, - IconReload, - IconShield, -} from 'twenty-ui/icon'; -import { Button } from 'twenty-ui/input'; +import { IconArrowBarToDown, IconPinned, IconShield } from 'twenty-ui/icon'; import { H2Title } from 'twenty-ui/typography'; import { type ApplicationRegistration } from '~/generated-metadata/graphql'; -import { - BackfillApplicationInstallationDocument, - UpdateAdminApplicationRegistrationDocument, -} from '~/generated-admin/graphql'; +import { UpdateAdminApplicationRegistrationDocument } from '~/generated-admin/graphql'; import { styled } from '@linaria/react'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { useMutation } from '@apollo/client/react'; import { useLingui } from '@lingui/react/macro'; -import { useState } from 'react'; import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; -import { useModal } from '@/ui/layout/modal/hooks/useModal'; const StyledToggleContainer = styled.div` display: flex; @@ -30,22 +17,6 @@ const StyledToggleContainer = styled.div` gap: ${themeCssVariables.spacing[3]}; `; -const StyledBackfillContainer = styled.div` - align-items: center; - display: flex; - gap: ${themeCssVariables.spacing[2]}; - margin-top: ${themeCssVariables.spacing[6]}; -`; - -const StyledButtonWrapper = styled.span` - display: inline-flex; -`; - -const BACKFILL_INSTALLATION_MODAL_ID = - 'backfill-application-installation-modal'; - -const BACKFILL_BUTTON_ID = 'backfill-application-installation-button'; - export const SettingsAdminApplicationRegistrationGeneralToggles = ({ registration, }: { @@ -53,40 +24,12 @@ export const SettingsAdminApplicationRegistrationGeneralToggles = ({ }) => { const { t } = useLingui(); const apolloAdminClient = useApolloAdminClient(); - const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar(); - const { openModal, closeModal } = useModal(); - - const [isBackfilling, setIsBackfilling] = useState(false); const [updateRegistration] = useMutation( UpdateAdminApplicationRegistrationDocument, { client: apolloAdminClient }, ); - const [backfillInstallation] = useMutation( - BackfillApplicationInstallationDocument, - { client: apolloAdminClient }, - ); - - const handleBackfill = async () => { - setIsBackfilling(true); - try { - await backfillInstallation({ - variables: { applicationRegistrationId: registration.id }, - }); - enqueueSuccessSnackBar({ - message: t`Backfill started. This app will be installed on all existing workspaces in the background.`, - }); - } catch { - enqueueErrorSnackBar({ - message: t`Failed to start backfill`, - }); - } finally { - setIsBackfilling(false); - closeModal(BACKFILL_INSTALLATION_MODAL_ID); - } - }; - return (
@@ -146,36 +89,6 @@ export const SettingsAdminApplicationRegistrationGeneralToggles = ({ /> - - -
); }; diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx index 42fcdbfb0c..e66523a7de 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx @@ -22,7 +22,9 @@ import { useParams } from 'react-router-dom'; import { type Manifest } from 'twenty-shared/application'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath, isDefined } from 'twenty-shared/utils'; +import { InlineBanner } from 'twenty-ui/feedback'; import { + IconAlertTriangle, IconBox, IconCommand, IconGraph, @@ -36,6 +38,7 @@ import { FindMarketplaceAppDetailDocument, FindMarketplaceAppManifestDocument, FindOneApplicationDocument, + IsApplicationStoppedDocument, PermissionFlagType, UninstallApplicationDocument, } from '~/generated-metadata/graphql'; @@ -82,6 +85,21 @@ export const SettingsApplicationDetails = () => { skip: !application?.universalIdentifier, }); + const { data: isApplicationStoppedData } = useQuery( + IsApplicationStoppedDocument, + { + variables: { + applicationUniversalIdentifier: application?.universalIdentifier ?? '', + }, + skip: !application?.universalIdentifier, + // The kill switch is toggled server-side, a cached value would hide it. + fetchPolicy: 'network-only', + }, + ); + + const isApplicationStopped = + isApplicationStoppedData?.isApplicationStopped === true; + const detail = detailData?.findMarketplaceAppDetail; const manifest = manifestData?.findMarketplaceAppDetail?.manifest as | Manifest @@ -349,6 +367,13 @@ export const SettingsApplicationDetails = () => { ]} > + {isApplicationStopped && ( + + )} {renderActiveTabContent()} diff --git a/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationRegistrationGeneralStats.tsx b/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationRegistrationGeneralStats.tsx index eec49f32a8..f30a186633 100644 --- a/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationRegistrationGeneralStats.tsx +++ b/packages/twenty-front/src/pages/settings/applications/components/SettingsApplicationRegistrationGeneralStats.tsx @@ -1,83 +1,16 @@ -import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput'; -import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; -import { useModal } from '@/ui/layout/modal/hooks/useModal'; -import { useMutation } from '@apollo/client/react'; import { useLingui } from '@lingui/react/macro'; -import { useState } from 'react'; -import { IconRefresh } from 'twenty-ui/icon'; -import { Button } from 'twenty-ui/input'; import { Section } from 'twenty-ui/layout'; import { H2Title } from 'twenty-ui/typography'; -import { styled } from '@linaria/react'; -import { themeCssVariables } from 'twenty-ui/theme-constants'; -import { UpgradeRegistrationApplicationsDocument } from '~/generated-admin/graphql'; import { type ApplicationRegistration } from '~/generated-metadata/graphql'; import { SettingsApplicationRegistrationInstalledWorkspaces } from '~/pages/settings/applications/components/SettingsApplicationRegistrationInstalledWorkspaces'; import { SettingsApplicationRegistrationInstallStats } from '~/pages/settings/applications/components/SettingsApplicationRegistrationInstallStats'; -const StyledUpgradeContainer = styled.div` - margin-top: ${themeCssVariables.spacing[6]}; -`; - -const StyledBatchSizeContainer = styled.div` - margin-top: ${themeCssVariables.spacing[4]}; - text-align: left; -`; - -const UPGRADE_INSTALLATIONS_MODAL_ID = - 'upgrade-registration-applications-modal'; - -const DEFAULT_UPGRADE_BATCH_SIZE = 5; - export const SettingsApplicationRegistrationGeneralStats = ({ registration, }: { registration: ApplicationRegistration; }) => { const { t } = useLingui(); - const apolloAdminClient = useApolloAdminClient(); - const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar(); - const { openModal, closeModal } = useModal(); - - const [isUpgrading, setIsUpgrading] = useState(false); - const [batchSizeInput, setBatchSizeInput] = useState( - String(DEFAULT_UPGRADE_BATCH_SIZE), - ); - - const [upgradeApplications] = useMutation( - UpgradeRegistrationApplicationsDocument, - { client: apolloAdminClient }, - ); - - const parsedBatchSize = Number.parseInt(batchSizeInput, 10); - const batchSize = - Number.isNaN(parsedBatchSize) || parsedBatchSize < 1 - ? DEFAULT_UPGRADE_BATCH_SIZE - : parsedBatchSize; - - const handleUpgrade = async () => { - setIsUpgrading(true); - try { - await upgradeApplications({ - variables: { - applicationRegistrationId: registration.id, - batchSize, - }, - }); - enqueueSuccessSnackBar({ - message: t`Upgrade started. Existing installations will be upgraded to the latest version in the background.`, - }); - } catch { - enqueueErrorSnackBar({ - message: t`Failed to start upgrade`, - }); - } finally { - setIsUpgrading(false); - closeModal(UPGRADE_INSTALLATIONS_MODAL_ID); - } - }; return (
@@ -91,41 +24,6 @@ export const SettingsApplicationRegistrationGeneralStats = ({ - -
); }; diff --git a/packages/twenty-server/src/database/commands/utils/ask-command-confirmation.util.ts b/packages/twenty-server/src/database/commands/utils/ask-command-confirmation.util.ts new file mode 100644 index 0000000000..b9e80218a9 --- /dev/null +++ b/packages/twenty-server/src/database/commands/utils/ask-command-confirmation.util.ts @@ -0,0 +1,18 @@ +import { createInterface } from 'node:readline/promises'; + +export const askCommandConfirmation = async ( + message: string, +): Promise => { + const readline = createInterface({ + input: process.stdin, + output: process.stdout, + }); + + try { + const answer = await readline.question(`${message} (y/N): `); + + return answer.trim().toLowerCase() === 'y'; + } finally { + readline.close(); + } +}; diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts index 78b5cad865..920fd89ca2 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts @@ -59,14 +59,6 @@ import { ApplicationRegistrationStatsDTO } from 'src/engine/core-modules/applica import { FindApplicationRegistrationInstalledWorkspacesInput } from 'src/engine/core-modules/application/application-registration/dtos/find-application-registration-installed-workspaces.input'; import { PaginatedApplicationRegistrationsDTO } from 'src/engine/core-modules/application/application-registration/dtos/paginated-application-registrations.dto'; import { UpdateApplicationRegistrationInput } from 'src/engine/core-modules/application/application-registration/dtos/update-application-registration.input'; -import { - BACKFILL_APPLICATION_INSTALLATION_JOB_NAME, - type BackfillApplicationInstallationJobData, -} from 'src/engine/core-modules/application/jobs/backfill-application-installation.job-constants'; -import { - UPGRADE_APPLICATIONS_JOB_NAME, - type UpgradeApplicationsJobData, -} from 'src/engine/core-modules/application/jobs/upgrade-applications.job-constants'; import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; import { type AuthContextUser } from 'src/engine/core-modules/auth/types/auth-context.type'; import { AdminAiModelsDTO } from 'src/engine/core-modules/client-config/client-config.entity'; @@ -153,8 +145,6 @@ export class AdminPanelResolver { private readonly workspaceRepository: Repository, @InjectMessageQueue(MessageQueue.cronQueue) private readonly cronQueueService: MessageQueueService, - @InjectMessageQueue(MessageQueue.workspaceQueue) - private readonly workspaceQueueService: MessageQueueService, ) {} @UseGuards(AdminPanelOrImpersonateGuard) @@ -535,59 +525,6 @@ export class AdminPanelResolver { return this.applicationRegistrationService.updateGlobal(input); } - @UseGuards(AdminPanelGuard) - @Mutation(() => Boolean) - async backfillApplicationInstallation( - @Args('applicationRegistrationId') applicationRegistrationId: string, - ): Promise { - const registration = - await this.applicationRegistrationService.findOneByIdGlobal( - applicationRegistrationId, - ); - - if (!registration.isPreInstalled) { - throw new UserInputError( - 'Only pre-installed apps can be backfilled. Enable pre-install first.', - ); - } - - await this.workspaceQueueService.add( - BACKFILL_APPLICATION_INSTALLATION_JOB_NAME, - { applicationRegistrationId }, - { - id: `${BACKFILL_APPLICATION_INSTALLATION_JOB_NAME}-${applicationRegistrationId}`, - }, // Avoids triggering multiple pending jobs for the same app - ); - - return true; - } - - @UseGuards(AdminPanelGuard) - @Mutation(() => Boolean) - async upgradeRegistrationApplications( - @Args('applicationRegistrationId') applicationRegistrationId: string, - @Args('batchSize', { type: () => Int, nullable: true }) - batchSize?: number, - ): Promise { - await this.applicationRegistrationService.findOneByIdGlobal( - applicationRegistrationId, - ); - - await this.workspaceQueueService.add( - UPGRADE_APPLICATIONS_JOB_NAME, - { - applicationRegistrationId, - onlyAutoUpgrade: false, - ...(isDefined(batchSize) ? { batchSize } : {}), - }, - { - id: `${UPGRADE_APPLICATIONS_JOB_NAME}-${applicationRegistrationId}`, - }, // Avoids triggering multiple pending jobs for the same app - ); - - return true; - } - @UseGuards(AdminPanelGuard) @Query(() => GraphQLJSON) async getAiProviders(): Promise> { diff --git a/packages/twenty-server/src/engine/core-modules/application/__tests__/application-stop.service.spec.ts b/packages/twenty-server/src/engine/core-modules/application/application-stop/__tests__/application-stop.service.spec.ts similarity index 85% rename from packages/twenty-server/src/engine/core-modules/application/__tests__/application-stop.service.spec.ts rename to packages/twenty-server/src/engine/core-modules/application/application-stop/__tests__/application-stop.service.spec.ts index d7351d4fc5..cece36daa2 100644 --- a/packages/twenty-server/src/engine/core-modules/application/__tests__/application-stop.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-stop/__tests__/application-stop.service.spec.ts @@ -3,7 +3,7 @@ import { Test, type TestingModule } from '@nestjs/testing'; import { APPLICATION_KILL_SWITCH_LOCAL_CACHE_TTL_MS, ApplicationStopService, -} from 'src/engine/core-modules/application/application-stop.service'; +} from 'src/engine/core-modules/application/application-stop/application-stop.service'; import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum'; const APPLICATION_UNIVERSAL_IDENTIFIER = 'application-universal-identifier-1'; @@ -14,6 +14,8 @@ describe('ApplicationStopService', () => { const cacheStorageService = { get: jest.fn(), + set: jest.fn(), + del: jest.fn(), }; beforeEach(async () => { @@ -36,6 +38,21 @@ describe('ApplicationStopService', () => { jest.restoreAllMocks(); }); + it('sets the kill switch when stopping an application', async () => { + await applicationStopService.stop(APPLICATION_UNIVERSAL_IDENTIFIER); + + expect(cacheStorageService.set).toHaveBeenCalledWith( + KILL_SWITCH_KEY, + 'stopped', + ); + }); + + it('deletes the kill switch when removing it', async () => { + await applicationStopService.remove(APPLICATION_UNIVERSAL_IDENTIFIER); + + expect(cacheStorageService.del).toHaveBeenCalledWith(KILL_SWITCH_KEY); + }); + it('returns true when the kill switch exists', async () => { cacheStorageService.get.mockResolvedValue('stopped'); diff --git a/packages/twenty-server/src/engine/core-modules/application/application-stop/application-stop.module.ts b/packages/twenty-server/src/engine/core-modules/application/application-stop/application-stop.module.ts new file mode 100644 index 0000000000..c1bc521e6f --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-stop/application-stop.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity'; +import { ApplicationStopService } from 'src/engine/core-modules/application/application-stop/application-stop.service'; +import { ApplicationKillSwitchCommand } from 'src/engine/core-modules/application/application-stop/commands/application-kill-switch.command'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + ApplicationRegistrationEntity, + ApplicationEntity, + ]), + ], + providers: [ApplicationStopService, ApplicationKillSwitchCommand], + exports: [ApplicationStopService], +}) +export class ApplicationStopModule {} diff --git a/packages/twenty-server/src/engine/core-modules/application/application-stop.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-stop/application-stop.service.ts similarity index 82% rename from packages/twenty-server/src/engine/core-modules/application/application-stop.service.ts rename to packages/twenty-server/src/engine/core-modules/application/application-stop/application-stop.service.ts index 0c7d45fe82..fb74145371 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-stop.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-stop/application-stop.service.ts @@ -23,6 +23,19 @@ export class ApplicationStopService { private readonly cacheStorageService: CacheStorageService, ) {} + async stop(applicationUniversalIdentifier: string): Promise { + await this.cacheStorageService.set( + this.getKillSwitchKey(applicationUniversalIdentifier), + 'stopped', + ); + } + + async remove(applicationUniversalIdentifier: string): Promise { + await this.cacheStorageService.del( + this.getKillSwitchKey(applicationUniversalIdentifier), + ); + } + async isApplicationStopped( applicationUniversalIdentifier: string, ): Promise { diff --git a/packages/twenty-server/src/engine/core-modules/application/application-stop/commands/application-kill-switch.command.ts b/packages/twenty-server/src/engine/core-modules/application/application-stop/commands/application-kill-switch.command.ts new file mode 100644 index 0000000000..13a21464ad --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-stop/commands/application-kill-switch.command.ts @@ -0,0 +1,136 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { Command, CommandRunner, Option } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; + +import { CommandLogger } from 'src/database/commands/logger'; +import { askCommandConfirmation } from 'src/database/commands/utils/ask-command-confirmation.util'; +import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity'; +import { ApplicationStopService } from 'src/engine/core-modules/application/application-stop/application-stop.service'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; + +const KILL_SWITCH_ACTIONS = ['stop', 'remove'] as const; + +type KillSwitchAction = (typeof KILL_SWITCH_ACTIONS)[number]; + +type ApplicationKillSwitchCommandOptions = { + applicationUniversalIdentifier: string; + yes?: boolean; +}; + +@Command({ + name: 'application:kill-switch', + arguments: '[action]', + description: + 'Toggle an application kill switch on every workspace: "stop" (default) halts its logic function executions until "remove" clears the switch.', +}) +export class ApplicationKillSwitchCommand extends CommandRunner { + protected logger: CommandLogger; + + constructor( + @InjectRepository(ApplicationRegistrationEntity) + private readonly applicationRegistrationRepository: Repository, + @InjectRepository(ApplicationEntity) + private readonly applicationRepository: Repository, + private readonly applicationStopService: ApplicationStopService, + ) { + super(); + this.logger = new CommandLogger({ + verbose: false, + constructorName: this.constructor.name, + }); + } + + @Option({ + flags: + '-u, --application-universal-identifier ', + description: 'Application universal identifier', + required: true, + }) + parseApplicationUniversalIdentifier(value: string): string { + return value; + } + + @Option({ + flags: '-y, --yes', + description: 'Skip the confirmation prompt (for non-interactive usage)', + required: false, + }) + parseYes(): boolean { + return true; + } + + override async run( + passedParams: string[], + options: ApplicationKillSwitchCommandOptions, + ): Promise { + const action = (passedParams[0] ?? 'stop') as KillSwitchAction; + + if (!KILL_SWITCH_ACTIONS.includes(action)) { + throw new Error( + `Invalid action "${passedParams[0]}". Expected one of: ${KILL_SWITCH_ACTIONS.join(', ')}`, + ); + } + + const registration = await this.applicationRegistrationRepository.findOne({ + where: { + universalIdentifier: options.applicationUniversalIdentifier, + }, + }); + + if (!isDefined(registration)) { + throw new Error( + `Application registration with universal identifier ${options.applicationUniversalIdentifier} not found`, + ); + } + + if (!(options.yes ?? false)) { + const isConfirmed = await this.askForConfirmation( + action, + options.applicationUniversalIdentifier, + registration.id, + ); + + if (!isConfirmed) { + this.logger.log('Aborted, kill switch left unchanged'); + + return; + } + } + + if (action === 'stop') { + await this.applicationStopService.stop( + options.applicationUniversalIdentifier, + ); + } else { + await this.applicationStopService.remove( + options.applicationUniversalIdentifier, + ); + } + + this.logger.log( + `Kill switch ${action === 'stop' ? 'enabled' : 'removed'} for "${registration.name}" (${options.applicationUniversalIdentifier}). Workers pick it up within a minute.`, + ); + + this.logger.log(chalk.blue('Command completed!')); + } + + private async askForConfirmation( + action: KillSwitchAction, + applicationUniversalIdentifier: string, + applicationRegistrationId: string, + ): Promise { + const installationCount = await this.applicationRepository.count({ + where: { applicationRegistrationId }, + }); + + const actionLabel = + action === 'stop' ? 'stopping' : 'removing the kill switch of'; + + return askCommandConfirmation( + `Confirm ${actionLabel} application ${applicationUniversalIdentifier} on ${installationCount} workspace(s)`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.module.ts b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.module.ts index ae047c4bc9..2b71d52c26 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.module.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.module.ts @@ -7,6 +7,7 @@ import { ApplicationRegistrationEntity } from 'src/engine/core-modules/applicati import { ApplicationRegistrationModule } from 'src/engine/core-modules/application/application-registration/application-registration.module'; import { ApplicationUpgradeResolver } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.resolver'; import { ApplicationUpgradeService } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.service'; +import { UpgradeApplicationCommand } from 'src/engine/core-modules/application/application-upgrade/commands/upgrade-application.command'; import { ApplicationVersionCheckCronJob } from 'src/engine/core-modules/application/application-upgrade/crons/application-version-check.cron.job'; import { ApplicationVersionCheckCronCommand } from 'src/engine/core-modules/application/application-upgrade/crons/commands/application-version-check.cron.command'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; @@ -30,6 +31,7 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi ApplicationUpgradeResolver, ApplicationVersionCheckCronJob, ApplicationVersionCheckCronCommand, + UpgradeApplicationCommand, ], exports: [ApplicationUpgradeService, ApplicationVersionCheckCronCommand], }) diff --git a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.service.ts index e56ca837db..b542f62465 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/application-upgrade.service.ts @@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import axios from 'axios'; -import { isDefined } from 'twenty-shared/utils'; -import { Repository } from 'typeorm'; +import { isDefined, isNonEmptyArray } from 'twenty-shared/utils'; +import { In, Repository } from 'typeorm'; import { z } from 'zod'; import { ApplicationInstallService } from 'src/engine/core-modules/application/application-install/application-install.service'; @@ -111,15 +111,21 @@ export class ApplicationUpgradeService { } } - async upgradeAllApplications({ + async findApplicationsToUpgrade({ applicationRegistrationId, onlyAutoUpgrade = false, - batchSize = UPGRADE_APPLICATIONS_DEFAULT_BATCH_SIZE, + workspaceIds, + workspaceCountLimit, }: { applicationRegistrationId: string; onlyAutoUpgrade?: boolean; - batchSize?: number; - }): Promise { + workspaceIds?: string[]; + workspaceCountLimit?: number; + }): Promise<{ + appRegistration: ApplicationRegistrationEntity; + targetVersion: string | null; + applicationsToUpgrade: ApplicationEntity[]; + }> { const appRegistration = await this.appRegistrationRepository.findOneOrFail({ where: { id: applicationRegistrationId }, }); @@ -127,28 +133,56 @@ export class ApplicationUpgradeService { const targetVersion = appRegistration.latestAvailableVersion; if (!isDefined(targetVersion)) { - return; + return { + appRegistration, + targetVersion: null, + applicationsToUpgrade: [], + }; } const applications = await this.applicationRepository.find({ where: { applicationRegistrationId, ...(onlyAutoUpgrade ? { autoUpgrade: true } : {}), + ...(isNonEmptyArray(workspaceIds) + ? { workspaceId: In(workspaceIds) } + : {}), }, }); - const applicationsToUpgrade = applications.filter( + let applicationsToUpgrade = applications.filter( (application) => application.version !== targetVersion, ); + if (isDefined(workspaceCountLimit)) { + applicationsToUpgrade = applicationsToUpgrade.slice( + 0, + workspaceCountLimit, + ); + } + + return { appRegistration, targetVersion, applicationsToUpgrade }; + } + + async upgradeApplications({ + appRegistration, + targetVersion, + applications, + batchSize = UPGRADE_APPLICATIONS_DEFAULT_BATCH_SIZE, + }: { + appRegistration: ApplicationRegistrationEntity; + targetVersion: string; + applications: ApplicationEntity[]; + batchSize?: number; + }): Promise { const sanitizedBatchSize = Math.max(1, Math.floor(batchSize)); for ( let batchStart = 0; - batchStart < applicationsToUpgrade.length; + batchStart < applications.length; batchStart += sanitizedBatchSize ) { - const batch = applicationsToUpgrade.slice( + const batch = applications.slice( batchStart, batchStart + sanitizedBatchSize, ); @@ -172,6 +206,39 @@ export class ApplicationUpgradeService { } } + async upgradeAllApplications({ + applicationRegistrationId, + onlyAutoUpgrade = false, + batchSize = UPGRADE_APPLICATIONS_DEFAULT_BATCH_SIZE, + workspaceIds, + workspaceCountLimit, + }: { + applicationRegistrationId: string; + onlyAutoUpgrade?: boolean; + batchSize?: number; + workspaceIds?: string[]; + workspaceCountLimit?: number; + }): Promise { + const { appRegistration, targetVersion, applicationsToUpgrade } = + await this.findApplicationsToUpgrade({ + applicationRegistrationId, + onlyAutoUpgrade, + workspaceIds, + workspaceCountLimit, + }); + + if (!isDefined(targetVersion)) { + return; + } + + await this.upgradeApplications({ + appRegistration, + targetVersion, + applications: applicationsToUpgrade, + batchSize, + }); + } + async upgradeApplication(params: { appRegistrationId: string; targetVersion: string; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-upgrade/commands/upgrade-application.command.ts b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/commands/upgrade-application.command.ts new file mode 100644 index 0000000000..07c777bedf --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-upgrade/commands/upgrade-application.command.ts @@ -0,0 +1,220 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { Command, CommandRunner, Option } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; + +import { CommandLogger } from 'src/database/commands/logger'; +import { askCommandConfirmation } from 'src/database/commands/utils/ask-command-confirmation.util'; +import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity'; +import { ApplicationUpgradeService } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.service'; + +type UpgradeApplicationCommandOptions = { + applicationRegistrationUniversalIdentifier: string; + batchSize?: number; + workspaceId?: Set; + workspaceCountLimit?: number; + dryRun?: boolean; + yes?: boolean; +}; + +const MAX_BATCH_SIZE = 50; +const MAX_WORKSPACE_COUNT_LIMIT = 50; + +const parseBoundedPositiveInteger = ( + value: string, + optionName: string, + maximum: number, +): number => { + const parsedValue = Number(value); + + if (!Number.isInteger(parsedValue) || parsedValue < 1) { + throw new Error( + `Invalid ${optionName} "${value}". Expected a positive integer`, + ); + } + + if (parsedValue > maximum) { + throw new Error(`Invalid ${optionName} "${value}". Maximum is ${maximum}`); + } + + return parsedValue; +}; + +@Command({ + name: 'application:upgrade', + description: + 'Upgrade an application to its latest available version on every workspace that already has it installed', +}) +export class UpgradeApplicationCommand extends CommandRunner { + protected logger: CommandLogger; + + constructor( + @InjectRepository(ApplicationRegistrationEntity) + private readonly applicationRegistrationRepository: Repository, + private readonly applicationUpgradeService: ApplicationUpgradeService, + ) { + super(); + this.logger = new CommandLogger({ + verbose: false, + constructorName: this.constructor.name, + }); + } + + @Option({ + flags: + '-u, --application-registration-universal-identifier ', + description: 'Application registration universal identifier', + required: true, + }) + parseApplicationRegistrationUniversalIdentifier(value: string): string { + return value; + } + + @Option({ + flags: '-b, --batch-size ', + description: `Number of workspaces upgraded in parallel (defaults to 5, max ${MAX_BATCH_SIZE})`, + required: false, + }) + parseBatchSize(value: string): number { + return parseBoundedPositiveInteger(value, 'batch size', MAX_BATCH_SIZE); + } + + @Option({ + flags: '-w, --workspace-id ', + description: + 'Only upgrade the given workspace id. Can be repeated to target several workspaces. Upgrades all workspaces if not provided.', + required: false, + }) + parseWorkspaceId(value: string, previous?: Set): Set { + const accumulator = previous ?? new Set(); + + accumulator.add(value); + + return accumulator; + } + + @Option({ + flags: '--workspace-count-limit ', + description: `Limit the number of workspaces to upgrade (max ${MAX_WORKSPACE_COUNT_LIMIT})`, + required: false, + }) + parseWorkspaceCountLimit(value: string): number { + return parseBoundedPositiveInteger( + value, + 'workspace count limit', + MAX_WORKSPACE_COUNT_LIMIT, + ); + } + + @Option({ + flags: '-d, --dry-run', + description: 'List the workspaces that would be upgraded without upgrading', + required: false, + }) + parseDryRun(): boolean { + return true; + } + + @Option({ + flags: '-y, --yes', + description: 'Skip the confirmation prompt (for non-interactive usage)', + required: false, + }) + parseYes(): boolean { + return true; + } + + override async run( + _passedParams: string[], + options: UpgradeApplicationCommandOptions, + ): Promise { + const registration = await this.applicationRegistrationRepository.findOne({ + where: { + universalIdentifier: options.applicationRegistrationUniversalIdentifier, + }, + }); + + if (!isDefined(registration)) { + throw new Error( + `Application registration with universal identifier ${options.applicationRegistrationUniversalIdentifier} not found`, + ); + } + + const workspaceIds = isDefined(options.workspaceId) + ? Array.from(options.workspaceId) + : undefined; + + const { appRegistration, targetVersion, applicationsToUpgrade } = + await this.applicationUpgradeService.findApplicationsToUpgrade({ + applicationRegistrationId: registration.id, + onlyAutoUpgrade: false, + workspaceIds, + workspaceCountLimit: options.workspaceCountLimit, + }); + + if (!isDefined(targetVersion)) { + this.logger.warn( + `Application "${registration.name}" (${registration.universalIdentifier}) has no latest available version, nothing to upgrade`, + ); + + return; + } + + const impactedWorkspaceIds = applicationsToUpgrade.map( + (application) => application.workspaceId, + ); + + if (options.dryRun ?? false) { + this.logger.log( + `[DRY RUN] Would upgrade "${registration.name}" (${registration.universalIdentifier}) to version ${targetVersion} on ${impactedWorkspaceIds.length} workspace(s)${ + impactedWorkspaceIds.length > 0 + ? `: ${impactedWorkspaceIds.join(', ')}` + : '' + }`, + ); + + return; + } + + if (impactedWorkspaceIds.length === 0) { + this.logger.log( + `No workspace to upgrade, every targeted installation of "${registration.name}" already runs version ${targetVersion}`, + ); + + return; + } + + if (!(options.yes ?? false)) { + const confirmationTarget = isDefined(workspaceIds) + ? `workspace(s) ${workspaceIds.join(', ')}` + : `${impactedWorkspaceIds.length} workspace(s)`; + + const isConfirmed = await askCommandConfirmation( + `Confirm upgrading application ${registration.universalIdentifier} to version ${targetVersion} on ${confirmationTarget}`, + ); + + if (!isConfirmed) { + this.logger.log('Aborted, no upgrade performed'); + + return; + } + } + + this.logger.log( + `Upgrading "${registration.name}" (${registration.universalIdentifier}) to version ${targetVersion} on ${impactedWorkspaceIds.length} workspace(s)...`, + ); + + // Runs on the exact set shown at confirmation time, so installations + // created or versions published while the operator answered are excluded. + await this.applicationUpgradeService.upgradeApplications({ + appRegistration, + targetVersion, + applications: applicationsToUpgrade, + batchSize: options.batchSize, + }); + + this.logger.log(chalk.blue('Command completed!')); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/application/application.module.ts b/packages/twenty-server/src/engine/core-modules/application/application.module.ts index 549f55b146..be0124c363 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application.module.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application.module.ts @@ -3,7 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity'; import { ApplicationGaugeService } from 'src/engine/core-modules/application/application-gauge.service'; -import { ApplicationStopService } from 'src/engine/core-modules/application/application-stop.service'; +import { ApplicationStopModule } from 'src/engine/core-modules/application/application-stop/application-stop.module'; import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { ApplicationResolver } from 'src/engine/core-modules/application/application.resolver'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; @@ -35,6 +35,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache ObjectMetadataEntity, ApplicationVariableEntity, ]), + ApplicationStopModule, WorkspaceManyOrAllFlatEntityMapsCacheModule, WorkspaceCacheModule, TwentyConfigModule, @@ -43,13 +44,12 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache ], exports: [ ApplicationService, - ApplicationStopService, + ApplicationStopModule, WorkspaceFlatApplicationMapCacheService, ], providers: [ ApplicationResolver, ApplicationService, - ApplicationStopService, ApplicationGaugeService, WorkspaceFlatApplicationMapCacheService, provideWorkspaceScopedRepository(AgentEntity), diff --git a/packages/twenty-server/src/engine/core-modules/application/application.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/application.resolver.ts index 33b8978224..999aaffc5f 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application.resolver.ts @@ -5,6 +5,7 @@ import { isAbsoluteUrl, isDefined } from 'twenty-shared/utils'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { ApplicationStopService } from 'src/engine/core-modules/application/application-stop/application-stop.service'; import { ApplicationDTO } from 'src/engine/core-modules/application/dtos/application.dto'; import { SdkClientChecksumsDTO } from 'src/engine/core-modules/sdk-client/dtos/sdk-client-checksums.dto'; import { getInstalledSdkMetadataModule } from 'src/engine/core-modules/sdk-client/utils/get-installed-sdk-metadata-module.util'; @@ -21,6 +22,7 @@ export class ApplicationResolver { constructor( private readonly twentyConfigService: TwentyConfigService, private readonly workspaceCacheService: WorkspaceCacheService, + private readonly applicationStopService: ApplicationStopService, ) {} @Query(() => SdkClientChecksumsDTO, { nullable: true }) @@ -46,6 +48,19 @@ export class ApplicationResolver { }; } + // Surfaces the kill switch so clients can warn users that the app is + // temporarily stopped and behaving in a degraded way. Kept as a dedicated + // query so listing applications does not trigger one Redis read per app. + @Query(() => Boolean) + async isApplicationStopped( + @Args('applicationUniversalIdentifier') + applicationUniversalIdentifier: string, + ): Promise { + return this.applicationStopService.isApplicationStopped( + applicationUniversalIdentifier, + ); + } + // Resolves the display url of the logo bundled in the installed // application's public assets, so clients never build file urls themselves. @ResolveField(() => String, { nullable: true }) diff --git a/packages/twenty-server/src/engine/core-modules/application/jobs/backfill-application-installation.job-constants.ts b/packages/twenty-server/src/engine/core-modules/application/jobs/backfill-application-installation.job-constants.ts deleted file mode 100644 index 9ae1341c66..0000000000 --- a/packages/twenty-server/src/engine/core-modules/application/jobs/backfill-application-installation.job-constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const BACKFILL_APPLICATION_INSTALLATION_JOB_NAME = - 'BackfillApplicationInstallationJob'; - -export type BackfillApplicationInstallationJobData = { - applicationRegistrationId: string; -}; diff --git a/packages/twenty-server/src/engine/core-modules/application/jobs/backfill-application-installation.job.ts b/packages/twenty-server/src/engine/core-modules/application/jobs/backfill-application-installation.job.ts deleted file mode 100644 index eea8520109..0000000000 --- a/packages/twenty-server/src/engine/core-modules/application/jobs/backfill-application-installation.job.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator'; -import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; -import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; -import { - BACKFILL_APPLICATION_INSTALLATION_JOB_NAME, - type BackfillApplicationInstallationJobData, -} from 'src/engine/core-modules/application/jobs/backfill-application-installation.job-constants'; -import { PreInstalledAppsService } from 'src/engine/core-modules/application/pre-installed-apps/pre-installed-apps.service'; - -@Processor(MessageQueue.workspaceQueue) -export class BackfillApplicationInstallationJob { - constructor( - private readonly preInstalledAppsService: PreInstalledAppsService, - ) {} - - @Process(BACKFILL_APPLICATION_INSTALLATION_JOB_NAME) - async handle(data: BackfillApplicationInstallationJobData): Promise { - await this.preInstalledAppsService.backfillApplicationOnAllWorkspaces( - data.applicationRegistrationId, - ); - } -} diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts index d29e54b9ef..236e6ddc3d 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts @@ -21,7 +21,7 @@ import { import { buildApplicationLogEnvelopes } from 'src/engine/core-modules/event-logs/producers/application-log/build-application-log-envelopes'; import { parseApplicationLogLines } from 'src/engine/core-modules/event-logs/producers/application-log/parse-application-log-lines'; import { ApplicationRegistrationVariableEntity } from 'src/engine/core-modules/application/application-registration-variable/application-registration-variable.entity'; -import { ApplicationStopService } from 'src/engine/core-modules/application/application-stop.service'; +import { ApplicationStopService } from 'src/engine/core-modules/application/application-stop/application-stop.service'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import type { FlatApplicationVariable } from 'src/engine/metadata-modules/flat-application-variable/types/flat-application-variable.type'; import { FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; diff --git a/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts b/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts index adc65639e5..9e657a94e2 100644 --- a/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts +++ b/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts @@ -15,9 +15,7 @@ import { StripeModule } from 'src/engine/core-modules/billing/stripe/stripe.modu import { ApplicationInstallModule } from 'src/engine/core-modules/application/application-install/application-install.module'; import { ApplicationRegistrationModule } from 'src/engine/core-modules/application/application-registration/application-registration.module'; import { ApplicationUpgradeModule } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.module'; -import { BackfillApplicationInstallationJob } from 'src/engine/core-modules/application/jobs/backfill-application-installation.job'; import { UpgradeApplicationsJob } from 'src/engine/core-modules/application/jobs/upgrade-applications.job'; -import { PreInstalledAppsModule } from 'src/engine/core-modules/application/pre-installed-apps/pre-installed-apps.module'; import { InstallOnboardingAppsJob } from 'src/engine/core-modules/onboarding/jobs/install-onboarding-apps.job'; import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module'; import { EmailSenderJob } from 'src/engine/core-modules/email/email-sender.job'; @@ -94,7 +92,6 @@ import { WorkflowModule } from 'src/modules/workflow/workflow.module'; LogicFunctionModule, EnterpriseModule, EmailingModule, - PreInstalledAppsModule, ApplicationInstallModule, ApplicationRegistrationModule, ApplicationUpgradeModule, @@ -114,7 +111,6 @@ import { WorkflowModule } from 'src/modules/workflow/workflow.module'; CleanWorkspaceDeletionWarningUserVarsJob, UpdateWorkspaceMemberEmailJob, GenerateSdkClientJob, - BackfillApplicationInstallationJob, UpgradeApplicationsJob, InstallOnboardingAppsJob, ],