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
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
51 lines
1.6 KiB
TypeScript
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();
|
|
},
|
|
});
|
|
}
|
|
}
|