fix(tenants): clearer duplicate slug/domain errors on create
Pre-check uniqueness and report which field conflicts with which existing version; strip primary domain from aliases. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -69,16 +69,23 @@ export class TenantsService {
|
||||
}
|
||||
|
||||
async create(dto: CreateTenantDto, userId?: string) {
|
||||
const aliases = this.uniqueNormalizedHosts(dto.domains);
|
||||
const slug = dto.slug.toLowerCase().trim();
|
||||
const domain = this.normalizeDomain(dto.domain);
|
||||
const aliases = this.uniqueNormalizedHosts(dto.domains).filter(
|
||||
(host) => this.hostKey(host) !== this.hostKey(domain),
|
||||
);
|
||||
|
||||
await this.assertSlugDomainAvailable({ slug, domain });
|
||||
|
||||
try {
|
||||
const tenant = await this.prisma.$transaction(async (tx: any) => {
|
||||
const created = await tx.tenant.create({
|
||||
data: {
|
||||
product: dto.product,
|
||||
slug: dto.slug.toLowerCase(),
|
||||
slug,
|
||||
nameFa: dto.nameFa,
|
||||
nameEn: dto.nameEn,
|
||||
domain: this.normalizeDomain(dto.domain),
|
||||
domain,
|
||||
domains: aliases as unknown as Prisma.InputJsonValue,
|
||||
avatarUrl: dto.avatarUrl,
|
||||
primaryColor: dto.primaryColor,
|
||||
@@ -140,8 +147,23 @@ export class TenantsService {
|
||||
throw new NotFoundException('نسخه یافت نشد');
|
||||
}
|
||||
|
||||
const nextAliases =
|
||||
const nextDomain =
|
||||
dto.domain !== undefined ? this.normalizeDomain(dto.domain) : before.domain;
|
||||
const nextAliasesRaw =
|
||||
dto.domains !== undefined ? this.uniqueNormalizedHosts(dto.domains) : undefined;
|
||||
const nextAliases =
|
||||
nextAliasesRaw !== undefined
|
||||
? nextAliasesRaw.filter((host) => this.hostKey(host) !== this.hostKey(nextDomain))
|
||||
: undefined;
|
||||
|
||||
if (dto.slug !== undefined || dto.domain !== undefined) {
|
||||
await this.assertSlugDomainAvailable({
|
||||
slug: dto.slug !== undefined ? dto.slug.toLowerCase().trim() : undefined,
|
||||
domain: dto.domain !== undefined ? nextDomain : undefined,
|
||||
excludeId: id,
|
||||
});
|
||||
}
|
||||
|
||||
const aliasesChanged =
|
||||
nextAliases !== undefined &&
|
||||
JSON.stringify(this.uniqueNormalizedHosts(this.readDomains(before.domains))) !==
|
||||
@@ -381,11 +403,52 @@ export class TenantsService {
|
||||
return `${Date.now()}`;
|
||||
}
|
||||
|
||||
private async assertSlugDomainAvailable(opts: {
|
||||
slug?: string;
|
||||
domain?: string;
|
||||
excludeId?: string;
|
||||
}) {
|
||||
if (opts.slug) {
|
||||
const bySlug = await this.prisma.tenant.findUnique({
|
||||
where: { slug: opts.slug },
|
||||
select: { id: true, nameFa: true, domain: true, slug: true },
|
||||
});
|
||||
if (bySlug && bySlug.id !== opts.excludeId) {
|
||||
throw new ConflictException(
|
||||
`slug «${opts.slug}» قبلاً برای نسخه «${bySlug.nameFa}» (${bySlug.domain}) ثبت شده است`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.domain) {
|
||||
const byDomain = await this.prisma.tenant.findUnique({
|
||||
where: { domain: opts.domain },
|
||||
select: { id: true, nameFa: true, domain: true, slug: true },
|
||||
});
|
||||
if (byDomain && byDomain.id !== opts.excludeId) {
|
||||
throw new ConflictException(
|
||||
`دامنه «${opts.domain}» قبلاً دامنهٔ اصلی نسخه «${byDomain.nameFa}» (slug: ${byDomain.slug}) است`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleUnique(error: unknown) {
|
||||
if (
|
||||
error instanceof Prisma.PrismaClientKnownRequestError &&
|
||||
error.code === 'P2002'
|
||||
) {
|
||||
const targets = Array.isArray(error.meta?.target)
|
||||
? (error.meta?.target as string[])
|
||||
: [];
|
||||
if (targets.includes('slug')) {
|
||||
throw new ConflictException('slug تکراری است — این slug قبلاً برای نسخه دیگری ثبت شده');
|
||||
}
|
||||
if (targets.includes('domain')) {
|
||||
throw new ConflictException(
|
||||
'دامنه تکراری است — این دامنه قبلاً دامنهٔ اصلی نسخه دیگری است',
|
||||
);
|
||||
}
|
||||
throw new ConflictException('slug یا domain تکراری است');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user