refactor(quick-260921-m34): Aufgabe 1 - Mandantenbindung entzaubert, 105 unnoetige any-Zusicherungen entfernt
- prisma-tenant.extension.ts: (prisma as any) und die Handannotation an
$allOperations in forTenant()/forSystem() entfernt; Kopfkommentar
unveraendert. .then((results: any[]) => ...) auf unknown[] umgestellt.
- 105 Aufrufstellen `const X = forTenant(...) as any` / `forSystem(...) as
any` von der Zusicherung befreit, Zuweisungsform woertlich erhalten
(rls-access-inventory.spec.ts bleibt scharf, 30/30 gruen einzeln
geprueft).
- withTenantTransaction(): Prisma.TransactionClient fuer tx probiert,
gemessen verworfen - bricht das Testdoppel in
prisma-tenant.extension.spec.ts (TS2322 auf einem absichtlich
unvollstaendigen Fake-Objekt). tx bleibt any, mit Begruendung am Typ.
- Gefolge des jetzt getypten Klienten entfernt: any[]-Annotationen und
.map((x: any) => ...) in groups.service.ts, module-grants.service.ts,
dkv.service.ts, ldap-config.service.ts, tenders.controller.ts:270.
- Befund (D-03): tender-matching.service.ts:159 trug eine Handannotation
(match: { tender: unknown }), die den Wert nur deshalb auf unknown
verengte, um TS7006 unter dem alten any-Klienten zu vermeiden - mit dem
getypten Klienten war das falsch. Annotation geloescht, kein Ersatz
durch Zusicherung.
- Zwei any bleiben gezielt in groups.service.ts (u/a in
ensureDefaultGroup(), gefolge von tx: any) - Begruendung am Code.
noExplicitAny apps/api/src: 288 -> 149 (Schranke 155). type-check 4/4,
lint 5/5 (0 error). apps/api 72/1143 gruen, apps/web 73/531 gruen,
rls-access-inventory.spec.ts 30/30 gruen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -121,7 +121,7 @@ export class TenderDigestScheduler implements OnModuleInit {
|
||||
// `distinct(['userId'])` liefert dann nur EINE der moeglichen
|
||||
// tenantId-Werte je Nutzer, welche ist von der internen Zeilenreihenfolge
|
||||
// abhaengig. Siehe docs/mandantentrennung-etappe2-fehlerrichtung.md.
|
||||
const systemPrisma = forSystem(this.prisma) as any;
|
||||
const systemPrisma = forSystem(this.prisma);
|
||||
const candidates: { userId: string; tenantId: string }[] = await systemPrisma.tenderMatch.findMany({
|
||||
where: { notifiedAt: null },
|
||||
select: { userId: true, tenantId: true },
|
||||
@@ -145,7 +145,7 @@ export class TenderDigestScheduler implements OnModuleInit {
|
||||
// `userId` sieht dieser Zugriff den ganzen Mandanten, exakt wie vor
|
||||
// der Migration. Ein Systemkontext fuer Hintergrunddienste ist
|
||||
// Etappe 3c, nicht Teil dieser Aenderung.
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||
|
||||
const pref = await tenantPrisma.tenderNotificationPref.findUnique({
|
||||
where: { userId },
|
||||
|
||||
@@ -102,7 +102,7 @@ export class TenderEmailConfigService {
|
||||
* by userId (T-17-01) — a user only ever reads their own mailbox.
|
||||
*/
|
||||
async getConfigForApi(userId: string, tenantId: string) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const safe = await tenantPrisma.tenderEmailConfig.findUnique({
|
||||
where: { userId },
|
||||
select: EMAIL_CONFIG_SAFE_SELECT,
|
||||
@@ -152,7 +152,7 @@ export class TenderEmailConfigService {
|
||||
*/
|
||||
async saveConfig(ctx: { userId: string; tenantId: string }, dto: TenderEmailConfigDto) {
|
||||
const { userId, tenantId } = ctx;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
let encryptedInboxCreds: string | undefined;
|
||||
|
||||
const credChanged =
|
||||
@@ -240,7 +240,7 @@ export class TenderEmailConfigService {
|
||||
|
||||
if (!username || !password) {
|
||||
try {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const existing = await tenantPrisma.tenderEmailConfig.findUnique({ where: { userId } });
|
||||
if (existing?.encryptedInboxCreds) {
|
||||
const stored = JSON.parse(this.crypto.decrypt(existing.encryptedInboxCreds)) as {
|
||||
|
||||
@@ -72,7 +72,7 @@ export class TenderMatchingService {
|
||||
// und wuerde stumm. Treffer-Anlage und Sofortmeldung bleiben je Profil
|
||||
// GEBUNDEN (unten); der Katalog-Lesezugriff (`tender`, D-03) bleibt
|
||||
// ungebunden. Eine LEERE Profilliste ist Nichtstun (keine Treffer).
|
||||
const systemPrisma = forSystem(this.prisma) as any;
|
||||
const systemPrisma = forSystem(this.prisma);
|
||||
const savedSearches: Prisma.TenderSavedSearchGetPayload<Record<string, never>>[] =
|
||||
await systemPrisma.tenderSavedSearch.findMany();
|
||||
|
||||
@@ -95,7 +95,7 @@ export class TenderMatchingService {
|
||||
// Gebunden an den Mandanten DIESES Profils (260909-laa, Aufgabe 3)
|
||||
// — EIN gebundener Client je Profil, nicht je Treffer, sonst
|
||||
// entstuende pro Zeile eine eigene Transaktion.
|
||||
const tenantPrisma = forTenant(this.prisma, search.tenantId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, search.tenantId);
|
||||
|
||||
for (const hit of hits) {
|
||||
await tenantPrisma.tenderMatch.upsert({
|
||||
@@ -134,7 +134,7 @@ export class TenderMatchingService {
|
||||
try {
|
||||
// Gebunden an den Mandanten DIESES Profils (260909-laa, Aufgabe 3)
|
||||
// — EIN gebundener Client je Profil.
|
||||
const tenantPrisma = forTenant(this.prisma, profile.tenantId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, profile.tenantId);
|
||||
|
||||
const fresh = await tenantPrisma.tenderMatch.findMany({
|
||||
where: {
|
||||
@@ -156,7 +156,7 @@ export class TenderMatchingService {
|
||||
{ email: user.email },
|
||||
profile.tenantId,
|
||||
{ name: profile.name },
|
||||
fresh.map((match: { tender: unknown }) => match.tender),
|
||||
fresh.map((match) => match.tender),
|
||||
);
|
||||
|
||||
if (sent) {
|
||||
|
||||
@@ -45,7 +45,7 @@ export class TenderNotificationPrefService {
|
||||
* autowrite needed to represent "using the default".
|
||||
*/
|
||||
async getForUser(userId: string, tenantId: string): Promise<{ digestInterval: string }> {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const existing = await tenantPrisma.tenderNotificationPref.findUnique({
|
||||
where: { userId },
|
||||
});
|
||||
@@ -63,7 +63,7 @@ export class TenderNotificationPrefService {
|
||||
* than creating a new one.
|
||||
*/
|
||||
async setForUser(userId: string, tenantId: string, digestInterval: string) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
try {
|
||||
return await tenantPrisma.tenderNotificationPref.upsert({
|
||||
where: { userId },
|
||||
|
||||
@@ -69,7 +69,7 @@ export class TenderRssFeedSourceService {
|
||||
* Bindung nicht überflüssig, sondern das zweite Netz.
|
||||
*/
|
||||
async listForUser(userId: string, tenantId: string) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
return tenantPrisma.tenderRssFeedSource.findMany({
|
||||
where: { OR: [{ userId: null }, { userId }] },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
@@ -93,7 +93,7 @@ export class TenderRssFeedSourceService {
|
||||
) {
|
||||
this.assertUrlAllowed(dto.url);
|
||||
|
||||
const tenantPrisma = forTenant(this.prisma, ctx.tenantId, ctx.userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, ctx.tenantId, ctx.userId);
|
||||
const existingCount = await tenantPrisma.tenderRssFeedSource.count({
|
||||
where: { userId: ctx.userId },
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ export class TenderSavedSearchService {
|
||||
* strictly by userId (V4/IDOR) — a foreign userId sees nothing.
|
||||
*/
|
||||
async list(userId: string, tenantId: string) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
return tenantPrisma.tenderSavedSearch.findMany({
|
||||
where: { userId },
|
||||
orderBy: { name: 'asc' },
|
||||
@@ -58,7 +58,7 @@ export class TenderSavedSearchService {
|
||||
* users, since the uniqueness is scoped per-user.
|
||||
*/
|
||||
async create(userId: string, tenantId: string, dto: CreateSavedSearchDto) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
try {
|
||||
return await tenantPrisma.tenderSavedSearch.create({
|
||||
data: {
|
||||
@@ -87,7 +87,7 @@ export class TenderSavedSearchService {
|
||||
* leaking whether another user's profile exists).
|
||||
*/
|
||||
async update(id: string, userId: string, tenantId: string, dto: UpdateSavedSearchDto) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const existing = await tenantPrisma.tenderSavedSearch.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
@@ -124,7 +124,7 @@ export class TenderSavedSearchService {
|
||||
* update().
|
||||
*/
|
||||
async remove(id: string, userId: string, tenantId: string) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const existing = await tenantPrisma.tenderSavedSearch.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ export class TenderTriageService {
|
||||
update.favoritedAt = dto.isFavorite ? now : null;
|
||||
}
|
||||
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
try {
|
||||
return await tenantPrisma.tenderTriage.upsert({
|
||||
where: { userId_tenderId: { userId, tenderId } },
|
||||
@@ -111,7 +111,7 @@ export class TenderTriageService {
|
||||
*/
|
||||
async listForUser(userId: string, tenantId: string, tenderIds: string[]) {
|
||||
if (!tenderIds.length) return [];
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
return tenantPrisma.tenderTriage.findMany({
|
||||
where: { userId, tenderId: { in: tenderIds } },
|
||||
});
|
||||
@@ -123,7 +123,7 @@ export class TenderTriageService {
|
||||
* tender-query.builder.ts's buildTenderWhere.
|
||||
*/
|
||||
async favoriteIds(userId: string, tenantId: string): Promise<string[]> {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const rows = await tenantPrisma.tenderTriage.findMany({
|
||||
where: { userId, isFavorite: true },
|
||||
select: { tenderId: true },
|
||||
|
||||
@@ -267,7 +267,7 @@ export class TendersController {
|
||||
const { userId, tenantId } = this.extractTriageContext(req);
|
||||
const feeds = await this.tenderRssFeedSource.listForUser(userId, tenantId);
|
||||
|
||||
return feeds.map(({ userId: ownerUserId, ...rest }: any) => ({
|
||||
return feeds.map(({ userId: ownerUserId, ...rest }) => ({
|
||||
...rest,
|
||||
isPlatformWide: ownerUserId === null,
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user