refactor(quick-260921-m34): Aufgabe 3a - 18 Fehlerfaenger auf unknown, mit echter Eingrenzung
catch (e: any) in groups, module-grants, ldap, user, admin-seed, calendar
und vier tenders-Diensten auf catch (e: unknown) umgestellt. Die
Eingrenzung passiert an der Verwendungsstelle, nicht per Zusicherung.
Neu: apps/api/src/prisma/prisma-error.ts mit prismaErrorCode() und
prismaErrorTarget(). Bewusst Form-Pruefungen statt instanceof
Prisma.PrismaClientKnownRequestError - gemessen: samtliche Testdoppel in
apps/api werfen new Error(...) mit angehaengtem .code (groups, user, ldap,
tenders, module-grants, admin-seed) und dashboard.service.spec.ts:451 ein
reines { code: 'P2002' }. Ein instanceof-Test haette all diese Werte in den
anderen Zweig geschickt - Verhaltensaenderung, verboten nach D-03/T-M34-06.
Die Helfer bilden err?.code und err?.meta?.target eins zu eins ab.
ldap.service.ts liest zusaetzlich meta.target; prismaErrorTarget() gibt
unknown zurueck, weil der Bestand dort Array UND Zeichenkette getrennt
behandelt - ein engerer Typ waere eine Behauptung.
calendar.service.ts:341 nutzt instanceof Error statt e?.message: gemessen
wirft validateUrlNotPrivate() ausschliesslich ForbiddenException (der
eigene catch dort setzt jeden Fremdfehler in eine um), also trifft
instanceof dieselben Faelle. Ersatzzweig 'URL not allowed' unveraendert.
noExplicitAny in apps/api/src: 56 -> 38. type-check 4/4, lint 5/5 (0
error), apps/api 72/1143, apps/web 73/531, rls-access-inventory 30/30.
noNonNullAssertion 56, as unknown as 33, Unterdrueckungsmarker 1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -338,8 +338,16 @@ export class CalendarService {
|
|||||||
try {
|
try {
|
||||||
// Exchange EWS servers are commonly on internal networks — SSRF check skipped for exchange type
|
// Exchange EWS servers are commonly on internal networks — SSRF check skipped for exchange type
|
||||||
if (dto.type !== 'exchange') await this.validateUrlNotPrivate(dto.url);
|
if (dto.type !== 'exchange') await this.validateUrlNotPrivate(dto.url);
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
return { success: false, error: e?.message ?? 'URL not allowed' };
|
// validateUrlNotPrivate() wirft ausschliesslich ForbiddenException
|
||||||
|
// (gemessen: jeder Wurf in der Methode ist eine, und der eigene
|
||||||
|
// catch-Block dort setzt jeden Fremdfehler in eine um). instanceof
|
||||||
|
// Error trifft deshalb genau dieselben Faelle wie das bisherige
|
||||||
|
// e?.message; der Ersatzzweig bleibt derselbe (D-03).
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: e instanceof Error ? e.message : 'URL not allowed',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = this.getProvider(dto.type);
|
const provider = this.getProvider(dto.type);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
import { MembershipSource } from '@prisma/client';
|
import { MembershipSource } from '@prisma/client';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant, withTenantTransaction } from '../prisma/prisma-tenant.extension';
|
import { forTenant, withTenantTransaction } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name der automatisch angelegten Standardgruppe (D-13). Geteilte Wahrheit
|
* Name der automatisch angelegten Standardgruppe (D-13). Geteilte Wahrheit
|
||||||
@@ -91,8 +92,8 @@ export class GroupsService {
|
|||||||
return await tenantPrisma.group.create({
|
return await tenantPrisma.group.create({
|
||||||
data: { tenantId, name },
|
data: { tenantId, name },
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
`Eine Gruppe mit dem Namen '${name}' existiert bereits in diesem Mandanten`,
|
`Eine Gruppe mit dem Namen '${name}' existiert bereits in diesem Mandanten`,
|
||||||
);
|
);
|
||||||
@@ -204,8 +205,8 @@ export class GroupsService {
|
|||||||
where: { id },
|
where: { id },
|
||||||
data: updateData,
|
data: updateData,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
`Eine Gruppe mit dem Namen '${updateData.name}' existiert bereits in diesem Mandanten`,
|
`Eine Gruppe mit dem Namen '${updateData.name}' existiert bereits in diesem Mandanten`,
|
||||||
);
|
);
|
||||||
@@ -244,8 +245,8 @@ export class GroupsService {
|
|||||||
const tenantPrisma = forTenant(this.prisma, tenantId);
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.group.delete({ where: { id } });
|
return await tenantPrisma.group.delete({ where: { id } });
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2025') {
|
if (prismaErrorCode(err) === 'P2025') {
|
||||||
throw new NotFoundException(`Gruppe '${id}' nicht gefunden`);
|
throw new NotFoundException(`Gruppe '${id}' nicht gefunden`);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@@ -398,8 +399,8 @@ export class GroupsService {
|
|||||||
|
|
||||||
return group;
|
return group;
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@@ -468,8 +469,8 @@ export class GroupsService {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schreibseite der Modul-Freigaben (PERM-03): Grants für Gruppen und für
|
* Schreibseite der Modul-Freigaben (PERM-03): Grants für Gruppen und für
|
||||||
@@ -132,8 +133,8 @@ export class ModuleGrantsService {
|
|||||||
`Grant erteilt: tenant=${tenantId} module=${moduleId} ${target}`,
|
`Grant erteilt: tenant=${tenantId} module=${moduleId} ${target}`,
|
||||||
);
|
);
|
||||||
return created;
|
return created;
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
const existing = await tenantPrisma.moduleGrant.findFirst({
|
const existing = await tenantPrisma.moduleGrant.findFirst({
|
||||||
where: {
|
where: {
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { Client, EqualityFilter, Entry } from 'ldapts';
|
import { Client, EqualityFilter, Entry } from 'ldapts';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode, prismaErrorTarget } from '../prisma/prisma-error';
|
||||||
import { GroupsService } from '../groups/groups.service';
|
import { GroupsService } from '../groups/groups.service';
|
||||||
import { UserService } from '../user/user.service';
|
import { UserService } from '../user/user.service';
|
||||||
|
|
||||||
@@ -842,9 +843,9 @@ export class LdapService {
|
|||||||
data: { tenantId, name, ldapDn: entry.dn, ldapObjectGuid },
|
data: { tenantId, name, ldapDn: entry.dn, ldapObjectGuid },
|
||||||
});
|
});
|
||||||
result.imported++;
|
result.imported++;
|
||||||
} catch (createError: any) {
|
} catch (createError: unknown) {
|
||||||
if (createError?.code === 'P2002') {
|
if (prismaErrorCode(createError) === 'P2002') {
|
||||||
const target = createError?.meta?.target;
|
const target = prismaErrorTarget(createError);
|
||||||
const targetsGuid = Array.isArray(target)
|
const targetsGuid = Array.isArray(target)
|
||||||
? target.includes('ldapObjectGuid')
|
? target.includes('ldapObjectGuid')
|
||||||
: String(target ?? '').includes('ldapObjectGuid');
|
: String(target ?? '').includes('ldapObjectGuid');
|
||||||
@@ -1526,8 +1527,8 @@ export class LdapService {
|
|||||||
data: { name, ldapDn: dn },
|
data: { name, ldapDn: dn },
|
||||||
});
|
});
|
||||||
result.groupsRenamed++;
|
result.groupsRenamed++;
|
||||||
} catch (updateError: any) {
|
} catch (updateError: unknown) {
|
||||||
if (updateError?.code === 'P2002') {
|
if (prismaErrorCode(updateError) === 'P2002') {
|
||||||
// WR-02 (16-REVIEW.md): this update() writes BOTH name and
|
// WR-02 (16-REVIEW.md): this update() writes BOTH name and
|
||||||
// ldapDn in one call — @@unique([tenantId, name]) AND
|
// ldapDn in one call — @@unique([tenantId, name]) AND
|
||||||
// @@unique([tenantId, ldapDn]) are both potential triggers
|
// @@unique([tenantId, ldapDn]) are both potential triggers
|
||||||
@@ -1535,7 +1536,7 @@ export class LdapService {
|
|||||||
// collision when updateError.meta.target says so. Mirrors
|
// collision when updateError.meta.target says so. Mirrors
|
||||||
// the discrimination importGroupsByDn() already does above
|
// the discrimination importGroupsByDn() already does above
|
||||||
// for its own create() call.
|
// for its own create() call.
|
||||||
const target = updateError?.meta?.target;
|
const target = prismaErrorTarget(updateError);
|
||||||
const targetsName = Array.isArray(target)
|
const targetsName = Array.isArray(target)
|
||||||
? target.includes('name')
|
? target.includes('name')
|
||||||
: String(target ?? '').includes('name');
|
: String(target ?? '').includes('name');
|
||||||
@@ -1591,8 +1592,8 @@ export class LdapService {
|
|||||||
await tenantPrisma.group.delete({ where: { id: group.id } });
|
await tenantPrisma.group.delete({ where: { id: group.id } });
|
||||||
result.groupsDeleted++;
|
result.groupsDeleted++;
|
||||||
anyDeleted = true;
|
anyDeleted = true;
|
||||||
} catch (deleteError: any) {
|
} catch (deleteError: unknown) {
|
||||||
if (deleteError?.code !== 'P2025') {
|
if (prismaErrorCode(deleteError) !== 'P2025') {
|
||||||
throw deleteError;
|
throw deleteError;
|
||||||
}
|
}
|
||||||
// Already gone (e.g. a concurrent manual delete) — not
|
// Already gone (e.g. a concurrent manual delete) — not
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Eingrenzung gefangener Fehlerwerte auf die Form, die der Bestandscode an
|
||||||
|
* Prisma-Fehlern tatsaechlich liest — `code` und `meta.target`, sonst nichts.
|
||||||
|
*
|
||||||
|
* Warum kein `instanceof Prisma.PrismaClientKnownRequestError`:
|
||||||
|
* Der Bestand prueft ueberall `err?.code === 'P2002'` und trifft damit JEDES
|
||||||
|
* Objekt, das ein `code`-Feld traegt. Genau darauf bauen die Testdoppel in
|
||||||
|
* `apps/api` auf — sie werfen `const err = new Error(...); err.code = 'P2002'`
|
||||||
|
* (groups, user, ldap, tenders, module-grants, admin-seed) und an einer Stelle
|
||||||
|
* ein reines `{ code: 'P2002' }` (dashboard.service.spec.ts:451). Ein
|
||||||
|
* `instanceof`-Test wuerde alle diese Werte in den jeweils ANDEREN Zweig
|
||||||
|
* schicken. Das waere eine Verhaltensaenderung und ist deshalb verboten
|
||||||
|
* (Plan 260921-m34, D-03 und T-M34-06).
|
||||||
|
*
|
||||||
|
* Diese Funktionen sind daher bewusst Form-Pruefungen ("duck typing") und
|
||||||
|
* keine Klassenpruefungen: sie bilden `err?.code` bzw. `err?.meta?.target`
|
||||||
|
* eins zu eins ab, nur eben ohne `any`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liefert den Prisma-Fehlercode (z.B. `P2002`, `P2025`), wenn der gefangene
|
||||||
|
* Wert ein Objekt mit einem Zeichenketten-Feld `code` ist — sonst `undefined`.
|
||||||
|
*
|
||||||
|
* Ein Feld `code`, das keine Zeichenkette ist, liefert `undefined`. Auch das
|
||||||
|
* ist verhaltensgleich: ein Vergleich `err.code === 'P2002'` waere dort
|
||||||
|
* ohnehin nie wahr geworden.
|
||||||
|
*/
|
||||||
|
export function prismaErrorCode(err: unknown): string | undefined {
|
||||||
|
if (typeof err !== 'object' || err === null) return undefined;
|
||||||
|
const code = (err as { code?: unknown }).code;
|
||||||
|
return typeof code === 'string' ? code : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liefert `meta.target` eines Prisma-Fehlers als `unknown`.
|
||||||
|
*
|
||||||
|
* Bewusst `unknown` und nicht `string[]`: Prisma liefert hier je nach
|
||||||
|
* Fehlerart ein Feld-Array ODER den Indexnamen als Zeichenkette, und der
|
||||||
|
* Bestand in `ldap.service.ts` behandelt genau diese beiden Faelle bereits
|
||||||
|
* getrennt (`Array.isArray(target) ? ... : String(target ?? '')`). Ein
|
||||||
|
* engerer Rueckgabetyp waere hier eine Behauptung statt einer Messung.
|
||||||
|
*/
|
||||||
|
export function prismaErrorTarget(err: unknown): unknown {
|
||||||
|
if (typeof err !== 'object' || err === null) return undefined;
|
||||||
|
const meta = (err as { meta?: unknown }).meta;
|
||||||
|
if (typeof meta !== 'object' || meta === null) return undefined;
|
||||||
|
return (meta as { target?: unknown }).target;
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import type { InboxConfig, InboxProvider } from '../inbox/inbox-provider.interfa
|
|||||||
|
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
import type { TenderEmailConfigDto } from './dto/tender-email-config.dto';
|
import type { TenderEmailConfigDto } from './dto/tender-email-config.dto';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -204,8 +205,8 @@ export class TenderEmailConfigService {
|
|||||||
update: { tenantId, ...data },
|
update: { tenantId, ...data },
|
||||||
select: EMAIL_CONFIG_SAFE_SELECT,
|
select: EMAIL_CONFIG_SAFE_SELECT,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
if (error?.code === 'P2002') {
|
if (prismaErrorCode(error) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Die Postfach-Konfiguration konnte nicht gespeichert werden, weil bereits ein widersprüchlicher Eintrag existiert. Bitte laden Sie die Seite neu und versuchen Sie es erneut.',
|
'Die Postfach-Konfiguration konnte nicht gespeichert werden, weil bereits ein widersprüchlicher Eintrag existiert. Bitte laden Sie die Seite neu und versuchen Sie es erneut.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ConflictException, Injectable } from '@nestjs/common';
|
import { ConflictException, Injectable } from '@nestjs/common';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for managing the per-user Tender digest interval preference
|
* Service for managing the per-user Tender digest interval preference
|
||||||
@@ -70,8 +71,8 @@ export class TenderNotificationPrefService {
|
|||||||
create: { userId, tenantId, digestInterval },
|
create: { userId, tenantId, digestInterval },
|
||||||
update: { digestInterval },
|
update: { digestInterval },
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
if (error?.code === 'P2002') {
|
if (prismaErrorCode(error) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Die Benachrichtigungseinstellung konnte nicht gespeichert werden, weil bereits ein widersprüchlicher Eintrag existiert. Bitte laden Sie die Seite neu und versuchen Sie es erneut.',
|
'Die Benachrichtigungseinstellung konnte nicht gespeichert werden, weil bereits ein widersprüchlicher Eintrag existiert. Bitte laden Sie die Seite neu und versuchen Sie es erneut.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ConflictException, Injectable, NotFoundException } from '@nestjs/common
|
|||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
import { CreateSavedSearchDto, UpdateSavedSearchDto } from './dto/saved-search.dto';
|
import { CreateSavedSearchDto, UpdateSavedSearchDto } from './dto/saved-search.dto';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,8 +70,8 @@ export class TenderSavedSearchService {
|
|||||||
...(dto.instantAlert !== undefined && { instantAlert: dto.instantAlert }),
|
...(dto.instantAlert !== undefined && { instantAlert: dto.instantAlert }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
if (error?.code === 'P2002') {
|
if (prismaErrorCode(error) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Ein Suchprofil mit diesem Namen existiert bereits.',
|
'Ein Suchprofil mit diesem Namen existiert bereits.',
|
||||||
);
|
);
|
||||||
@@ -108,8 +109,8 @@ export class TenderSavedSearchService {
|
|||||||
where: { id },
|
where: { id },
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
if (error?.code === 'P2002') {
|
if (prismaErrorCode(error) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Ein Suchprofil mit diesem Namen existiert bereits.',
|
'Ein Suchprofil mit diesem Namen existiert bereits.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ConflictException, Injectable } from '@nestjs/common';
|
import { ConflictException, Injectable } from '@nestjs/common';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial triage update accepted by setTriage(). Both fields are optional
|
* Partial triage update accepted by setTriage(). Both fields are optional
|
||||||
@@ -90,8 +91,8 @@ export class TenderTriageService {
|
|||||||
favoritedAt: dto.isFavorite ? now : null,
|
favoritedAt: dto.isFavorite ? now : null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
if (error?.code === 'P2002') {
|
if (prismaErrorCode(error) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Der Bearbeitungsstand zu dieser Ausschreibung konnte nicht gespeichert werden. Bitte die Seite neu laden und es erneut versuchen.',
|
'Der Bearbeitungsstand zu dieser Ausschreibung konnte nicht gespeichert werden. Bitte die Seite neu laden und es erneut versuchen.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config';
|
|||||||
import * as argon2 from 'argon2';
|
import * as argon2 from 'argon2';
|
||||||
import { GroupsService } from '../groups/groups.service';
|
import { GroupsService } from '../groups/groups.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +118,7 @@ export class AdminSeedService implements OnApplicationBootstrap {
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
// Entschaerfung der Startsperre (260910-das, Befund I): trifft die
|
// Entschaerfung der Startsperre (260910-das, Befund I): trifft die
|
||||||
// Erstanlage die plattformweite Eindeutigkeit von username/email
|
// Erstanlage die plattformweite Eindeutigkeit von username/email
|
||||||
// (P2002), bedeutet das an DIESER Stelle exakt dasselbe wie ein
|
// (P2002), bedeutet das an DIESER Stelle exakt dasselbe wie ein
|
||||||
@@ -129,7 +130,7 @@ export class AdminSeedService implements OnApplicationBootstrap {
|
|||||||
// festgehaltenen Absicht (seedAdmin() bleibt bewusst ungekapselt) --
|
// festgehaltenen Absicht (seedAdmin() bleibt bewusst ungekapselt) --
|
||||||
// JEDER ANDERE Fehler bricht den Start weiterhin ab. Nur dieser eine,
|
// JEDER ANDERE Fehler bricht den Start weiterhin ab. Nur dieser eine,
|
||||||
// an dieser Stelle gleichbedeutende Fall wird ergaenzt.
|
// an dieser Stelle gleichbedeutende Fall wird ergaenzt.
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Admin user "${username}" seed skipped: uniqueness collision on username/email (an administrator with this identity already exists, currently invisible under this tenant context) — see docs/mandantentrennung-etappe2-fehlerrichtung.md, Abschnitt "Bereich user"`,
|
`Admin user "${username}" seed skipped: uniqueness collision on username/email (an administrator with this identity already exists, currently invisible under this tenant context) — see docs/mandantentrennung-etappe2-fehlerrichtung.md, Abschnitt "Bereich user"`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ConflictException, Injectable, Logger } from '@nestjs/common';
|
|||||||
import * as argon2 from 'argon2';
|
import * as argon2 from 'argon2';
|
||||||
import { GroupsService } from '../groups/groups.service';
|
import { GroupsService } from '../groups/groups.service';
|
||||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||||
|
import { prismaErrorCode } from '../prisma/prisma-error';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,8 +117,8 @@ export class UserService {
|
|||||||
passwordHash: password ? await argon2.hash(password) : null,
|
passwordHash: password ? await argon2.hash(password) : null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Benutzername oder E-Mail-Adresse sind plattformweit bereits vergeben.',
|
'Benutzername oder E-Mail-Adresse sind plattformweit bereits vergeben.',
|
||||||
);
|
);
|
||||||
@@ -174,8 +175,8 @@ export class UserService {
|
|||||||
where: { id },
|
where: { id },
|
||||||
data: updateData,
|
data: updateData,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.code === 'P2002') {
|
if (prismaErrorCode(err) === 'P2002') {
|
||||||
throw new ConflictException(
|
throw new ConflictException(
|
||||||
'Benutzername oder E-Mail-Adresse sind plattformweit bereits vergeben.',
|
'Benutzername oder E-Mail-Adresse sind plattformweit bereits vergeben.',
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user