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>
58 lines
2.1 KiB
TypeScript
58 lines
2.1 KiB
TypeScript
import { Module } from '@nestjs/common';
|
|
import { PrismaModule } from './prisma/prisma.module';
|
|
import { PlatformModule } from './platform/platform.module';
|
|
import { AttestationModule } from './platform/attestation.module';
|
|
import { IdentityModule } from './identity/identity.module';
|
|
import { InteractionsModule } from './interactions/interactions.module';
|
|
import { IdempotencyModule } from './idempotency/idempotency.module';
|
|
import { ProjectionModule } from './projection/projection.module';
|
|
import { OutboxModule } from './outbox/outbox.module';
|
|
import { ThreadsModule } from './threads/threads.module';
|
|
import { MessageModule } from './messaging/message.module';
|
|
import { InboxModule } from './inbox/inbox.module';
|
|
import { TemplateModule } from './templates/template.module';
|
|
import { MailModule } from './mail/mail.module';
|
|
import { MediaModule } from './media/media.module';
|
|
import { NotificationModule } from './notifications/notification.module';
|
|
import { SupportModule } from './support/support.module';
|
|
import { AdaptersModule } from './adapters/adapters.module';
|
|
import { RoutingModule } from './routing/routing.module';
|
|
import { AiModule } from './ai/ai.module';
|
|
import { CalendarModule } from './calendar/calendar.module';
|
|
import { CapabilityModule } from './capability/capability.module';
|
|
import { DsrModule } from './dsr/dsr.module';
|
|
import { RetentionModule } from './retention/retention.module';
|
|
import { HealthController } from './health.controller';
|
|
import { MetricsController } from './observability/metrics.controller';
|
|
import { DevController } from './dev/dev.controller';
|
|
|
|
@Module({
|
|
imports: [
|
|
PrismaModule,
|
|
PlatformModule,
|
|
AttestationModule,
|
|
IdentityModule,
|
|
InteractionsModule,
|
|
IdempotencyModule,
|
|
ProjectionModule,
|
|
OutboxModule,
|
|
ThreadsModule,
|
|
MessageModule,
|
|
InboxModule,
|
|
TemplateModule,
|
|
MailModule,
|
|
MediaModule,
|
|
NotificationModule,
|
|
SupportModule,
|
|
AdaptersModule,
|
|
RoutingModule,
|
|
AiModule,
|
|
CalendarModule,
|
|
CapabilityModule,
|
|
DsrModule,
|
|
RetentionModule,
|
|
],
|
|
controllers: [HealthController, MetricsController, DevController],
|
|
})
|
|
export class AppModule {}
|