fix(emailing-domain): shorten suppression unique constraint to fit 63-char limit

The constraint name exceeded Postgres' 63-char identifier limit, so it was
truncated in the DB and migrate:generate kept emitting a rename (CI drift).
Shorten it, regenerate the migration off a main baseline, and simplify
suppress() back to an idempotent upsert with an empty-address guard.
This commit is contained in:
neo773
2026-05-29 18:54:48 +05:30
parent b5f865f0b3
commit da38aec641
4 changed files with 19 additions and 28 deletions
@@ -3,13 +3,13 @@ import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
@RegisteredInstanceCommand('2.9.0', 1780050277781)
@RegisteredInstanceCommand('2.9.0', 1780060974610)
export class AddEmailGroupSuppressedRecipientFastInstanceCommand implements FastInstanceCommand {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('CREATE TYPE "core"."emailGroupSuppressedRecipient_scope_enum" AS ENUM(\'GLOBAL\', \'CAMPAIGN\')');
await queryRunner.query('CREATE TYPE "core"."emailGroupSuppressedRecipient_reason_enum" AS ENUM(\'HARD_BOUNCE\', \'COMPLAINT\', \'UNSUBSCRIBE\')');
await queryRunner.query('CREATE TYPE "core"."emailGroupSuppressedRecipient_createdbysource_enum" AS ENUM(\'EMAIL\', \'CALENDAR\', \'WORKFLOW\', \'AGENT\', \'API\', \'IMPORT\', \'MANUAL\', \'SYSTEM\', \'WEBHOOK\', \'APPLICATION\')');
await queryRunner.query('CREATE TABLE "core"."emailGroupSuppressedRecipient" ("workspaceId" uuid NOT NULL, "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "emailAddress" character varying NOT NULL, "scope" "core"."emailGroupSuppressedRecipient_scope_enum" NOT NULL, "reason" "core"."emailGroupSuppressedRecipient_reason_enum" NOT NULL, "isSuppressed" boolean NOT NULL DEFAULT true, "providerEventId" character varying, "createdBySource" "core"."emailGroupSuppressedRecipient_createdbysource_enum" NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "IDX_EMAIL_GROUP_SUPPRESSED_RECIPIENT_WORKSPACE_EMAIL_SCOPE_UNIQUE" UNIQUE ("workspaceId", "emailAddress", "scope"), CONSTRAINT "PK_55b0607e539d7941cbaedf1328a" PRIMARY KEY ("id"))');
await queryRunner.query('CREATE TABLE "core"."emailGroupSuppressedRecipient" ("workspaceId" uuid NOT NULL, "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "emailAddress" character varying NOT NULL, "scope" "core"."emailGroupSuppressedRecipient_scope_enum" NOT NULL, "reason" "core"."emailGroupSuppressedRecipient_reason_enum" NOT NULL, "isSuppressed" boolean NOT NULL DEFAULT true, "providerEventId" character varying, "createdBySource" "core"."emailGroupSuppressedRecipient_createdbysource_enum" NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "IDX_EMAIL_GROUP_SUPPRESSED_RECIPIENT_WS_EMAIL_SCOPE_UNIQUE" UNIQUE ("workspaceId", "emailAddress", "scope"), CONSTRAINT "PK_55b0607e539d7941cbaedf1328a" PRIMARY KEY ("id"))');
await queryRunner.query('ALTER TABLE "core"."emailGroupSuppressedRecipient" ADD CONSTRAINT "FK_866066f1e73b748f917fd6fcd80" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION');
}
@@ -58,7 +58,7 @@ import { DropFieldMetadataIsUniqueColumnFastInstanceCommand } from 'src/database
import { EmailingDomainTenantStatusAndGlobalUniquenessFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-fast-1799000020000-emailing-domain-tenant-status-and-global-uniqueness';
import { EncryptNonSecretApplicationVariableSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-slow-1798400000000-encrypt-non-secret-application-variable';
import { MigrateAiModelPreferencesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-slow-1799000010000-migrate-ai-model-preferences';
import { AddEmailGroupSuppressedRecipientFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-fast-1780050277781-add-email-group-suppressed-recipient';
import { AddEmailGroupSuppressedRecipientFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-9/2-9-instance-command-fast-1780060974610-add-email-group-suppressed-recipient';
export const INSTANCE_COMMANDS = [
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
@@ -14,7 +14,7 @@ import { EmailGroupSuppressionScope } from 'src/engine/core-modules/emailing-dom
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
@Entity({ name: 'emailGroupSuppressedRecipient', schema: 'core' })
@Unique('IDX_EMAIL_GROUP_SUPPRESSED_RECIPIENT_WORKSPACE_EMAIL_SCOPE_UNIQUE', [
@Unique('IDX_EMAIL_GROUP_SUPPRESSED_RECIPIENT_WS_EMAIL_SCOPE_UNIQUE', [
'workspaceId',
'emailAddress',
'scope',
@@ -1,7 +1,8 @@
import { Injectable } from '@nestjs/common';
import { isNonEmptyString } from '@sniptt/guards';
import { FieldActorSource } from 'twenty-shared/types';
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
import { isNonEmptyArray } from 'twenty-shared/utils';
import { In } from 'typeorm';
import { BLOCKED_SCOPES_BY_SEND_TYPE } from 'src/engine/core-modules/emailing-domain/constants/blocked-scopes-by-send-type.constant';
@@ -66,32 +67,22 @@ export class EmailGroupSuppressionService {
providerEventId = null,
}: SuppressRecipientArgs): Promise<void> {
const normalizedEmailAddress = emailAddress.trim().toLowerCase();
const scope = SUPPRESSION_SCOPE_BY_REASON[reason];
const existingSuppression =
await this.suppressedRecipientRepository.findOne(workspaceId, {
where: { emailAddress: normalizedEmailAddress, scope },
});
if (isDefined(existingSuppression)) {
if (!existingSuppression.isSuppressed) {
await this.suppressedRecipientRepository.update(
workspaceId,
{ id: existingSuppression.id },
{ isSuppressed: true },
);
}
if (!isNonEmptyString(normalizedEmailAddress)) {
return;
}
await this.suppressedRecipientRepository.save(workspaceId, {
emailAddress: normalizedEmailAddress,
scope,
reason,
isSuppressed: true,
createdBySource,
providerEventId,
});
await this.suppressedRecipientRepository.upsert(
workspaceId,
{
emailAddress: normalizedEmailAddress,
scope: SUPPRESSION_SCOPE_BY_REASON[reason],
reason,
isSuppressed: true,
createdBySource,
providerEventId,
},
['workspaceId', 'emailAddress', 'scope'],
);
}
}