diff --git a/apps/api/scripts/rls-scratch-check.mjs b/apps/api/scripts/rls-scratch-check.mjs index b9a4724..aae4079 100644 --- a/apps/api/scripts/rls-scratch-check.mjs +++ b/apps/api/scripts/rls-scratch-check.mjs @@ -216,19 +216,6 @@ async function forTenantQuery(prisma, tenantId, queryFn, userId) { return result; } -/** - * Spiegelbildlich zu `forSystem()` in apps/api/src/prisma/prisma-tenant.extension.ts - * (Etappe 3c, 260914-eym) — bei jeder Aenderung dort HIER nachziehen: EINE - * getaggte Anweisung setzt `app.system_context = 'true'` und AUSDRUECKLICH - * `app.current_tenant = ''` und `app.current_user = ''`, alle drei als - * Literale; danach die Abfrage in derselben Array-Transaktion. - */ -async function forSystemQuery(prisma, queryFn) { - const setContext = prisma.$executeRaw`SELECT set_config('app.system_context', 'true', true), set_config('app.current_tenant', '', true), set_config('app.current_user', '', true)`; - const [, result] = await prisma.$transaction([setContext, queryFn(prisma)]); - return result; -} - /** * Aufgabe 1 — misst die fuenf im Plan genannten Verhaltensweisen von * forTenant() unter der Rolle ohne BYPASSRLS. @@ -3242,7 +3229,7 @@ async function runCalendarAreaChecks(adminUrl, scratchRoleUrl, results) { // gebunden MIT Benutzer jetzt NICHT mehr lesbar ist (siehe Umkehrung // unten). const a2Row = rowsForA.find((r) => r.userId === 'user-a2'); - const a2CredentialsVisible = Boolean(a2Row && a2Row.encryptedPassword); + const a2CredentialsVisible = Boolean(a2Row?.encryptedPassword); report( results, 'calendarsource-ohne-benutzer-sieht-beide-nutzer-desselben-mandanten', diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts index 758c836..ab5301e 100644 --- a/apps/api/src/auth/auth.service.ts +++ b/apps/api/src/auth/auth.service.ts @@ -9,7 +9,7 @@ import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { Role } from '@prisma/client'; import * as argon2 from 'argon2'; -import { randomUUID } from 'crypto'; +import { randomUUID } from 'node:crypto'; import { Response } from 'express'; import { LdapConfigService } from '../ldap/ldap-config.service'; import { LdapService } from '../ldap/ldap.service'; @@ -111,7 +111,7 @@ export class AuthService { `; const user = rows[0]; - if (!user || !user.isActive) { + if (!user?.isActive) { return null; } @@ -125,7 +125,7 @@ export class AuthService { } const config = await this.ldapConfigService.getConfig(user.tenantId); - if (!config || !config.isActive) { + if (!config?.isActive) { return null; } @@ -220,7 +220,7 @@ export class AuthService { const user = rows[0]; // Always return success to prevent email enumeration (T-02-12) - if (!user || !user.isActive) { + if (!user?.isActive) { this.logger.log( `Password reset requested for unknown/inactive email: ${email}`, ); @@ -358,7 +358,7 @@ export class AuthService { where: { id: userId }, }); - if (!user || !user.passwordHash) { + if (!user?.passwordHash) { throw new UnauthorizedException('User not found or has no local password'); } diff --git a/apps/api/src/auth/decorators/current-user.decorator.ts b/apps/api/src/auth/decorators/current-user.decorator.ts index 7919497..b3f5c83 100644 --- a/apps/api/src/auth/decorators/current-user.decorator.ts +++ b/apps/api/src/auth/decorators/current-user.decorator.ts @@ -1,7 +1,7 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const CurrentUser = createParamDecorator( - (data: unknown, ctx: ExecutionContext) => { + (_data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return request.user; }, diff --git a/apps/api/src/auth/interceptors/force-password-change.interceptor.ts b/apps/api/src/auth/interceptors/force-password-change.interceptor.ts index e988065..222a8b5 100644 --- a/apps/api/src/auth/interceptors/force-password-change.interceptor.ts +++ b/apps/api/src/auth/interceptors/force-password-change.interceptor.ts @@ -50,7 +50,6 @@ export class ForcePasswordChangeInterceptor implements NestInterceptor { // Allow specific routes even when password change is required const path = request.route?.path || request.url; - const method = request.method; const allowedPaths = [ '/auth/change-password', diff --git a/apps/api/src/auth/strategies/jwt.strategy.ts b/apps/api/src/auth/strategies/jwt.strategy.ts index 20b5d93..bb8b970 100644 --- a/apps/api/src/auth/strategies/jwt.strategy.ts +++ b/apps/api/src/auth/strategies/jwt.strategy.ts @@ -8,8 +8,8 @@ import { Request } from 'express'; * Custom extractor that reads JWT from the httpOnly "session" cookie. */ function cookieExtractor(req: Request): string | null { - if (req && req.cookies) { - return req.cookies['session'] || null; + if (req?.cookies) { + return req.cookies.session || null; } return null; } diff --git a/apps/api/src/calendar/calendar.service.ts b/apps/api/src/calendar/calendar.service.ts index 43ff6c9..ef2182b 100644 --- a/apps/api/src/calendar/calendar.service.ts +++ b/apps/api/src/calendar/calendar.service.ts @@ -318,7 +318,7 @@ export class CalendarService { }); return { success }; - } catch (error) { + } catch { const errorMsg = 'Connection failed'; // T-05-13: generic error, no credentials await tenantPrisma.calendarSource.update({ where: { id }, @@ -355,7 +355,7 @@ export class CalendarService { try { const success = await provider.testConnection(tempSource); return { success }; - } catch (e: any) { + } catch { return { success: false, error: 'Connection failed' }; } } diff --git a/apps/api/src/calendar/dto/create-calendar-source.dto.ts b/apps/api/src/calendar/dto/create-calendar-source.dto.ts index 679346f..d703539 100644 --- a/apps/api/src/calendar/dto/create-calendar-source.dto.ts +++ b/apps/api/src/calendar/dto/create-calendar-source.dto.ts @@ -1,5 +1,4 @@ import { - IsBoolean, IsHexColor, IsIn, IsNotEmpty, diff --git a/apps/api/src/calendar/providers/caldav.provider.ts b/apps/api/src/calendar/providers/caldav.provider.ts index 61ee28f..1c4fe93 100644 --- a/apps/api/src/calendar/providers/caldav.provider.ts +++ b/apps/api/src/calendar/providers/caldav.provider.ts @@ -47,7 +47,7 @@ export class CalDAVProvider implements CalendarProvider { for (const key of Object.keys(parsed)) { const component = parsed[key]; - if (!component || component.type !== 'VEVENT') continue; + if (component?.type !== 'VEVENT') continue; const vevent = component as ical.VEvent; const start = new Date(vevent.start); diff --git a/apps/api/src/calendar/providers/exchange.provider.ts b/apps/api/src/calendar/providers/exchange.provider.ts index 1585f61..455a989 100644 --- a/apps/api/src/calendar/providers/exchange.provider.ts +++ b/apps/api/src/calendar/providers/exchange.provider.ts @@ -180,8 +180,8 @@ export class ExchangeProvider implements CalendarProvider { id: `${source.id}-${item.id}`, sourceId: source.id, title: item.subject || 'Untitled', - start: new Date(item.start?.dateTime + 'Z'), - end: new Date(item.end?.dateTime + 'Z'), + start: new Date(`${item.start?.dateTime}Z`), + end: new Date(`${item.end?.dateTime}Z`), allDay: item.isAllDay || false, location: item.location?.displayName || undefined, description: item.bodyPreview || undefined, diff --git a/apps/api/src/calendar/providers/ics.provider.ts b/apps/api/src/calendar/providers/ics.provider.ts index 2d6b974..d9857bf 100644 --- a/apps/api/src/calendar/providers/ics.provider.ts +++ b/apps/api/src/calendar/providers/ics.provider.ts @@ -30,7 +30,7 @@ export class ICSProvider implements CalendarProvider { for (const key of Object.keys(data)) { const component = data[key]; - if (!component || component.type !== 'VEVENT') continue; + if (component?.type !== 'VEVENT') continue; const vevent = component as ical.VEvent; diff --git a/apps/api/src/cert-manager/cert-manager.service.spec.ts b/apps/api/src/cert-manager/cert-manager.service.spec.ts index 16bfd2b..484a585 100644 --- a/apps/api/src/cert-manager/cert-manager.service.spec.ts +++ b/apps/api/src/cert-manager/cert-manager.service.spec.ts @@ -116,7 +116,7 @@ describe('CertManagerService helpers', () => { const cert2 = generateSelfSignedCert(); const pem1 = forge.pki.certificateToPem(cert1); const pem2 = forge.pki.certificateToPem(cert2); - const chain = pem1 + '\n' + pem2; + const chain = `${pem1}\n${pem2}`; const parsed = service.parsePemChain(chain); expect(parsed).toHaveLength(2); @@ -266,7 +266,7 @@ describe('splitCerts', () => { cert2Pem = forge.pki.certificateToPem(c2); // Fullchain fixture: two PEMs concatenated - const fullchainPem = cert1Pem + '\n' + cert2Pem; + const fullchainPem = `${cert1Pem}\n${cert2Pem}`; fullchainBuffer = Buffer.from(fullchainPem, 'utf-8'); // P7B fixture: PEM-wrapped PKCS7 SignedData bundle with both certs diff --git a/apps/api/src/cert-manager/cert-manager.service.ts b/apps/api/src/cert-manager/cert-manager.service.ts index e69972c..fc54101 100644 --- a/apps/api/src/cert-manager/cert-manager.service.ts +++ b/apps/api/src/cert-manager/cert-manager.service.ts @@ -302,7 +302,7 @@ export class CertManagerService { fingerprint: { sha1, sha256 }, pemPreview: forge.pki.certificateToPem(cert), }; - } catch (err) { + } catch { this.logger.warn('parseCert: failed to extract CertDetails fields'); throw new BadRequestException('Failed to extract certificate details'); } @@ -505,7 +505,7 @@ export class CertManagerService { return { filename: 'chain.pem', content, - mimeType: FORMAT_MIME['pem'], + mimeType: FORMAT_MIME.pem, }; } else if (outputFormat === 'pfx') { // Open Question 1 resolution: toPkcs12Asn1(null, certs, password) works in node-forge 1.4.0 @@ -523,7 +523,7 @@ export class CertManagerService { return { filename: 'bundle.pfx', content, - mimeType: FORMAT_MIME['pfx'], + mimeType: FORMAT_MIME.pfx, }; } else { throw new BadRequestException( @@ -667,7 +667,7 @@ export class CertManagerService { return { filename: 'converted.pfx', content, - mimeType: FORMAT_MIME['pfx'], + mimeType: FORMAT_MIME.pfx, }; } @@ -676,7 +676,7 @@ export class CertManagerService { content, mimeType: FORMAT_MIME[targetFormat], }; - } catch (err) { + } catch { this.logger.warn('convertCert: failed to serialize to target format'); throw new BadRequestException( `Failed to convert certificate to ${targetFormat}: serialization error`, diff --git a/apps/api/src/crypto/crypto.service.ts b/apps/api/src/crypto/crypto.service.ts index 7b3319c..96d1fce 100644 --- a/apps/api/src/crypto/crypto.service.ts +++ b/apps/api/src/crypto/crypto.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; +import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'; /** Current name of the platform-wide encryption key. */ export const ENCRYPTION_KEY_ENV = 'TESSERA_ENCRYPTION_KEY'; diff --git a/apps/api/src/desktop/desktop.service.spec.ts b/apps/api/src/desktop/desktop.service.spec.ts index 2643e2c..c3490e3 100644 --- a/apps/api/src/desktop/desktop.service.spec.ts +++ b/apps/api/src/desktop/desktop.service.spec.ts @@ -1,8 +1,8 @@ import 'reflect-metadata'; -import * as crypto from 'crypto'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; +import * as crypto from 'node:crypto'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; import { NestFactory } from '@nestjs/core'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; @@ -168,7 +168,7 @@ describe('DesktopService/DesktopController — HTTP-Durchstich (Phase 18)', () = }); it('Test 5 (Whitelist vor Dateisystem): nicht existierendes Verzeichnis + mac wirft BadRequestException, nicht NotFoundException', () => { - const missingDir = path.join(os.tmpdir(), 'tessera-desktop-does-not-exist-' + Date.now()); + const missingDir = path.join(os.tmpdir(), `tessera-desktop-does-not-exist-${Date.now()}`); const previous = process.env.DESKTOP_DIST_DIR; process.env.DESKTOP_DIST_DIR = missingDir; try { diff --git a/apps/api/src/desktop/desktop.service.ts b/apps/api/src/desktop/desktop.service.ts index d7fe17a..a8c7ff5 100644 --- a/apps/api/src/desktop/desktop.service.ts +++ b/apps/api/src/desktop/desktop.service.ts @@ -6,8 +6,8 @@ import type { DesktopPlatform, DesktopUpdateResponse, } from '@tessera/shared'; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; /** * Wertevorrat der Plattformen (Phase 18, D-10). Geschlossen -- eine dritte diff --git a/apps/api/src/dkv/dkv-export.service.ts b/apps/api/src/dkv/dkv-export.service.ts index 53b5559..554309c 100644 --- a/apps/api/src/dkv/dkv-export.service.ts +++ b/apps/api/src/dkv/dkv-export.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import * as XLSX from 'xlsx'; import { ExportRow } from './dkv.types'; diff --git a/apps/api/src/dkv/dkv-parser.service.ts b/apps/api/src/dkv/dkv-parser.service.ts index a847748..46e8e48 100644 --- a/apps/api/src/dkv/dkv-parser.service.ts +++ b/apps/api/src/dkv/dkv-parser.service.ts @@ -39,7 +39,7 @@ export class DkvParserService { let text: string; try { text = await this.extractText(buffer); - } catch (err) { + } catch { this.logger.error('DKV PDF text extraction failed'); throw new Error('DKV invoice parsing failed — text extraction error'); } @@ -98,7 +98,7 @@ export class DkvParserService { // Anchor on VEHICLE: marker — each block extends until next VEHICLE: or end // Kennzeichen format: "GP-JL 728E", "GP ML 720", etc. const vehicleBlockPattern = - /VEHICLE:\s+([A-Z0-9 ._\-]+?)\s+CARD NO\.:\s+(\S+)([\s\S]*?)(?=VEHICLE:|$)/g; + /VEHICLE:\s+([A-Z0-9 ._-]+?)\s+CARD NO\.:\s+(\S+)([\s\S]*?)(?=VEHICLE:|$)/g; let match: RegExpExecArray | null; while ((match = vehicleBlockPattern.exec(text)) !== null) { diff --git a/apps/api/src/dkv/dkv-parser.validate.ts b/apps/api/src/dkv/dkv-parser.validate.ts index b1220c9..a617d2f 100644 --- a/apps/api/src/dkv/dkv-parser.validate.ts +++ b/apps/api/src/dkv/dkv-parser.validate.ts @@ -133,7 +133,7 @@ function parseDkvText(text: string): DkvVehicleBlock[] { // Anchor on VEHICLE: marker — each block extends until next VEHICLE: or end of text // Kennzeichen can contain letters, digits, hyphens, spaces, dots (e.g. "GP-JL 728E") const vehicleBlockPattern = - /VEHICLE:\s+([A-Z0-9 ._\-]+?)\s+CARD NO\.:\s+(\S+)([\s\S]*?)(?=VEHICLE:|$)/g; + /VEHICLE:\s+([A-Z0-9 ._-]+?)\s+CARD NO\.:\s+(\S+)([\s\S]*?)(?=VEHICLE:|$)/g; let match: RegExpExecArray | null; while ((match = vehicleBlockPattern.exec(text)) !== null) { diff --git a/apps/api/src/dkv/dkv.controller.ts b/apps/api/src/dkv/dkv.controller.ts index 4cc460d..e6fe793 100644 --- a/apps/api/src/dkv/dkv.controller.ts +++ b/apps/api/src/dkv/dkv.controller.ts @@ -219,7 +219,7 @@ export class DkvController { ) { const tenantId = this._requireTenant(req); - if (!file || !file.buffer) { + if (!file?.buffer) { throw new BadRequestException('No CSV file uploaded (field name must be "file")'); } diff --git a/apps/api/src/dkv/dkv.service.spec.ts b/apps/api/src/dkv/dkv.service.spec.ts index eed6f73..cbeb0e4 100644 --- a/apps/api/src/dkv/dkv.service.spec.ts +++ b/apps/api/src/dkv/dkv.service.spec.ts @@ -1,4 +1,4 @@ -import * as fs from 'fs'; +import * as fs from 'node:fs'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { DkvService } from './dkv.service'; diff --git a/apps/api/src/dkv/dkv.service.ts b/apps/api/src/dkv/dkv.service.ts index 088be71..77b06ed 100644 --- a/apps/api/src/dkv/dkv.service.ts +++ b/apps/api/src/dkv/dkv.service.ts @@ -5,8 +5,8 @@ import { NotFoundException, } from '@nestjs/common'; import { CryptoService } from '../crypto/crypto.service'; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { PrismaService } from '../prisma/prisma.service'; import { forSystem, forTenant } from '../prisma/prisma-tenant.extension'; @@ -513,7 +513,7 @@ export class DkvService { smtpError = (err as Error).message; this.logger.warn(`DKV SMTP send attempt ${attempt}/3 failed: ${smtpError}`); if (attempt < 3) { - await _delay(Math.pow(2, attempt) * 1000); // 2s, 4s + await _delay(2 ** attempt * 1000); // 2s, 4s } else { smtpStatus = 'Versand fehlgeschlagen'; // D-16: file stays locally available for manual download @@ -707,7 +707,7 @@ export class DkvService { filename.includes('/') || filename.includes('\\') || filename.includes('..') || - !/^(RG-DKV-|DKV_)[\w\-]+\.xlsx$/.test(filename) + !/^(RG-DKV-|DKV_)[\w-]+\.xlsx$/.test(filename) ) { throw new BadRequestException('Invalid export filename'); } @@ -806,9 +806,9 @@ export class DkvService { ): string { if (fromPdf) return fromPdf.replace(/\//g, '-'); // DKV email subjects carry the invoice number with slashes: "26/650869002/002" - const m = subject?.match(/(\d{2}[\/\-]\d{9}[\/\-]\d{3})/); + const m = subject?.match(/(\d{2}[/-]\d{9}[/-]\d{3})/); if (m?.[1]) return m[1].replace(/\//g, '-'); - return `email-${String(uid).replace(/[^a-zA-Z0-9\-]/g, '_')}`; + return `email-${String(uid).replace(/[^a-zA-Z0-9-]/g, '_')}`; } /** @@ -880,5 +880,5 @@ function _formatDateYYMMDD(date: string): string { * Example: "GP JL 740E" == "GP-JL 740E" == "GPJL740E" after normalization. */ function _normalizeKennzeichen(k: string): string { - return k.toUpperCase().replace(/[\s\-\.]/g, ''); + return k.toUpperCase().replace(/[\s\-.]/g, ''); } diff --git a/apps/api/src/favorites/icon-discovery.service.ts b/apps/api/src/favorites/icon-discovery.service.ts index 90de12a..a6a7e32 100644 --- a/apps/api/src/favorites/icon-discovery.service.ts +++ b/apps/api/src/favorites/icon-discovery.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { lookup } from 'dns/promises'; -import { isIP } from 'net'; +import { lookup } from 'node:dns/promises'; +import { isIP } from 'node:net'; import { Agent, fetch as undiciFetch, type Response as UndiciResponse } from 'undici'; /** diff --git a/apps/api/src/groups/groups.service.spec.ts b/apps/api/src/groups/groups.service.spec.ts index 068ee26..25a5822 100644 --- a/apps/api/src/groups/groups.service.spec.ts +++ b/apps/api/src/groups/groups.service.spec.ts @@ -1044,7 +1044,7 @@ describe('GroupsService — Bindung an forTenant()/withTenantTransaction() (2609 it('reassignDefaultBeforeDelete() bindet die drei Lesezugriffe UND die Transaktion an denselben Mandanten', async () => { const prisma = makeFakePrisma(); const service = new GroupsService(prisma as any); - const def = await service.create('t1', { name: DEFAULT_GROUP_NAME }); + await service.create('t1', { name: DEFAULT_GROUP_NAME }); const toDelete = await service.create('t1', { name: 'Zu loeschen' }); await service.update('t1', toDelete.id, { isDefault: true }); diff --git a/apps/api/src/groups/groups.service.ts b/apps/api/src/groups/groups.service.ts index 2db039d..aa1dd08 100644 --- a/apps/api/src/groups/groups.service.ts +++ b/apps/api/src/groups/groups.service.ts @@ -440,7 +440,7 @@ export class GroupsService { const group = await tenantPrisma.group.findFirst({ where: { id: groupId, tenantId }, }); - if (!group || !group.isDefault) { + if (!group?.isDefault) { return false; } diff --git a/apps/api/src/inbox/imap.provider.spec.ts b/apps/api/src/inbox/imap.provider.spec.ts index 40f06c3..e9246d1 100644 --- a/apps/api/src/inbox/imap.provider.spec.ts +++ b/apps/api/src/inbox/imap.provider.spec.ts @@ -20,7 +20,7 @@ vi.mock('imapflow', () => ({ })); function makeReadable(text: string): NodeJS.ReadableStream { - const { Readable } = require('stream') as typeof import('stream'); + const { Readable } = require('node:stream') as typeof import('stream'); return Readable.from([Buffer.from(text, 'utf8')]); } diff --git a/apps/api/src/ldap/ldap-config.service.spec.ts b/apps/api/src/ldap/ldap-config.service.spec.ts index c3762f4..e3dda80 100644 --- a/apps/api/src/ldap/ldap-config.service.spec.ts +++ b/apps/api/src/ldap/ldap-config.service.spec.ts @@ -84,7 +84,7 @@ describe('LdapConfigService — Bind-Passwort verschluesselt at rest', () => { const written = prisma.ldapConfig.create.mock.calls[0][0].data; expect(written.encryptedBindPassword).toBe( - 'aa11:bb22:' + Buffer.from('geheim').toString('hex'), + `aa11:bb22:${Buffer.from('geheim').toString('hex')}`, ); expect(JSON.stringify(written)).not.toContain('geheim'); // Die alte Klartext-Spalte darf nicht wieder auftauchen. @@ -95,7 +95,7 @@ describe('LdapConfigService — Bind-Passwort verschluesselt at rest', () => { await service.updateConfig('t1', { bindPassword: 'neu' } as any); const first = prisma.ldapConfig.update.mock.calls[0][0].data; expect(first.encryptedBindPassword).toBe( - 'aa11:bb22:' + Buffer.from('neu').toString('hex'), + `aa11:bb22:${Buffer.from('neu').toString('hex')}`, ); await service.updateConfig('t1', { serverUrl: 'ldap://anders' } as any); @@ -113,7 +113,7 @@ describe('LdapConfigService — Bind-Passwort verschluesselt at rest', () => { it('gibt Aufrufern weiterhin ein entschluesseltes bindPassword', async () => { prisma.ldapConfig.findUnique.mockResolvedValue({ ...CONFIG_ROW, - encryptedBindPassword: 'aa11:bb22:' + Buffer.from('geheim').toString('hex'), + encryptedBindPassword: `aa11:bb22:${Buffer.from('geheim').toString('hex')}`, }); const config: any = await service.getConfig('t1'); @@ -156,7 +156,7 @@ describe('LdapConfigService — Bind-Passwort verschluesselt at rest', () => { { id: 'b', tenantId: 't2', - encryptedBindPassword: 'aa11:bb22:' + Buffer.from('schon').toString('hex'), + encryptedBindPassword: `aa11:bb22:${Buffer.from('schon').toString('hex')}`, }, { id: 'c', tenantId: 't3', encryptedBindPassword: null }, ]); @@ -167,7 +167,7 @@ describe('LdapConfigService — Bind-Passwort verschluesselt at rest', () => { const call = prisma.ldapConfig.update.mock.calls[0][0]; expect(call.where).toEqual({ id: 'a' }); expect(call.data.encryptedBindPassword).toBe( - 'aa11:bb22:' + Buffer.from('klartext').toString('hex'), + `aa11:bb22:${Buffer.from('klartext').toString('hex')}`, ); }); @@ -176,7 +176,7 @@ describe('LdapConfigService — Bind-Passwort verschluesselt at rest', () => { { id: 'a', tenantId: 't1', - encryptedBindPassword: 'aa11:bb22:' + Buffer.from('x').toString('hex'), + encryptedBindPassword: `aa11:bb22:${Buffer.from('x').toString('hex')}`, }, ]); @@ -354,7 +354,7 @@ describe('LdapConfigService — Bindung an forTenant() (260909-ipc)', () => { const call = boundClient.ldapConfig.update.mock.calls[0][0]; expect(call.where).toEqual({ id: 'alt' }); expect(call.data.encryptedBindPassword).toBe( - 'aa11:bb22:' + Buffer.from('klartext').toString('hex'), + `aa11:bb22:${Buffer.from('klartext').toString('hex')}`, ); // Der rohe Client schreibt NICHT. expect(prisma.ldapConfig.update).not.toHaveBeenCalled(); diff --git a/apps/api/src/ldap/ldap.service.spec.ts b/apps/api/src/ldap/ldap.service.spec.ts index f8012bf..3bd9743 100644 --- a/apps/api/src/ldap/ldap.service.spec.ts +++ b/apps/api/src/ldap/ldap.service.spec.ts @@ -1545,7 +1545,7 @@ describe('LdapService.syncBoundGroupsForTenant — Rekonziliation gegen das Verz isDefault: false, }, ]; - prisma.group.update = vi.fn((args: any) => { + prisma.group.update = vi.fn((_args: any) => { const err: any = new Error('Unique constraint'); err.code = 'P2002'; // A P2002 on the OTHER unique index this update() can hit — @@ -1793,7 +1793,7 @@ describe('LdapService.syncBoundGroupsForTenant — Rekonziliation gegen das Verz ]; mockSearch.mockImplementation((_baseDn: string, opts: any) => { const swept = sweptGuid(opts.filter); - if (swept && swept.equals(guidBuffer)) { + if (swept?.equals(guidBuffer)) { return Promise.reject(new Error('directory unavailable')); } return Promise.resolve({ diff --git a/apps/api/src/ldap/ldap.service.ts b/apps/api/src/ldap/ldap.service.ts index 6be1ed9..0142287 100644 --- a/apps/api/src/ldap/ldap.service.ts +++ b/apps/api/src/ldap/ldap.service.ts @@ -322,13 +322,13 @@ export class LdapService { const dn = entry.dn; const isOu = /^ou=/i.test(dn); const record = entry as unknown as Record; - const rawName = isOu ? record['ou'] : record['cn']; + const rawName = isOu ? record.ou : record.cn; const name = Array.isArray(rawName) ? String(rawName[0]) : rawName ? String(rawName) : dn; - const guidValue = record['objectGUID']; + const guidValue = record.objectGUID; const guidHex = !isOu && Buffer.isBuffer(guidValue) ? guidValue.toString('hex') @@ -401,7 +401,7 @@ export class LdapService { mappedData[mapping.tesseraField] = String(resolved); } } - return { username: mappedData['username']?.toLowerCase(), mappedData }; + return { username: mappedData.username?.toLowerCase(), mappedData }; } /** @@ -483,13 +483,13 @@ export class LdapService { if (existing) { let emailToWrite: string | undefined; let emailConflict: LdapEmailConflict | undefined; - if (mappedData['email']) { + if (mappedData.email) { const decision = await this.resolveEmailForWrite( - mappedData['email'], + mappedData.email, existing.id, ); if (decision.collides) { - emailConflict = { account: username, email: mappedData['email'] }; + emailConflict = { account: username, email: mappedData.email }; } else { emailToWrite = decision.email; } @@ -498,11 +498,11 @@ export class LdapService { await tenantPrisma.user.update({ where: { id: existing.id }, data: { - ...(mappedData['displayName'] && { - displayName: mappedData['displayName'], + ...(mappedData.displayName && { + displayName: mappedData.displayName, }), ...(emailToWrite && { email: emailToWrite }), - ...(mappedData['username'] && { username }), + ...(mappedData.username && { username }), ldapDn: dn, isActive: true, }, @@ -511,16 +511,16 @@ export class LdapService { } let createEmail: string | undefined = - mappedData['email'] || `${username}@ldap.local`; + mappedData.email || `${username}@ldap.local`; let emailConflict: LdapEmailConflict | undefined; - if (mappedData['email']) { + if (mappedData.email) { const decision = await this.resolveEmailForWrite( - mappedData['email'], + mappedData.email, null, ); if (decision.collides) { createEmail = undefined; - emailConflict = { account: username, email: mappedData['email'] }; + emailConflict = { account: username, email: mappedData.email }; } else { createEmail = decision.email; } @@ -529,7 +529,7 @@ export class LdapService { await this.userService.create({ username, ...(createEmail && { email: createEmail }), - displayName: mappedData['displayName'], + displayName: mappedData.displayName, role: 'USER', tenantId, ldapDn: dn, @@ -592,9 +592,9 @@ export class LdapService { const entries = Array.from(entriesByDn.values()).map((entry) => ({ dn: entry.dn, - username: first(entry['sAMAccountName']), - displayName: first(entry['displayName']) || first(entry['cn']), - email: first(entry['mail']), + username: first(entry.sAMAccountName), + displayName: first(entry.displayName) || first(entry.cn), + email: first(entry.mail), })); // Flag entries already present for this tenant (by ldapDn or username) in @@ -724,10 +724,10 @@ export class LdapService { // account is still created and counted, this manual-import path's // display stays as-is. let createEmail: string | undefined = - mappedData['email'] || `${username}@ldap.local`; - if (mappedData['email']) { + mappedData.email || `${username}@ldap.local`; + if (mappedData.email) { const decision = await this.resolveEmailForWrite( - mappedData['email'], + mappedData.email, null, ); createEmail = decision.collides ? undefined : decision.email; @@ -736,7 +736,7 @@ export class LdapService { await this.userService.create({ username, ...(createEmail && { email: createEmail }), - displayName: mappedData['displayName'], + displayName: mappedData.displayName, role: 'USER', tenantId, ldapDn: dn, @@ -813,14 +813,14 @@ export class LdapService { const entry = searchEntries[0]; const record = entry as unknown as Record; - const guidValue = record['objectGUID']; + const guidValue = record.objectGUID; if (!Buffer.isBuffer(guidValue)) { result.errors.push(`${dn}: objectGUID not readable`); continue; } const ldapObjectGuid = guidValue.toString('hex'); - const rawName = record['cn']; + const rawName = record.cn; const name = Array.isArray(rawName) ? String(rawName[0]) : rawName @@ -1436,7 +1436,7 @@ export class LdapService { string, unknown >; - const backfillGuid = backfillRecord['objectGUID']; + const backfillGuid = backfillRecord.objectGUID; if (!Buffer.isBuffer(backfillGuid)) { result.errors.push( `Gruppe ${group.name}: Alt-Bindung ${group.ldapDn} ohne lesbaren objectGUID`, @@ -1492,7 +1492,7 @@ export class LdapService { if (hit) { // 3. Rename/DN reconciliation (SC-3). const hitRecord = hit as unknown as Record; - const rawName = hitRecord['cn']; + const rawName = hitRecord.cn; const name = Array.isArray(rawName) ? String(rawName[0]) : rawName diff --git a/apps/api/src/prisma/prisma-tenant.extension.spec.ts b/apps/api/src/prisma/prisma-tenant.extension.spec.ts index 02f74e3..9a61ceb 100644 --- a/apps/api/src/prisma/prisma-tenant.extension.spec.ts +++ b/apps/api/src/prisma/prisma-tenant.extension.spec.ts @@ -76,7 +76,7 @@ describe('forTenant() — Array-Form von $transaction (WINDOWS #20)', () => { const scoped = forTenant(fakePrisma, 'tenant-a') as any; let queryCallCount = 0; - const query = (args: unknown) => { + const query = (_args: unknown) => { queryCallCount += 1; return fakeQueryResult; }; @@ -97,7 +97,7 @@ describe('forTenant() — Array-Form von $transaction (WINDOWS #20)', () => { it('setzt den Mandantenkontext ueber ein getaggtes $executeRaw-Template, nicht ueber zusammengebauten Text', async () => { const fakePrisma: any = { - $transaction: vi.fn((arg: unknown) => Promise.resolve(['set-config-result', 'query-result'])), + $transaction: vi.fn((_arg: unknown) => Promise.resolve(['set-config-result', 'query-result'])), $extends: (config: any) => ({ async __invoke(args: unknown, query: (args: unknown) => unknown) { return config.query.$allOperations({ args, query }); @@ -286,7 +286,7 @@ describe('withTenantTransaction() — interaktive Callback-Form auf dem UNgebund it('setzt den Mandantenkontext als erste Anweisung DIREKT AUF tx, nicht auf dem aeusseren Client', async () => { const setConfigCalls: unknown[] = []; const fakeTx: any = { - $executeRaw: vi.fn((strings: TemplateStringsArray, ...values: unknown[]) => { + $executeRaw: vi.fn((_strings: TemplateStringsArray, ...values: unknown[]) => { setConfigCalls.push(values); return Promise.resolve(1); }), diff --git a/apps/api/src/tenders/adapters/cosinex.adapter.spec.ts b/apps/api/src/tenders/adapters/cosinex.adapter.spec.ts index 852ebc0..fc4eeef 100644 --- a/apps/api/src/tenders/adapters/cosinex.adapter.spec.ts +++ b/apps/api/src/tenders/adapters/cosinex.adapter.spec.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { CosinexAdapter } from './cosinex.adapter'; diff --git a/apps/api/src/tenders/adapters/doe-opendata.adapter.spec.ts b/apps/api/src/tenders/adapters/doe-opendata.adapter.spec.ts index 1b2d702..3cb983b 100644 --- a/apps/api/src/tenders/adapters/doe-opendata.adapter.spec.ts +++ b/apps/api/src/tenders/adapters/doe-opendata.adapter.spec.ts @@ -1,6 +1,6 @@ import AdmZip from 'adm-zip'; -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { buildDoeNoticeUrl, DoeOpenDataAdapter } from './doe-opendata.adapter'; diff --git a/apps/api/src/tenders/adapters/email-alert.adapter.spec.ts b/apps/api/src/tenders/adapters/email-alert.adapter.spec.ts index d00ca51..496cdc6 100644 --- a/apps/api/src/tenders/adapters/email-alert.adapter.spec.ts +++ b/apps/api/src/tenders/adapters/email-alert.adapter.spec.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { Logger } from '@nestjs/common'; import { describe, expect, it, vi } from 'vitest'; import { diff --git a/apps/api/src/tenders/adapters/email-alert.adapter.ts b/apps/api/src/tenders/adapters/email-alert.adapter.ts index 62ad7e7..b0c270a 100644 --- a/apps/api/src/tenders/adapters/email-alert.adapter.ts +++ b/apps/api/src/tenders/adapters/email-alert.adapter.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { CryptoService } from '../../crypto/crypto.service'; import * as cheerio from 'cheerio'; -import { createHash } from 'crypto'; +import { createHash } from 'node:crypto'; import { ExchangeInboxProvider } from '../../inbox/exchange-inbox.provider'; import { ImapProvider } from '../../inbox/imap.provider'; diff --git a/apps/api/src/tenders/adapters/netserver.adapter.spec.ts b/apps/api/src/tenders/adapters/netserver.adapter.spec.ts index 8ec3f1a..a3caef0 100644 --- a/apps/api/src/tenders/adapters/netserver.adapter.spec.ts +++ b/apps/api/src/tenders/adapters/netserver.adapter.spec.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { NetServerAdapter } from './netserver.adapter'; diff --git a/apps/api/src/tenders/adapters/rss.adapter.spec.ts b/apps/api/src/tenders/adapters/rss.adapter.spec.ts index 8d3ef84..ca57c03 100644 --- a/apps/api/src/tenders/adapters/rss.adapter.spec.ts +++ b/apps/api/src/tenders/adapters/rss.adapter.spec.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { RssAdapter } from './rss.adapter'; diff --git a/apps/api/src/tenders/adapters/rss.adapter.ts b/apps/api/src/tenders/adapters/rss.adapter.ts index fddda3b..2aae868 100644 --- a/apps/api/src/tenders/adapters/rss.adapter.ts +++ b/apps/api/src/tenders/adapters/rss.adapter.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { createHash } from 'crypto'; +import { createHash } from 'node:crypto'; import { XMLParser } from 'fast-xml-parser'; import { PrismaService } from '../../prisma/prisma.service'; import type { RawTenderRecord, SourceType } from '../tender.types'; diff --git a/apps/api/src/tenders/tender-digest.scheduler.spec.ts b/apps/api/src/tenders/tender-digest.scheduler.spec.ts index cba42fd..0c9b7ed 100644 --- a/apps/api/src/tenders/tender-digest.scheduler.spec.ts +++ b/apps/api/src/tenders/tender-digest.scheduler.spec.ts @@ -235,8 +235,8 @@ describe('TenderDigestScheduler — Multi-Tenant via findMany (Pitfall 1)', () = }); it('never uses findFirst anywhere in the module source — findMany over all due users is mandatory (Pitfall 1)', async () => { - const { readFileSync } = await import('fs'); - const { join } = await import('path'); + const { readFileSync } = await import('node:fs'); + const { join } = await import('node:path'); const source = readFileSync(join(__dirname, 'tender-digest.scheduler.ts'), 'utf8'); expect(source).not.toMatch(/findFirst/); }); diff --git a/apps/api/src/tenders/tender-digest.scheduler.ts b/apps/api/src/tenders/tender-digest.scheduler.ts index 23f5ef8..5c732f9 100644 --- a/apps/api/src/tenders/tender-digest.scheduler.ts +++ b/apps/api/src/tenders/tender-digest.scheduler.ts @@ -168,7 +168,7 @@ export class TenderDigestScheduler implements OnModuleInit { // Kein Konto, oder ein Konto ohne Adresse (WINDOWS #15, kollidierte // AD-Adresse) -- die Zugehoerigkeit funktioniert, nur der // Mailversand wird uebersprungen (zugesagtes Verhalten). - if (!user || !user.email) continue; + if (!user?.email) continue; const sections = groupMatchesByProfile(matches); const sent = await this.mail.sendDigest({ email: user.email }, user.tenantId, sections); diff --git a/apps/api/src/tenders/tender-fingerprint.ts b/apps/api/src/tenders/tender-fingerprint.ts index ceeb8dc..bd78b27 100644 --- a/apps/api/src/tenders/tender-fingerprint.ts +++ b/apps/api/src/tenders/tender-fingerprint.ts @@ -1,4 +1,4 @@ -import { createHash } from 'crypto'; +import { createHash } from 'node:crypto'; /** * tenderFingerprint — pure, deterministic cross-source dedup key (SCHEMA-03, diff --git a/apps/api/src/tenders/tender-ingestion.service.spec.ts b/apps/api/src/tenders/tender-ingestion.service.spec.ts index edd8461..bd709fb 100644 --- a/apps/api/src/tenders/tender-ingestion.service.spec.ts +++ b/apps/api/src/tenders/tender-ingestion.service.spec.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { describe, expect, it, vi } from 'vitest'; import { TenderIngestionService } from './tender-ingestion.service'; diff --git a/apps/api/src/tenders/tender-matching.service.ts b/apps/api/src/tenders/tender-matching.service.ts index 082a7dc..42b1142 100644 --- a/apps/api/src/tenders/tender-matching.service.ts +++ b/apps/api/src/tenders/tender-matching.service.ts @@ -150,7 +150,7 @@ export class TenderMatchingService { // Kein Konto, oder ein Konto ohne Adresse (WINDOWS #15, kollidierte // AD-Adresse) -- die Zugehoerigkeit funktioniert, nur der // Mailversand wird uebersprungen (zugesagtes Verhalten). - if (!user || !user.email) continue; + if (!user?.email) continue; const sent = await this.mail.sendInstant( { email: user.email }, diff --git a/apps/api/src/tenders/tender-normalizer.service.spec.ts b/apps/api/src/tenders/tender-normalizer.service.spec.ts index a4b3afa..52f94e7 100644 --- a/apps/api/src/tenders/tender-normalizer.service.spec.ts +++ b/apps/api/src/tenders/tender-normalizer.service.spec.ts @@ -1,7 +1,7 @@ import AdmZip from 'adm-zip'; import { XMLParser } from 'fast-xml-parser'; -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { describe, expect, it } from 'vitest'; import type { RawTenderRecord, SourceType } from './tender.types'; import { TenderNormalizerService } from './tender-normalizer.service'; diff --git a/apps/api/src/tenders/tender-normalizer.service.ts b/apps/api/src/tenders/tender-normalizer.service.ts index fede804..088be10 100644 --- a/apps/api/src/tenders/tender-normalizer.service.ts +++ b/apps/api/src/tenders/tender-normalizer.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { createHash } from 'crypto'; +import { createHash } from 'node:crypto'; import { divisionOf } from './cpv/cpv-catalog'; import { bundeslandFromRegion } from './geo/nuts-bundesland'; import type { NormalizedTenderFields, RawTenderRecord } from './tender.types'; diff --git a/apps/api/src/tenders/tender-rss-feed.service.spec.ts b/apps/api/src/tenders/tender-rss-feed.service.spec.ts index 6250e95..c9c503e 100644 --- a/apps/api/src/tenders/tender-rss-feed.service.spec.ts +++ b/apps/api/src/tenders/tender-rss-feed.service.spec.ts @@ -64,7 +64,7 @@ function makeFakePrisma() { const tenderRssFeedSource = { findMany: async ({ where, orderBy }: any = {}) => { - let all = [...rows.values()].filter((row) => matchesWhere(row, where)); + const all = [...rows.values()].filter((row) => matchesWhere(row, where)); if (orderBy?.createdAt === 'asc') { all.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()); } diff --git a/apps/api/src/tenders/tender-scheduler.service.spec.ts b/apps/api/src/tenders/tender-scheduler.service.spec.ts index f505728..ed6b143 100644 --- a/apps/api/src/tenders/tender-scheduler.service.spec.ts +++ b/apps/api/src/tenders/tender-scheduler.service.spec.ts @@ -1,5 +1,5 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { describe, expect, it, vi } from 'vitest'; import { ModuleRegistryService } from '../module-registry/module-registry.service'; import { TenderSchedulerService } from './tender-scheduler.service'; diff --git a/apps/api/src/user/user.controller.ts b/apps/api/src/user/user.controller.ts index b765f17..b7fa970 100644 --- a/apps/api/src/user/user.controller.ts +++ b/apps/api/src/user/user.controller.ts @@ -16,8 +16,8 @@ import { } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { Role } from '@prisma/client'; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { Response } from 'express'; import { CurrentUser } from '../auth/decorators/current-user.decorator'; import { Roles } from '../auth/decorators/roles.decorator'; @@ -293,7 +293,7 @@ export class UserController { @UploadedFile() file: any, @CurrentUser() currentUser: any, ) { - if (!file || !file.buffer) { + if (!file?.buffer) { throw new BadRequestException('No file provided'); } diff --git a/apps/desktop/src/setup.html b/apps/desktop/src/setup.html index 312d737..6e631cb 100644 --- a/apps/desktop/src/setup.html +++ b/apps/desktop/src/setup.html @@ -312,12 +312,12 @@ return; } - showInfo('Tessera ' + version + ' gefunden – Verbindung wird hergestellt …'); + showInfo(`Tessera ${version} gefunden – Verbindung wird hergestellt …`); try { await invoke('save_server_url', { url: normalizedUrl }); } catch (err) { - showError('Die Adresse konnte nicht gespeichert werden: ' + String(err)); + showError(`Die Adresse konnte nicht gespeichert werden: ${String(err)}`); connectBtn.disabled = false; connectBtn.textContent = 'Verbinden'; } @@ -344,7 +344,7 @@ */ function enterChangeMode(current) { urlInput.value = current; - currentServer.textContent = 'Aktuell verbunden mit: ' + current; + currentServer.textContent = `Aktuell verbunden mit: ${current}`; currentServer.style.display = 'block'; subtitle.textContent = 'Server-Adresse ändern'; cancelBtn.hidden = false; diff --git a/apps/web/src/app/(auth)/login/page.tsx b/apps/web/src/app/(auth)/login/page.tsx index 2e49a96..bab78d0 100644 --- a/apps/web/src/app/(auth)/login/page.tsx +++ b/apps/web/src/app/(auth)/login/page.tsx @@ -2,7 +2,6 @@ import { useState, useTransition } from 'react'; import { useTranslations } from 'next-intl'; -import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { login } from '@/lib/auth-actions'; import { sanitizeNextPath } from '@/lib/safe-next'; @@ -18,7 +17,6 @@ import { DesktopDownloadLinks } from '@/components/desktop/desktop-download-link */ export default function LoginPage() { const t = useTranslations('auth'); - const router = useRouter(); const [isPending, startTransition] = useTransition(); const [error, setError] = useState(null); diff --git a/apps/web/src/app/(portal)/change-password/page.tsx b/apps/web/src/app/(portal)/change-password/page.tsx index 1a14ad0..adcdfc4 100644 --- a/apps/web/src/app/(portal)/change-password/page.tsx +++ b/apps/web/src/app/(portal)/change-password/page.tsx @@ -2,14 +2,10 @@ import { useState, useTransition, useEffect } from 'react'; import { useTranslations } from 'next-intl'; -import { useRouter } from 'next/navigation'; import { fetchCurrentUser, changePasswordAction } from '@/lib/auth-actions'; -import { useAuthStore } from '@/lib/stores/auth-store'; export default function ChangePasswordPage() { const t = useTranslations('auth'); - const router = useRouter(); - const { user, setUser } = useAuthStore(); const [isPending, startTransition] = useTransition(); const [error, setError] = useState(null); const [passwordMismatch, setPasswordMismatch] = useState(false); diff --git a/apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx b/apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx index cbb09dd..dfacb06 100644 --- a/apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx +++ b/apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx @@ -1,4 +1,4 @@ -import { cleanup, render, screen, fireEvent, waitFor, act } from '@testing-library/react'; +import { cleanup, render, screen, fireEvent, waitFor } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; // Mock next-intl — provide certManager namespace keys diff --git a/apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx b/apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx index cc581bc..f35d235 100644 --- a/apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx +++ b/apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx @@ -17,7 +17,7 @@ const ROLE_STYLES: Record = { 'end-entity': 'bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300', }; -function downloadAllAsZip(certs: SplitResponse['certs'], t: (k: string) => string) { +function downloadAllAsZip(certs: SplitResponse['certs']) { const files: Record = {}; for (const cert of certs) { const bytes = Uint8Array.from(atob(cert.content), (c) => c.charCodeAt(0)); @@ -79,7 +79,7 @@ export function SplitTab({ file, pemText: _pemText, password: _password }: Split {result && result.certs.length > 1 && (