feat(14-03): add per-tenant encrypted TenderEmailConfig + ownerTenantId write-side (D-13)
Prisma: new TenderEmailConfig model (per-tenant, tenantId @unique, mirrors
DkvModuleConfig) + Tender.ownerTenantId nullable column + index (D-13:
null = global/platform-wide, unchanged for all existing rows and every
public source; set = visible only to that tenant). Migration
20260723113917_tender_email_config_owner_tenant_id applied locally.
TenderEmailConfigService: safe-select admin CRUD (GET never returns the
password, only hasPassword — T-07-12) with DkvService's encrypt-preserve-
empty semantics, via CalendarCryptoService (AES-256-GCM).
RawTenderRecord/NormalizedTenderFields gain optional ownerTenantId,
threaded through TenderNormalizerService.assemble() unchanged.
TenderDedupService's CREATE branch writes ownerTenantId (defaulting to
null); the UPDATE branch deliberately never references it, so a tender
later also seen on a public source is never retroactively hidden.
EmailAlertAdapter.fetchTenders() now does the real per-tenant fan-out:
findMany({isActive:true}) across ALL tenants (deliberate, documented
cross-tenant platform-scheduler read, never forTenant()/RLS), decrypts
each tenant's credentials, picks imap/exchange provider, and tags every
extracted candidate with ownerTenantId — catch-per-tenant so one broken
mailbox never blocks the others.
tenders.module.ts: imports CalendarModule/InboxModule, registers
EmailAlertAdapter + TenderEmailConfigService, seeds an 'email-alert'
TenderSourcePollConfig row (pollGranularity='tick', isActive=false —
no default mailbox to activate yet, D-02 framework-ready stance).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Tender" ADD COLUMN "ownerTenantId" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TenderEmailConfig" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"protocol" TEXT NOT NULL DEFAULT 'imap',
|
||||
"host" TEXT,
|
||||
"port" INTEGER,
|
||||
"encryption" TEXT NOT NULL DEFAULT 'ssl-tls',
|
||||
"folder" TEXT NOT NULL DEFAULT 'INBOX',
|
||||
"senderFilter" TEXT,
|
||||
"domain" TEXT,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT false,
|
||||
"encryptedInboxCreds" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "TenderEmailConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TenderEmailConfig_tenantId_key" ON "TenderEmailConfig"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "TenderEmailConfig_tenantId_idx" ON "TenderEmailConfig"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Tender_ownerTenantId_idx" ON "Tender"("ownerTenantId");
|
||||
Reference in New Issue
Block a user