Files
tessera-ctl/.planning/phases/07-dkv-fleet-module/07-REVIEW-FIX.md
T
schalli 071531f880
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
docs(07): add code review fix report
2026-06-27 17:23:37 +02:00

4.6 KiB

phase, fixed_at, review_path, iteration, findings_in_scope, fixed, skipped, status
phase fixed_at review_path iteration findings_in_scope fixed skipped status
07-dkv-fleet-module 2026-06-27T15:45:00Z .planning/phases/07-dkv-fleet-module/07-REVIEW.md 1 8 8 0 all_fixed

Phase 07: Code Review Fix Report

Fixed at: 2026-06-27T15:45:00Z Source review: .planning/phases/07-dkv-fleet-module/07-REVIEW.md Iteration: 1

Summary:

  • Findings in scope: 8 (3 Critical, 5 Warning)
  • Fixed: 8
  • Skipped: 0

Fixed Issues

CR-01: CSV import count field mismatch — import count always undefined

Files modified: apps/api/src/dkv/dkv.service.ts, apps/web/src/lib/dkv-api.ts Commit: f3f610f Applied fix: Renamed the backend return field from imported to count in dkv.service.ts line 501. Updated the TypeScript return type in dkv-api.ts to Promise<{ count: number; mode: string }> for completeness. The frontend already read result.count and onImported(result.count), so both call sites now resolve correctly without further changes.


CR-02: Cron expression silently incorrect for pollIntervalMin > 59

Files modified: apps/api/src/dkv/dkv-scheduler.service.ts, apps/api/src/dkv/dto/dkv-config.dto.ts Commit: 955a946 Applied fix: Replaced the unconditional */${intervalMin} * * * * expression with a conditional: intervals below 60 use the minute field (*/${intervalMin} * * * *), intervals 60 and above use the hours field (0 */${hours} * * * where hours = Math.floor(intervalMin / 60)). Also added @Max(1440) (24 h) to DkvConfigDto.pollIntervalMin to prevent unbounded values.


CR-03: IMAP connection not closed when mailbox lock fails

Files modified: apps/api/src/dkv/providers/imap.provider.ts Commit: cb0d378 Applied fix: Moved client.getMailboxLock() inside the try block. Declared lock as let lock: ... | null = null before the try so the type is available in finally. Changed lock.release() to lock?.release() so the finally block handles the case where lock acquisition never succeeded. client.logout() is now always called regardless of whether getMailboxLock threw.


WR-01: nodemailer transport not closed — connection pool leak

Files modified: apps/api/src/dkv/dkv-mail.service.ts Commit: ded6523 Applied fix: Added finally { transport.close(); } to the existing try/catch block in sendExportEmail. The transport is now always closed after each send attempt (success or failure), preventing SMTP connection pool accumulation under repeated sends with retry backoff.


WR-02: Pagination query params not coerced to numbers — @IsInt() silently fails

Files modified: apps/api/src/dkv/dto/dkv-history.dto.ts Commit: 49eab55 Applied fix: Added import { Type } from 'class-transformer' and decorated both page and limit fields with @Type(() => Number). Also added @Max(100) to limit to bound the result-set size. HTTP query string values are now coerced from string to number before @IsInt() / @Min() validators run.


WR-03: formatDateTime never catches — invalid dates produce "NaN.NaN.NaN, NaN:NaN Uhr"

Files modified: apps/web/src/app/(portal)/modules/dkv-fleet/components/InvoiceHistoryTable.tsx Commit: 338655c Applied fix: Replaced the dead try/catch wrapper with an explicit isNaN(d.getTime()) guard. new Date() never throws — it returns an Invalid Date object — so the catch branch was unreachable. The fix returns the raw isoString as fallback when the date is invalid, preventing the "NaN.NaN.NaN, NaN:NaN Uhr" display.


WR-04: Exchange UniqueId can contain path characters in generated export filename

Files modified: apps/api/src/dkv/dkv.service.ts Commit: 9de16ba Applied fix: Added .replace(/[^a-zA-Z0-9\-]/g, '_') sanitisation to the _extractInvoiceNumber fallback return value. Exchange EWS UniqueIds are base64 and can contain +, /, and =; a / would cause path.join(userFilesDir, filename) to resolve into a subdirectory, making writeFileSync fail. The sanitisation strips all non-alphanumeric/hyphen characters to _ before the value reaches the filesystem path.


WR-05: No file size limit on CSV multipart upload

Files modified: apps/api/src/dkv/dkv.controller.ts Commit: d2d224c Applied fix: Added limits: { fileSize: 5 * 1024 * 1024 } (5 MB) to the FileInterceptor options for the POST /dkv/vehicles/import endpoint. multer now rejects any upload exceeding 5 MB before Node.js buffers the content, preventing a heap exhaustion via oversized file upload.


Fixed: 2026-06-27T15:45:00Z Fixer: Claude (gsd-code-fixer) Iteration: 1