dorfteich/apps/api/src/mail/mail.module.ts
Claude Fable 5 69d9072d2c
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 5m51s
CI / Build container images (pull_request) Successful in 3m4s
CI / Auth e2e pack (pull_request) Successful in 8m4s
CI / Import/export fidelity gate (pull_request) Successful in 57s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
#234: retention for mail_outbox
Sent mails were kept forever, and digest bodies name page titles and
actors — an unbounded copy of content-adjacent data. A new daily
mail-outbox-retention job deletes SENT rows (by sentAt) and permanently
FAILED rows (by nextAttemptAt, the last attempt's stamp) once they pass
mail.outboxRetentionDays (instance setting, default 30). PENDING rows —
including failed-but-retryable ones — stay the retry loop's alone.

Decision recorded (security.md §Privacy, residual-risk note for #231):
digest mails keep carrying page titles for now — there is no per-page
classification marking yet to key a suppression on (ADR 0022 / M32
revisits), and a VS-NfD reference configuration can leave SMTP
unconfigured entirely.

Job-count fence in system.spec: 8 -> 9.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ
2026-07-30 22:17:28 +02:00

51 lines
1.6 KiB
TypeScript

import { Module, OnModuleInit } from '@nestjs/common';
import { CommonModule } from '../common/common.module';
import { SchedulerModule } from '../scheduler/scheduler.module';
import { SchedulerService } from '../scheduler/scheduler.service';
import { SettingsModule } from '../settings/settings.module';
import { MailRetentionService } from './mail-retention.service';
import { MAIL_TRANSPORT, MailTransport, MailWorker } from './mail-worker.service';
import { MailService } from './mail.service';
import { SmtpConfigService } from './smtp-config.service';
/** Daily, per operations.md's maintenance-jobs table (issue #234). */
const OUTBOX_RETENTION_CADENCE_SECONDS = 24 * 60 * 60;
@Module({
imports: [CommonModule, SchedulerModule, SettingsModule],
providers: [
MailService,
MailWorker,
MailRetentionService,
SmtpConfigService,
{
// Delegates per send so the wizard's SMTP changes (SmtpConfigService.
// refresh) take effect without restarting the worker.
provide: MAIL_TRANSPORT,
inject: [SmtpConfigService],
useFactory: (smtp: SmtpConfigService): MailTransport => ({
sendMail: (mail) => smtp.transport().sendMail(mail),
}),
},
],
exports: [MailService, SmtpConfigService],
})
export class MailModule implements OnModuleInit {
constructor(
private readonly scheduler: SchedulerService,
private readonly retention: MailRetentionService,
) {}
onModuleInit(): void {
this.scheduler.register({
name: 'mail-outbox-retention',
cadenceSeconds: OUTBOX_RETENTION_CADENCE_SECONDS,
run: async () => {
await this.retention.pruneExpired();
},
});
}
}