Files
twenty/packages/twenty-server/src/database/commands/cron-register-all.command.ts
T
neo773 c04714b9e0 fix(messaging): register and harden webhook subscription renewal cron (#23006)
The renewal cron was never wired into cron:register:all, so
Gmail/Calendar/Graph watches were never renewed and went dark ~7 days
after connect (the max watch lifetime all three providers allow).
Register it, fan renewals out as per-channel queue jobs scoped to active
workspaces, retry FAILED channels, and recreate Google Calendar watches
before stopping the old one.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23006?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-20 17:26:47 +05:30

249 lines
12 KiB
TypeScript

import { Logger } from '@nestjs/common';
import { Command, CommandRunner } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { MarketplaceCatalogSyncCronCommand } from 'src/engine/core-modules/application/application-marketplace/crons/commands/marketplace-catalog-sync.cron.command';
import { StaleRegistrationCleanupCronCommand } from 'src/engine/core-modules/application/application-oauth/stale-registration-cleanup/commands/stale-registration-cleanup.cron.command';
import { ApplicationVersionCheckCronCommand } from 'src/engine/core-modules/application/application-upgrade/crons/commands/application-version-check.cron.command';
import { BillingReminderCronCommand } from 'src/engine/core-modules/billing/reminders/crons/commands/billing-reminder.cron.command';
import { EnterpriseKeyValidationCronCommand } from 'src/engine/core-modules/enterprise/cron/command/enterprise-key-validation.cron.command';
import { EventLogCleanupCronCommand } from 'src/engine/core-modules/event-logs/cleanup/commands/event-log-cleanup.cron.command';
import { PendingFileCleanupCronCommand } from 'src/engine/core-modules/file/file-upload/crons/commands/pending-file-cleanup.cron.command';
import { RotateSigningKeysCronCommand } from 'src/engine/core-modules/jwt/crons/commands/rotate-signing-keys.cron.command';
import { CronTriggerCronCommand } from 'src/engine/core-modules/logic-function/logic-function-trigger/triggers/cron/cron-trigger.cron.command';
import { CheckPublicDomainsValidRecordsCronCommand } from 'src/engine/core-modules/public-domain/crons/commands/check-public-domains-valid-records.cron.command';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { CheckCustomDomainValidRecordsCronCommand } from 'src/engine/core-modules/workspace/crons/commands/check-custom-domain-valid-records.cron.command';
import { WebhookSubscriptionRenewalCronCommand } from 'src/modules/connected-account/webhook-subscription-manager/crons/commands/webhook-subscription-renewal.cron.command';
import { TrashCleanupCronCommand } from 'src/engine/trash-cleanup/commands/trash-cleanup.cron.command';
import { CleanOnboardingWorkspacesCronCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-onboarding-workspaces.cron.command';
import { CleanSuspendedWorkspacesCronCommand } from 'src/engine/workspace-manager/workspace-cleaner/commands/clean-suspended-workspaces.cron.command';
import { CalendarEventListFetchCronCommand } from 'src/modules/calendar/calendar-event-import-manager/crons/commands/calendar-event-list-fetch.cron.command';
import { CalendarEventsImportCronCommand } from 'src/modules/calendar/calendar-event-import-manager/crons/commands/calendar-import.cron.command';
import { CalendarOngoingStaleCronCommand } from 'src/modules/calendar/calendar-event-import-manager/crons/commands/calendar-ongoing-stale.cron.command';
import { CalendarRelaunchFailedCalendarChannelsCronCommand } from 'src/modules/calendar/calendar-event-import-manager/crons/commands/calendar-relaunch-failed-calendar-channels.cron.command';
import { MessagingMessageListFetchCronCommand } from 'src/modules/messaging/message-import-manager/crons/commands/messaging-message-list-fetch.cron.command';
import { MessagingMessagesImportCronCommand } from 'src/modules/messaging/message-import-manager/crons/commands/messaging-messages-import.cron.command';
import { MessagingOngoingStaleCronCommand } from 'src/modules/messaging/message-import-manager/crons/commands/messaging-ongoing-stale.cron.command';
import { MessagingRelaunchFailedMessageChannelsCronCommand } from 'src/modules/messaging/message-import-manager/crons/commands/messaging-relaunch-failed-message-channels.cron.command';
import { WorkflowCleanWorkflowRunsCronCommand } from 'src/modules/workflow/workflow-runner/workflow-run-queue/cron/command/workflow-clean-workflow-runs.cron.command';
import { WorkflowHandleStaledRunsCronCommand } from 'src/modules/workflow/workflow-runner/workflow-run-queue/cron/command/workflow-handle-staled-runs.cron.command';
import { WorkflowRunEnqueueCronCommand } from 'src/modules/workflow/workflow-runner/workflow-run-queue/cron/command/workflow-run-enqueue.cron.command';
import { WorkflowCronTriggerCronCommand } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/commands/workflow-cron-trigger.cron.command';
@Command({
name: 'cron:register:all',
description: 'Register all background sync cron jobs',
})
export class CronRegisterAllCommand extends CommandRunner {
private readonly logger = new Logger(CronRegisterAllCommand.name);
constructor(
private readonly messagingMessagesImportCronCommand: MessagingMessagesImportCronCommand,
private readonly messagingMessageListFetchCronCommand: MessagingMessageListFetchCronCommand,
private readonly messagingOngoingStaleCronCommand: MessagingOngoingStaleCronCommand,
private readonly messagingRelaunchFailedMessageChannelsCronCommand: MessagingRelaunchFailedMessageChannelsCronCommand,
private readonly calendarEventListFetchCronCommand: CalendarEventListFetchCronCommand,
private readonly calendarEventsImportCronCommand: CalendarEventsImportCronCommand,
private readonly calendarOngoingStaleCronCommand: CalendarOngoingStaleCronCommand,
private readonly calendarRelaunchFailedCalendarChannelsCronCommand: CalendarRelaunchFailedCalendarChannelsCronCommand,
private readonly webhookSubscriptionRenewalCronCommand: WebhookSubscriptionRenewalCronCommand,
private readonly workflowCronTriggerCronCommand: WorkflowCronTriggerCronCommand,
private readonly workflowRunEnqueueCronCommand: WorkflowRunEnqueueCronCommand,
private readonly workflowHandleStaledRunsCronCommand: WorkflowHandleStaledRunsCronCommand,
private readonly workflowCleanWorkflowRunsCronCommand: WorkflowCleanWorkflowRunsCronCommand,
private readonly checkCustomDomainValidRecordsCronCommand: CheckCustomDomainValidRecordsCronCommand,
private readonly checkPublicDomainsValidRecordsCronCommand: CheckPublicDomainsValidRecordsCronCommand,
private readonly cronTriggerCronCommand: CronTriggerCronCommand,
private readonly cleanSuspendedWorkspacesCronCommand: CleanSuspendedWorkspacesCronCommand,
private readonly cleanOnboardingWorkspacesCronCommand: CleanOnboardingWorkspacesCronCommand,
private readonly trashCleanupCronCommand: TrashCleanupCronCommand,
private readonly eventLogCleanupCronCommand: EventLogCleanupCronCommand,
private readonly enterpriseKeyValidationCronCommand: EnterpriseKeyValidationCronCommand,
private readonly rotateSigningKeysCronCommand: RotateSigningKeysCronCommand,
private readonly marketplaceCatalogSyncCronCommand: MarketplaceCatalogSyncCronCommand,
private readonly applicationVersionCheckCronCommand: ApplicationVersionCheckCronCommand,
private readonly staleRegistrationCleanupCronCommand: StaleRegistrationCleanupCronCommand,
private readonly pendingFileCleanupCronCommand: PendingFileCleanupCronCommand,
private readonly billingReminderCronCommand: BillingReminderCronCommand,
private readonly twentyConfigService: TwentyConfigService,
) {
super();
}
async run(): Promise<void> {
this.logger.log('Registering all background sync cron jobs...');
const isSigningKeyAutoRotationEnabled = isDefined(
this.twentyConfigService.get('SIGNING_KEY_ROTATION_DAYS'),
);
const isMarketplaceCatalogSyncEnabled = this.twentyConfigService.get(
'MARKETPLACE_CATALOG_SYNC_CRON_ENABLED',
);
const isBillingEnabled = this.twentyConfigService.get('IS_BILLING_ENABLED');
const allCommands = [
{
name: 'MessagingMessagesImport',
command: this.messagingMessagesImportCronCommand,
},
{
name: 'MessagingMessageListFetch',
command: this.messagingMessageListFetchCronCommand,
},
{
name: 'MessagingOngoingStale',
command: this.messagingOngoingStaleCronCommand,
},
{
name: 'MessagingRelaunchFailedMessageChannels',
command: this.messagingRelaunchFailedMessageChannelsCronCommand,
},
{
name: 'CalendarEventListFetch',
command: this.calendarEventListFetchCronCommand,
},
{
name: 'CalendarEventsImport',
command: this.calendarEventsImportCronCommand,
},
{
name: 'CalendarOngoingStale',
command: this.calendarOngoingStaleCronCommand,
},
{
name: 'CalendarRelaunchFailedCalendarChannels',
command: this.calendarRelaunchFailedCalendarChannelsCronCommand,
},
{
name: 'WebhookSubscriptionRenewal',
command: this.webhookSubscriptionRenewalCronCommand,
},
{
name: 'CheckCustomDomainValidRecords',
command: this.checkCustomDomainValidRecordsCronCommand,
},
{
name: 'CheckPublicDomainsValidRecords',
command: this.checkPublicDomainsValidRecordsCronCommand,
},
{
name: 'WorkflowCronTrigger',
command: this.workflowCronTriggerCronCommand,
},
{
name: 'WorkflowRunEnqueue',
command: this.workflowRunEnqueueCronCommand,
},
{
name: 'WorkflowHandleStaledRuns',
command: this.workflowHandleStaledRunsCronCommand,
},
{
name: 'WorkflowCleanWorkflowRuns',
command: this.workflowCleanWorkflowRunsCronCommand,
},
{
name: 'CronTrigger',
command: this.cronTriggerCronCommand,
},
{
name: 'CleanSuspendedWorkspaces',
command: this.cleanSuspendedWorkspacesCronCommand,
},
{
name: 'CleanOnboardingWorkspaces',
command: this.cleanOnboardingWorkspacesCronCommand,
},
{
name: 'TrashCleanup',
command: this.trashCleanupCronCommand,
},
{
name: 'EventLogCleanup',
command: this.eventLogCleanupCronCommand,
},
{
name: 'MarketplaceCatalogSync',
command: this.marketplaceCatalogSyncCronCommand,
isEnabled: isMarketplaceCatalogSyncEnabled,
},
{
name: 'ApplicationVersionCheck',
command: this.applicationVersionCheckCronCommand,
},
{
name: 'EnterpriseKeyValidation',
command: this.enterpriseKeyValidationCronCommand,
},
{
name: 'RotateSigningKeys',
command: this.rotateSigningKeysCronCommand,
isEnabled: isSigningKeyAutoRotationEnabled,
},
{
name: 'StaleRegistrationCleanup',
command: this.staleRegistrationCleanupCronCommand,
},
{
name: 'PendingFileCleanup',
command: this.pendingFileCleanupCronCommand,
},
{
name: 'BillingReminder',
command: this.billingReminderCronCommand,
isEnabled: isBillingEnabled,
},
];
let successCount = 0;
let failureCount = 0;
const failures: string[] = [];
const successes: string[] = [];
const skipped: string[] = [];
for (const { name, command, isEnabled = true } of allCommands) {
if (!isEnabled) {
this.logger.log(`Skipping ${name} cron job (disabled by config)`);
skipped.push(name);
continue;
}
try {
this.logger.log(`Registering ${name} cron job...`);
await command.run();
this.logger.log(`Successfully registered ${name} cron job`);
successCount++;
successes.push(name);
} catch (error) {
this.logger.error(`Failed to register ${name} cron job:`, error);
failureCount++;
failures.push(name);
}
}
this.logger.log(
`Cron job registration completed: ${successCount} successful, ${failureCount} failed, ${skipped.length} skipped`,
);
if (failures.length > 0) {
this.logger.warn(`Failed commands: ${failures.join(', ')}`);
}
if (successCount > 0) {
this.logger.log(`Successful commands: ${successes.join(', ')}`);
}
if (skipped.length > 0) {
this.logger.log(`Skipped commands: ${skipped.join(', ')}`);
}
}
}