43 lines
1.2 KiB
TypeScript
43 lines
1.2 KiB
TypeScript
|
|
import { inArray } from 'drizzle-orm';
|
||
|
|
|
||
|
|
import { db } from '@/lib/db';
|
||
|
|
import { user } from '@/lib/db/schema/users';
|
||
|
|
import { searchAuditLogs } from '@/lib/services/audit-search.service';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Shared loader for the per-entity Activity tab. Wraps `searchAuditLogs`
|
||
|
|
* with actor-email resolution so each row can render `who did what`.
|
||
|
|
*
|
||
|
|
* Tenant gate happens at the API route — this service trusts the caller
|
||
|
|
* to pass an entityId that belongs to `portId`.
|
||
|
|
*/
|
||
|
|
export async function loadEntityActivity(args: {
|
||
|
|
portId: string;
|
||
|
|
entityType: string;
|
||
|
|
entityId: string;
|
||
|
|
limit?: number;
|
||
|
|
}) {
|
||
|
|
const { rows } = await searchAuditLogs({
|
||
|
|
portId: args.portId,
|
||
|
|
entityType: args.entityType,
|
||
|
|
entityId: args.entityId,
|
||
|
|
limit: args.limit ?? 50,
|
||
|
|
});
|
||
|
|
|
||
|
|
const userIds = Array.from(
|
||
|
|
new Set(rows.map((r) => r.userId).filter((u): u is string => Boolean(u))),
|
||
|
|
);
|
||
|
|
const userRows = userIds.length
|
||
|
|
? await db
|
||
|
|
.select({ id: user.id, email: user.email, name: user.name })
|
||
|
|
.from(user)
|
||
|
|
.where(inArray(user.id, userIds))
|
||
|
|
: [];
|
||
|
|
const userMap = new Map(userRows.map((u) => [u.id, u]));
|
||
|
|
|
||
|
|
return rows.map((r) => ({
|
||
|
|
...r,
|
||
|
|
actor: r.userId ? (userMap.get(r.userId) ?? null) : null,
|
||
|
|
}));
|
||
|
|
}
|