cleanup
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { prisma } from "./prisma.js";
|
||||
|
||||
const SESSION_DURATION_MS = 12 * 60 * 60 * 1000;
|
||||
const SESSION_RETENTION_DAYS = 30;
|
||||
|
||||
export interface AuthSessionContext {
|
||||
id: string;
|
||||
@@ -12,6 +13,10 @@ export function getSessionExpiryDate(now = new Date()) {
|
||||
return new Date(now.getTime() + SESSION_DURATION_MS);
|
||||
}
|
||||
|
||||
export function getSessionRetentionCutoff(now = new Date()) {
|
||||
return new Date(now.getTime() - SESSION_RETENTION_DAYS * 24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
export async function createAuthSession(input: { userId: string; ipAddress?: string | null; userAgent?: string | null }) {
|
||||
return prisma.authSession.create({
|
||||
data: {
|
||||
@@ -69,3 +74,27 @@ export async function revokeAuthSession(sessionId: string, input: { revokedById?
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function pruneOldAuthSessions() {
|
||||
const cutoff = getSessionRetentionCutoff();
|
||||
|
||||
const result = await prisma.authSession.deleteMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
revokedAt: {
|
||||
lt: cutoff,
|
||||
},
|
||||
},
|
||||
{
|
||||
revokedAt: null,
|
||||
expiresAt: {
|
||||
lt: cutoff,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
return result.count;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createApp } from "./app.js";
|
||||
import { env } from "./config/env.js";
|
||||
import { pruneOldAuthSessions } from "./lib/auth-sessions.js";
|
||||
import { bootstrapAppData } from "./lib/bootstrap.js";
|
||||
import { prisma } from "./lib/prisma.js";
|
||||
import { setLatestStartupReport } from "./lib/startup-state.js";
|
||||
@@ -8,6 +9,7 @@ import { recordSupportLog } from "./lib/support-log.js";
|
||||
|
||||
async function start() {
|
||||
await bootstrapAppData();
|
||||
const prunedSessionCount = await pruneOldAuthSessions();
|
||||
const startupReport = await assertStartupReadiness();
|
||||
setLatestStartupReport(startupReport);
|
||||
|
||||
@@ -21,6 +23,7 @@ async function start() {
|
||||
passCount: startupReport.passCount,
|
||||
warnCount: startupReport.warnCount,
|
||||
failCount: startupReport.failCount,
|
||||
prunedSessionCount,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user