171 lines
6.1 KiB
TypeScript
171 lines
6.1 KiB
TypeScript
import { createPrismaPlatformLeadService, type PlatformLeadService } from "@hivegrid/db/platform-leads-admin.server";
|
|
import {
|
|
createAdminSalesLeadService,
|
|
createPrismaAdminSalesLeadRepository,
|
|
} from "@hivegrid/db/admin-sales-leads.server";
|
|
import type { AdminPermissionName } from "@hivegrid/db/admin-permissions";
|
|
import { runWithRlsContext } from "@hivegrid/db/rls-context.server";
|
|
import { listAdminPermissionNames } from "@hivegrid/db/admin-rbac.server";
|
|
|
|
import { extractWebActorIp, extractWebActorUserAgent, extractWebRequestId } from "~/utils/audit/withAuditContext";
|
|
import { db } from "~/utils/db.server";
|
|
import { getUserInfo } from "~/utils/session.server";
|
|
|
|
export const PLATFORM_LEAD_PERMISSIONS = Object.freeze({
|
|
readOwn: "admin_sales.crm.read",
|
|
readAll: "admin_sales.crm.read_all",
|
|
write: "admin_sales.crm.write",
|
|
writeAll: "admin_sales.crm.write_all",
|
|
admin: "admin_sales.crm.assign",
|
|
merge: "admin_sales.crm.merge",
|
|
} as const);
|
|
|
|
export type PlatformLeadOperatorAccess = {
|
|
readonly actorAdminUserId: string;
|
|
readonly actorEmail: string;
|
|
readonly canReadOwn: boolean;
|
|
readonly canReadAll: boolean;
|
|
readonly canWrite: boolean;
|
|
readonly canAdmin: boolean;
|
|
readonly canMerge: boolean;
|
|
readonly audit: (reason: string) => {
|
|
actorAdminUserId: string;
|
|
actorEmail: string;
|
|
actorIp: string | null;
|
|
actorUserAgent: string | null;
|
|
requestId: string | null;
|
|
reason: string;
|
|
};
|
|
};
|
|
|
|
export type PlatformLeadOperatorContext = {
|
|
readonly access: PlatformLeadOperatorAccess;
|
|
readonly service: PlatformLeadService;
|
|
readonly advancedService: ReturnType<typeof createAdminSalesLeadService>;
|
|
};
|
|
|
|
export type PlatformLeadAssigneeOption = {
|
|
readonly id: string;
|
|
readonly label: string;
|
|
};
|
|
|
|
export function platformLeadNotFound(): Response {
|
|
return new Response("Not Found", { status: 404 });
|
|
}
|
|
|
|
export function requirePlatformLeadRead(access: PlatformLeadOperatorAccess): void {
|
|
if (!access.canReadOwn && !access.canReadAll) throw platformLeadNotFound();
|
|
}
|
|
|
|
export function requirePlatformLeadWrite(access: PlatformLeadOperatorAccess): void {
|
|
requirePlatformLeadRead(access);
|
|
if (!access.canWrite) throw platformLeadNotFound();
|
|
}
|
|
|
|
export function requirePlatformLeadAdmin(access: PlatformLeadOperatorAccess): void {
|
|
requirePlatformLeadRead(access);
|
|
if (!access.canAdmin) throw platformLeadNotFound();
|
|
}
|
|
|
|
/**
|
|
* Binds the current operator identity for the complete read/write callback.
|
|
* This is deliberately callback-based: returning a client after the callback
|
|
* would lose the transaction-local RLS identity on staging and production.
|
|
*/
|
|
export async function runAsPlatformLeadOperator<T>(request: Request, callback: (context: PlatformLeadOperatorContext) => Promise<T>): Promise<T> {
|
|
const session = await getUserInfo(request);
|
|
if (!session.userId) throw platformLeadNotFound();
|
|
|
|
return runWithRlsContext({ userId: session.userId }, async () => {
|
|
const operator = await db.adminUser.findUnique({
|
|
where: { id: session.userId },
|
|
select: { id: true, email: true, status: true },
|
|
});
|
|
if (!operator || operator.status !== "active") throw platformLeadNotFound();
|
|
|
|
const permissionNames = new Set(await listAdminPermissionNames(operator.id));
|
|
const auditBase = {
|
|
actorAdminUserId: operator.id,
|
|
actorEmail: operator.email,
|
|
actorIp: extractWebActorIp(request),
|
|
actorUserAgent: extractWebActorUserAgent(request),
|
|
requestId: extractWebRequestId(request),
|
|
};
|
|
const access: PlatformLeadOperatorAccess = {
|
|
actorAdminUserId: operator.id,
|
|
actorEmail: operator.email,
|
|
canReadOwn: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.readOwn),
|
|
canReadAll: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.readAll),
|
|
canWrite: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.write),
|
|
canAdmin: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.admin),
|
|
canMerge: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.merge),
|
|
audit: (reason) => ({ ...auditBase, reason }),
|
|
};
|
|
|
|
const authorize = (permission: AdminPermissionName) => {
|
|
if (!permissionNames.has(permission)) throw platformLeadNotFound();
|
|
};
|
|
return callback({
|
|
access,
|
|
service: createPrismaPlatformLeadService(db, {
|
|
adminUserId: operator.id,
|
|
canReadAll: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.readAll),
|
|
canWriteAll: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.writeAll),
|
|
canReadAllCommunication: permissionNames.has(
|
|
"admin_sales.communication.read_all"
|
|
),
|
|
}),
|
|
advancedService: createAdminSalesLeadService({
|
|
repository: createPrismaAdminSalesLeadRepository(db, {
|
|
accessScope: {
|
|
adminUserId: operator.id,
|
|
canReadAll: permissionNames.has(PLATFORM_LEAD_PERMISSIONS.readAll),
|
|
canWriteAll: permissionNames.has(
|
|
PLATFORM_LEAD_PERMISSIONS.writeAll
|
|
),
|
|
canReadAllCommunication: permissionNames.has(
|
|
"admin_sales.communication.read_all"
|
|
),
|
|
},
|
|
}),
|
|
authorize,
|
|
}),
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function listPlatformLeadAssignees(): Promise<PlatformLeadAssigneeOption[]> {
|
|
const operators = await db.adminUser.findMany({
|
|
where: {
|
|
status: "active",
|
|
roles: {
|
|
some: {
|
|
role: {
|
|
active: true,
|
|
permissions: {
|
|
some: {
|
|
permission: {
|
|
name: PLATFORM_LEAD_PERMISSIONS.readOwn,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
orderBy: [{ firstName: "asc" }, { lastName: "asc" }, { id: "asc" }],
|
|
select: { id: true, firstName: true, lastName: true, email: true },
|
|
});
|
|
return operators.map((operator) => ({
|
|
id: operator.id,
|
|
label: [operator.firstName, operator.lastName].filter(Boolean).join(" ").trim() || operator.email,
|
|
}));
|
|
}
|
|
|
|
export function assertPlatformLeadVisibleToOperator(lead: { readonly assignedToAdminUserId: string | null }, access: PlatformLeadOperatorAccess): void {
|
|
requirePlatformLeadRead(access);
|
|
if (!access.canReadAll && lead.assignedToAdminUserId !== access.actorAdminUserId) {
|
|
throw platformLeadNotFound();
|
|
}
|
|
}
|