generated from nhcarrigan/template
feat: security and auditing
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import type { FastifyRequest } from "fastify";
|
||||
import { prisma } from "../lib/prisma";
|
||||
import type { AuditAction, AuditCategory, AuditLogFilters } from "@library/shared-types";
|
||||
|
||||
interface AuditLogData {
|
||||
action: AuditAction;
|
||||
category: AuditCategory;
|
||||
userId?: string;
|
||||
targetUserId?: string;
|
||||
resourceType?: string;
|
||||
resourceId?: string;
|
||||
details?: string;
|
||||
success?: boolean;
|
||||
}
|
||||
|
||||
export const AuditService = {
|
||||
async log(data: AuditLogData, request?: FastifyRequest) {
|
||||
const userAgent = request?.headers["user-agent"] ?? undefined;
|
||||
|
||||
return prisma.auditLog.create({
|
||||
data: {
|
||||
action: data.action,
|
||||
category: data.category,
|
||||
userId: data.userId,
|
||||
targetUserId: data.targetUserId,
|
||||
resourceType: data.resourceType,
|
||||
resourceId: data.resourceId,
|
||||
details: data.details,
|
||||
userAgent,
|
||||
success: data.success ?? true,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
async logFromRequest(
|
||||
request: FastifyRequest,
|
||||
data: Omit<AuditLogData, "userId">
|
||||
) {
|
||||
const userId = (request.user as { id?: string } | undefined)?.id;
|
||||
|
||||
return this.log(
|
||||
{
|
||||
...data,
|
||||
userId,
|
||||
},
|
||||
request
|
||||
);
|
||||
},
|
||||
|
||||
async getLogs(filters: AuditLogFilters = {}) {
|
||||
const { action, category, userId, success, startDate, endDate, page = 1, limit = 50 } = filters;
|
||||
|
||||
const where: Record<string, unknown> = {};
|
||||
|
||||
if (action) {
|
||||
where.action = action;
|
||||
}
|
||||
if (category) {
|
||||
where.category = category;
|
||||
}
|
||||
if (userId) {
|
||||
where.userId = userId;
|
||||
}
|
||||
if (success !== undefined) {
|
||||
where.success = success;
|
||||
}
|
||||
if (startDate || endDate) {
|
||||
where.createdAt = {};
|
||||
if (startDate) {
|
||||
(where.createdAt as Record<string, Date>).gte = startDate;
|
||||
}
|
||||
if (endDate) {
|
||||
(where.createdAt as Record<string, Date>).lte = endDate;
|
||||
}
|
||||
}
|
||||
|
||||
const [logs, total] = await Promise.all([
|
||||
prisma.auditLog.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: "desc" },
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
}),
|
||||
prisma.auditLog.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
logs,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
},
|
||||
|
||||
async getLogsByUser(userId: string, page = 1, limit = 50) {
|
||||
return this.getLogs({ userId, page, limit });
|
||||
},
|
||||
|
||||
async getSecurityLogs(page = 1, limit = 50) {
|
||||
return this.getLogs({ category: "SECURITY" as AuditCategory, page, limit });
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user