Commit Graph

12 Commits

Author SHA1 Message Date
schalli 955a94638c fix(07): CR-02 correct cron expression for pollIntervalMin >= 60
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Has started running
Values >=60 in the cron minute field silently misbehave (*/60 fires once per
hour, */90 fires once per hour, etc.). Use the hours field for intervals >=60:
  <60 min  → */N * * * *
  >=60 min → 0 */H * * * (H = floor(N/60))
Also adds @Max(1440) to DkvConfigDto to bound the field at 24 h.
2026-06-27 17:20:11 +02:00
schalli f3f610f9e1 fix(07): CR-01 rename import return field imported→count to match frontend
Tessera CI/CD / Lint & Type Check (push) Failing after 37s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Deploy (push) Has been skipped
Backend dkv.service.ts returned { imported } but frontend read result.count,
causing the success toast to always display "undefined Fahrzeuge importiert".
Align backend field name to count and update the dkv-api.ts return type to
include mode for completeness.
2026-06-27 17:19:44 +02:00
schalli 2a3d1c1e85 feat(07-04): DkvModule + registry seed + AppModule registration + i18n keys
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Waiting to run
- dkv.seed.ts: seedDkvModule with slug=dkv-fleet, category=fleet, isSystem=true
- dkv.module.ts: imports ModuleRegistryModule + CalendarModule (CalendarCryptoService)
  provides all 7 DKV services + 2 providers + controller; OnModuleInit seeds registry
- app.module.ts: DkvModule added to imports (ScheduleModule + SettingsModule from Plans 01/03)
- de.json + en.json: complete dkvFleet namespace (50+ keys incl. status, col, form, errors)
- settings namespace extended with categoryGeneral, categorySmtp, smtp sub-object
2026-06-27 00:30:08 +02:00
schalli c22d36758c feat(07-04): DkvSchedulerService + DkvController — dynamic cron + REST surface
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 36s
Tessera CI/CD / Tests (push) Waiting to run
- DkvSchedulerService: SchedulerRegistry.addCronJob (dynamic interval, not static @Cron)
- onModuleInit loads first active config (v1 single-tenant, documented in SUMMARY)
- setInterval() replaces existing job and registers new one with */ cron expression
- stopJob() removes job when config.isActive=false
- cron package resolved via require() workaround (pnpm strict isolation: transitive dep)
- DkvController: 12 handlers all carrying @Roles(Role.ADMIN, Role.SUPER_ADMIN)
- Routes: GET/PUT config, POST check-now, POST test-connection, GET history,
  GET exports/:filename, GET/POST/PUT/DELETE vehicles, POST vehicles/import
- vehicles/import uses FileInterceptor('file') for CSV multipart upload
- exports/:filename streams file as attachment; traversal guard in DkvService
- Controller coordinates scheduler after PUT /dkv/config (no circular dep)
2026-06-27 00:28:18 +02:00
schalli c40a023321 feat(07-04): DkvService — pipeline orchestration + vehicle/config/history logic
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 38s
Tessera CI/CD / Tests (push) Waiting to run
- Single-flight guard (processing flag) prevents concurrent inbox processing
- processInbox: poll → 3-retry parse → driver-map → xlsx → 3-retry SMTP send → history
- Parse failure (D-10): records Fehler history row with errorMessage
- SMTP failure (D-16): exponential backoff 2s/4s, records Versand fehlgeschlagen
- CONFIG_SAFE_SELECT excludes encryptedInboxCreds (T-07-12)
- getExportFile rejects filenames with path separators or outside DKV_*.xlsx pattern (T-07-09)
- CSV import: merge (upsert by tenantId+kennzeichen) and replace (deleteMany then createMany) modes
- Invoice number extracted from email subject via regex; falls back to email-{uid}
- Vehicle format string resolved via DkvExportService.resolveFahrzeug (D-19)
2026-06-27 00:24:47 +02:00
schalli 4deefb52de feat(07-03): DkvMailService — runtime nodemailer transport with xlsx attachment (DKV-04)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 38s
Tessera CI/CD / Tests (push) Waiting to run
- createTransport() called per-send from DB SmtpConfig (Pitfall 3 mitigation — not at startup)
- Injects SettingsService to load decrypted SMTP config per tenant
- secure/requireTLS mapped from encryption field (ssl-tls / starttls / none)
- Auth omitted when username absent (anonymous relay support)
- Attachment contentType: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- Error path rethrows after generic log (T-07-10) so DkvService can run 3-retry backoff (D-16)
- Does not import @nestjs-modules/mailer abstractions
2026-06-27 00:11:59 +02:00
schalli 6b76ca9633 feat(07-03): DkvExportService — xlsx generation + user-files/ prune (DKV-04)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 41s
Tessera CI/CD / Tests (push) Waiting to run
- buildExcelBuffer: 5-column xlsx per D-13 (Lieferdatum as string, never Date), SheetJS aoa_to_sheet
- resolveFahrzeug: replaces {Marke}/{Modell}/{Kennzeichen}/{Fahrer} tokens in format string (D-19)
- writeAndPrune: server-side filename DKV_YYYY-MM_<nr>.xlsx (T-07-09 path-traversal prevention),
  writes to user-files/ (resolved from monorepo root, not request input), prunes to last 10 DKV_*.xlsx
  files sorted by mtime ascending (D-15, Pitfall 7 atomicity)
2026-06-27 00:10:56 +02:00
schalli 924de76f93 feat(07-02): ExchangeInboxProvider — EWS email inbox access
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 41s
Tessera CI/CD / Tests (push) Waiting to run
- Implements InboxProvider contract (fetchPdfAttachments + testConnection)
- Uses WellKnownFolderName.Inbox + FindItems + EmailMessage.Bind — NOT FindAppointments (Pitfall 6)
- Dynamic import('ews-javascript-api') following ExchangeProvider calendar pattern
- Server-side sender filter via SearchFilter.ContainsSubstring with client-side verification
- 25MB attachment size guard in extractPdfAttachments — PDF-bomb mitigation (T-07-05)
- Generic error messages only in all catch blocks — no credential values (T-07-03)
- Returns [] on error (consistent with calendar ExchangeProvider error path)
2026-06-27 00:03:56 +02:00
schalli b3f21a8747 feat(07-02): ImapProvider — IMAP inbox access via imapflow
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 39s
Tessera CI/CD / Tests (push) Waiting to run
- Implements InboxProvider contract (fetchPdfAttachments + testConnection)
- fetchAll() called before any download() — avoids IMAP connection deadlock (Pitfall 1)
- ImapFlow constructed with logger:false — credential safety (T-07-03)
- 25MB attachment size guard in streamToBuffer — PDF-bomb mitigation (T-07-05)
- collectPdfParts() recursively traverses MIME tree for application/pdf parts
- Generic error messages only — no credential values in logs (T-07-03)
- secure/requireTLS flags derived from encryption field (ssl-tls vs starttls)
2026-06-27 00:02:00 +02:00
schalli 86ec8966d0 feat(07-02): InboxProvider interface + config/vehicle/history DTOs
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Waiting to run
- inbox-provider.interface.ts: InboxProvider contract with fetchPdfAttachments + testConnection; re-exports InboxConfig/InboxEmail/InboxAttachment with export type (isolatedModules)
- dkv-config.dto.ts: DkvConfigDto with @IsEmail() senderFilter/exportRecipient, @Min(5) pollIntervalMin, @IsIn() protocol/encryption (T-07-04)
- dkv-vehicle.dto.ts: CreateVehicleDto (all required @IsNotEmpty) + UpdateVehicleDto (all optional)
- dkv-history.dto.ts: DkvHistoryQueryDto with @IsInt @Min(1) page/limit pagination (T-07-06)
- Fix: export type re-exports required for isolatedModules TypeScript setting
2026-06-27 00:00:14 +02:00
schalli 6235aaf31c feat(07-01): DKV PDF parser validated against real invoice + injectable service
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 41s
Tessera CI/CD / Tests (push) Waiting to run
- dkv-parser.validate.ts: empirical validation script against user-files/invoice.pdf
  - 27 vehicle blocks, 66 transactions extracted (matches expected count)
  - Handles two PDF extraction formats: single-tx (tab-separated) + multi-tx (columnar)
  - German number parsing: replace(/\./g,'').replace(',','.') applied to km and menge
  - Exits 1 with full raw text dump if zero vehicle blocks parsed (assertion guard)
- dkv-parser.service.ts: @Injectable() NestJS service wrapping validated logic
  - parsePdf(buffer: Buffer): Promise<DkvVehicleBlock[]>
  - Uses pdf-parse v2 class API: new PDFParse({data:buffer}) — NOT v1 pdfParse()
  - Calls destroy() after extraction (T-07-01 memory safety)
  - Generic error messages only on parse failure (T-07-02 info disclosure)

Regex adjustment vs Research Pattern 4: space-based regex replaced with
tab-split (single-tx) + columnar transpose (multi-tx) after empirical analysis
of actual invoice.pdf text extraction output.
2026-06-26 19:36:45 +02:00
schalli c4b39ccd1b feat(07-01): install DKV deps, add Prisma models, wire ScheduleModule + crypto export
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 37s
Tessera CI/CD / Tests (push) Waiting to run
- pnpm add imapflow@^1.4.3, pdf-parse@^2.4.5, xlsx@^0.18.5 to @tessera/api
- Append DkvModuleConfig, DkvVehicleMaster, DkvInvoiceHistory, SmtpConfig models to schema.prisma (15 models total)
- Add ScheduleModule.forRoot() to AppModule imports (prerequisite for DkvSchedulerService)
- Export CalendarCryptoService from CalendarModule (needed by DkvModule + SettingsModule)
- Create apps/api/src/dkv/dkv.types.ts with DkvVehicleBlock, DkvTransaction, InboxConfig, InboxEmail, InboxAttachment, ExportRow interfaces
2026-06-26 19:25:42 +02:00