9b075f46f9
MailService renders a template then deposits it as an EMAIL interaction on a per-email thread (thread model: one thread per email), reusing IngestService. Because ingest adds no participants and a thread is only visible to its participants, it ensureParticipant()s BOTH sender and recipient — so the mirror is actually visible. - postInternal: app-to-app mail, no SMTP → recipient's in-app inbox. - sendExternalWithMirror: SMTP send (TemplatedSender) + mirror an interaction ONLY for a registered recipient (pre-registration sends are email-only — no inbox exists yet). Idempotent across both the send and the mirror. - Writes Interactions, NEVER InboxItems (the projector owns those — KG-15). - POST /v1/mail/internal, POST /v1/mail/send. MailModule in AppModule. Verified over HTTP: internal mail → recipient SEES the thread in their inbox; external send → command + mirror; no-source/no-auth 400. 7 unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
19 lines
703 B
TypeScript
19 lines
703 B
TypeScript
import { Module } from '@nestjs/common';
|
|
import { TemplateModule } from '../templates/template.module';
|
|
import { InteractionsModule } from '../interactions/interactions.module';
|
|
import { MailService } from './mail.service';
|
|
import { MailController } from './mail.controller';
|
|
|
|
/**
|
|
* Mail orchestration: render a template (TemplateModule) → deliver it either app-to-app (an EMAIL
|
|
* interaction via IngestService) or externally (TemplatedSender/SMTP) with an in-app mirror.
|
|
* SessionVerifier + ActorResolver are global.
|
|
*/
|
|
@Module({
|
|
imports: [TemplateModule, InteractionsModule],
|
|
controllers: [MailController],
|
|
providers: [MailService],
|
|
exports: [MailService],
|
|
})
|
|
export class MailModule {}
|