This commit is contained in:
2026-03-15 19:40:35 -05:00
parent 275c73b584
commit dcac4f135d
17 changed files with 659 additions and 318 deletions

View File

@@ -146,6 +146,10 @@ function mapAuthSession(
lastName: string;
} | null;
},
reviewContext: {
reviewState: "NORMAL" | "REVIEW";
reviewReasons: string[];
},
currentSessionId?: string
): AdminAuthSessionDto {
const now = Date.now();
@@ -157,6 +161,8 @@ function mapAuthSession(
userEmail: record.user.email,
userName: `${record.user.firstName} ${record.user.lastName}`.trim(),
status,
reviewState: reviewContext.reviewState,
reviewReasons: reviewContext.reviewReasons,
isCurrent: record.id === currentSessionId,
createdAt: record.createdAt.toISOString(),
lastSeenAt: record.lastSeenAt.toISOString(),
@@ -404,7 +410,63 @@ export async function listAdminAuthSessions(currentSessionId?: string | null): P
take: 200,
});
return sessions.map((session) => mapAuthSession(session, currentSessionId ?? undefined));
const now = Date.now();
const activeSessionsByUser = new Map<
string,
Array<{
id: string;
ipAddress: string | null;
userAgent: string | null;
lastSeenAt: Date;
}>
>();
for (const session of sessions) {
const isActive = !session.revokedAt && session.expiresAt.getTime() > now;
if (!isActive) {
continue;
}
const existing = activeSessionsByUser.get(session.userId) ?? [];
existing.push({
id: session.id,
ipAddress: session.ipAddress,
userAgent: session.userAgent,
lastSeenAt: session.lastSeenAt,
});
activeSessionsByUser.set(session.userId, existing);
}
return sessions.map((session) => {
const reviewReasons: string[] = [];
const activeUserSessions = activeSessionsByUser.get(session.userId) ?? [];
const isActive = !session.revokedAt && session.expiresAt.getTime() > now;
const staleThresholdMs = 7 * 24 * 60 * 60 * 1000;
if (isActive && activeUserSessions.length > 1) {
reviewReasons.push("Multiple active sessions");
}
if (isActive) {
const distinctIps = new Set(activeUserSessions.map((entry) => entry.ipAddress).filter(Boolean));
if (distinctIps.size > 1) {
reviewReasons.push("Multiple active IP addresses");
}
if (now - session.lastSeenAt.getTime() > staleThresholdMs) {
reviewReasons.push("Stale active session");
}
}
return mapAuthSession(
session,
{
reviewState: reviewReasons.length > 0 ? "REVIEW" : "NORMAL",
reviewReasons,
},
currentSessionId ?? undefined
);
});
}
export async function revokeAdminAuthSession(sessionId: string, actorId?: string | null) {
@@ -596,6 +658,8 @@ export async function updateAdminUser(userId: string, payload: AdminUserInput, a
export async function getAdminDiagnostics(): Promise<AdminDiagnosticsDto> {
const startupReport = getLatestStartupReport();
const recentSupportLogs = listSupportLogs(50);
const now = new Date();
const reviewSessions = await listAdminAuthSessions();
const [
companyProfile,
userCount,
@@ -624,7 +688,7 @@ export async function getAdminDiagnostics(): Promise<AdminDiagnosticsDto> {
where: {
revokedAt: null,
expiresAt: {
gt: new Date(),
gt: now,
},
},
}),
@@ -667,6 +731,7 @@ export async function getAdminDiagnostics(): Promise<AdminDiagnosticsDto> {
userCount,
activeUserCount,
activeSessionCount,
reviewSessionCount: reviewSessions.filter((session) => session.reviewState === "REVIEW").length,
roleCount,
permissionCount,
customerCount,