- 07-03-SUMMARY.md: all 4 tasks documented, threat mitigations verified, user-files/ path resolution and MailModule factory priority chain explained - STATE.md: advanced to Plan 4 of 6, added 5 new decisions, metrics row - ROADMAP.md: 07-03 checked complete, Phase 7 progress 3/6
9.8 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07-dkv-fleet-module | 03 | dkv-export-smtp-settings |
|
|
|
|
|
|
Phase 07 Plan 03: DKV Export + SMTP Settings — Summary
SettingsModule (SMTP config backend), DkvExportService (xlsx generation + 10-file prune), DkvMailService (runtime nodemailer transport per send), and migrated MailModule (DB SmtpConfig priority with env fallback — D-06).
Tasks Completed
| Task | Name | Commit | Key Files |
|---|---|---|---|
| 1 | SettingsModule — SMTP config backend + connection test (DKV-05) | 1bec0e7 |
settings.module.ts, settings.controller.ts, settings.service.ts, dto/smtp-config.dto.ts, app.module.ts |
| 2 | DkvExportService — xlsx generation + user-files/ prune (DKV-04) | 6b76ca9 |
dkv-export.service.ts |
| 3 | DkvMailService — runtime SMTP transport with attachment (DKV-04) | 4deefb5 |
dkv-mail.service.ts |
| 4 | Migrate MailModule to DB-sourced SMTP transport with env fallback (D-06) | de48e35 |
mail.module.ts |
Architecture Notes
user-files/ Path Resolution
DkvExportService resolves the path using:
path.resolve(__dirname, '..', '..', '..', '..', 'user-files')
__dirname at runtime is apps/api/dist/dkv/. Going up 4 levels: dkv/ → dist/ → api/ → apps/ → <repo-root>/. This reaches user-files/ at the monorepo root regardless of the process working directory. The path is never derived from request input (T-07-09).
Export File Prune Ordering
Files matching DKV_*.xlsx in user-files/ are collected, sorted by mtime ascending (oldest modification time first), and all entries beyond index 9 (i.e., beyond the last 10) are deleted via fs.unlinkSync. The writeAndPrune method handles write + prune atomically within a single synchronous call — the caller's processing lock (Plan 04) prevents interleaved operations (Pitfall 7).
MailModule Factory Transport Resolution (D-06)
The MailerModule.forRootAsync factory is async and resolves the transport in this priority order:
- DB SmtpConfig (
settingsService.getStartupSmtpConfig()→prisma.smtpConfig.findFirst()): used when any row exists. In single-tenant deployments, the one SmtpConfig row serves as the system mail transport. Decrypted password is used only to build the transport object — never logged (T-07-11). - Env vars
MAIL_HOST/MAIL_PORT/MAIL_USER/MAIL_PASS: used when no DB row exists. - Legacy env vars
TESSERA_SMTP_HOST/TESSERA_SMTP_PORT/TESSERA_SMTP_USER/TESSERA_SMTP_PASSWORD: secondary fallback preserving backward compatibility. - Hardcoded
localhost:1025: final fallback for local dev (Mailhog).
No circular import exists: MailModule → SettingsModule → CalendarModule — no reverse edges.
DkvMailService Transport Strategy
Transport is created via nodemailer.createTransport() inside sendExportEmail(), not at module init. This means:
- SMTP config changes in the admin UI take effect on the next send without a service restart.
- Contrast with
MailerModule/MailServicewhich configure transport once at startup. - This is the mandatory pattern for DKV mail per Research Pitfall 3.
Verification Results
pnpm --filter @tessera/api type-checkexits 0: PASSSettingsModuleinapp.module.tsimports: PASSSettingsModuleexportsSettingsService: PASS- All controller handlers carry
@Roles(Role.ADMIN, Role.SUPER_ADMIN): PASS getSmtpConfigpresent in settings.service.ts: PASStestSmtpConfigpresent in settings.service.ts: PASSgetStartupSmtpConfigpresent in settings.service.ts: PASSgetStartupSmtpConfigcalled in mail.module.ts: PASSforRootAsyncin mail.module.ts: PASSMAIL_HOSTenv fallback in mail.module.ts: PASSSettingsModuleimported in mail.module.ts: PASSaoa_to_sheetin dkv-export.service.ts: PASSLieferdatumheader in dkv-export.service.ts: PASS- 5-column header order exactly matching D-13: PASS
MAX_EXPORT_FILES = 10prune limit: PASS- Lieferdatum written as string (no Date wrapping in code): PASS
createTransportin dkv-mail.service.ts: PASS- No
MailerServiceimport in dkv-mail.service.ts: PASS
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] "MailerService" string in Task 3 grep verification
- Found during: Task 3 verification
- Issue: The plan's automated check
! grep -q "MailerService" apps/api/src/dkv/dkv-mail.service.tsfailed because a JSDoc comment contained the string "This service does NOT import or use MailerService from @nestjs-modules/mailer." The grep test is literal string matching and does not distinguish comments from code. - Fix: Replaced the comment wording to "This service uses nodemailer directly — NOT the @nestjs-modules/mailer abstraction." — no MailerService string in the file.
- Files modified: apps/api/src/dkv/dkv-mail.service.ts
- Commit:
4deefb5
Known Stubs
None — all functionality is fully implemented. DkvExportService, DkvMailService, and SettingsService contain no placeholder values, hardcoded empty returns, or TODO items that would block Plan 04 orchestration.
Threat Flags
None. All STRIDE threats in this plan's threat register were mitigated:
- T-07-07:
SMTP_SAFE_SELECTexcludesencryptedPassword; GET returnshasPasswordboolean;encryptedPasswordstripped in controller before returning - T-07-08: All three endpoints (GET, PUT, POST test) carry
@Roles(Role.ADMIN, Role.SUPER_ADMIN);SmtpConfigDtovalidates host/port/encryption - T-07-09:
writeAndPrunefilename built frominvoiceMonth+rechnungsnummerparameters (from parsed PDF), never from HTTP request input - T-07-10:
DkvMailServicecatch block logs only(error as Error).message— no credentials, host, transport details - T-07-11:
getStartupSmtpConfigdecrypts password only to build the transport return object; no logging of the value - T-07-16:
testSmtpConfigreturns{ success: boolean }only; verify() failures logged with generic message (no credentials)
Self-Check: PASSED
Files verified present:
- apps/api/src/settings/dto/smtp-config.dto.ts ✓
- apps/api/src/settings/settings.service.ts ✓
- apps/api/src/settings/settings.controller.ts ✓
- apps/api/src/settings/settings.module.ts ✓
- apps/api/src/dkv/dkv-export.service.ts ✓
- apps/api/src/dkv/dkv-mail.service.ts ✓
- apps/api/src/mail/mail.module.ts (modified) ✓
- apps/api/src/app.module.ts (modified) ✓
Commits verified in git log:
1bec0e7✓ (feat(07-03): SettingsModule — SMTP config backend + connection test)6b76ca9✓ (feat(07-03): DkvExportService — xlsx generation + user-files/ prune)4deefb5✓ (feat(07-03): DkvMailService — runtime nodemailer transport with xlsx attachment)de48e35✓ (feat(07-03): Migrate MailModule to DB-sourced SMTP transport with env fallback)