feat(service): P3.1 inbox tables + extract ActorResolver
IiosInboxItem + IiosInboxItemStateHistory (migration inbox-init). Extracted resolveScope/resolveActor/ensureParticipant into a shared ActorResolver (IdentityModule) so message + inbox layers share identity resolution; MessageService delegates (P2 tests unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
/** The authenticated principal derived from the session port (token). */
|
||||
export interface MessagePrincipal {
|
||||
userId: string;
|
||||
orgId: string;
|
||||
appId: string;
|
||||
tenantId?: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a principal to kernel rows (scope → source handle → actor) and
|
||||
* manages thread participation. Shared by the message + inbox layers so neither
|
||||
* re-implements identity resolution. Unknown handles stay UNVERIFIED (the MDM
|
||||
* port resolution is applied at ingest; native users get a PORTAL_USER handle).
|
||||
*/
|
||||
@Injectable()
|
||||
export class ActorResolver {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async resolveScope(principal: MessagePrincipal) {
|
||||
return (
|
||||
(await this.prisma.iiosScope.findFirst({
|
||||
where: { orgId: principal.orgId, appId: principal.appId, tenantId: principal.tenantId ?? null },
|
||||
})) ??
|
||||
(await this.prisma.iiosScope.create({
|
||||
data: { orgId: principal.orgId, appId: principal.appId, tenantId: principal.tenantId },
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
async resolveActor(scopeId: string, principal: MessagePrincipal) {
|
||||
const handle = await this.prisma.iiosSourceHandle.upsert({
|
||||
where: { scopeId_kind_externalId: { scopeId, kind: 'PORTAL_USER', externalId: principal.userId } },
|
||||
create: { scopeId, kind: 'PORTAL_USER', externalId: principal.userId, displayName: principal.displayName },
|
||||
update: { lastSeenAt: new Date(), displayName: principal.displayName },
|
||||
});
|
||||
return (
|
||||
(await this.prisma.iiosActorRef.findFirst({ where: { sourceHandleId: handle.id } })) ??
|
||||
(await this.prisma.iiosActorRef.create({
|
||||
data: { kind: 'HUMAN', sourceHandleId: handle.id, displayName: principal.displayName },
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
async ensureParticipant(threadId: string, actorId: string): Promise<void> {
|
||||
await this.prisma.iiosThreadParticipant.upsert({
|
||||
where: { threadId_actorId: { threadId, actorId } },
|
||||
create: { threadId, actorId },
|
||||
update: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user