Add pagination to audit log retrieval.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/1081a452-f77b-4ab9-a9b8-4f930a0a540d.jpg
This commit is contained in:
alphaeusmote
2025-04-09 17:53:08 +00:00
2 changed files with 130 additions and 29 deletions
+84 -14
View File
@@ -3585,22 +3585,58 @@ export async function registerRoutes(app: Express): Promise<Server> {
* tags: [Audit Logs] * tags: [Audit Logs]
* security: * security:
* - cookieAuth: [] * - cookieAuth: []
* parameters:
* - name: page
* in: query
* required: false
* schema:
* type: integer
* default: 1
* description: Page number
* - name: pageSize
* in: query
* required: false
* schema:
* type: integer
* default: 10
* description: Number of items per page
* responses: * responses:
* 200: * 200:
* description: List of all audit logs * description: Paginated list of audit logs
* content: * content:
* application/json: * application/json:
* schema: * schema:
* type: array * type: object
* items: * properties:
* $ref: '#/components/schemas/AuditLog' * data:
* type: array
* items:
* $ref: '#/components/schemas/AuditLog'
* metadata:
* type: object
* properties:
* currentPage:
* type: integer
* totalPages:
* type: integer
* totalRecords:
* type: integer
* nextPage:
* type: integer
* nullable: true
* prevPage:
* type: integer
* nullable: true
* 401: * 401:
* $ref: '#/components/responses/UnauthorizedError' * $ref: '#/components/responses/UnauthorizedError'
*/ */
app.get("/api/audit-logs", requireAuth, async (req: Request<any>, res: Response, next: NextFunction) => { app.get("/api/audit-logs", requireAuth, async (req, res, next) => {
try { try {
const logs = await storage.getAuditLogs(); const page = req.query.page ? parseInt(req.query.page as string) : 1;
res.json(logs); const pageSize = req.query.pageSize ? parseInt(req.query.pageSize as string) : 10;
const result = await storage.getAuditLogs(undefined, undefined, page, pageSize);
res.json(result);
} catch (error) { } catch (error) {
next(error); next(error);
} }
@@ -3620,23 +3656,57 @@ export async function registerRoutes(app: Express): Promise<Server> {
* required: true * required: true
* schema: * schema:
* type: integer * type: integer
* - name: page
* in: query
* required: false
* schema:
* type: integer
* default: 1
* description: Page number
* - name: pageSize
* in: query
* required: false
* schema:
* type: integer
* default: 10
* description: Number of items per page
* responses: * responses:
* 200: * 200:
* description: List of audit logs for the specified connection * description: Paginated list of audit logs for the specified connection
* content: * content:
* application/json: * application/json:
* schema: * schema:
* type: array * type: object
* items: * properties:
* $ref: '#/components/schemas/AuditLog' * data:
* type: array
* items:
* $ref: '#/components/schemas/AuditLog'
* metadata:
* type: object
* properties:
* currentPage:
* type: integer
* totalPages:
* type: integer
* totalRecords:
* type: integer
* nextPage:
* type: integer
* nullable: true
* prevPage:
* type: integer
* nullable: true
* 401: * 401:
* $ref: '#/components/responses/UnauthorizedError' * $ref: '#/components/responses/UnauthorizedError'
* 404: * 404:
* $ref: '#/components/responses/NotFoundError' * $ref: '#/components/responses/NotFoundError'
*/ */
app.get("/api/connections/:connectionId/audit-logs", requireAuth, async (req: Request<any>, res: Response, next: NextFunction) => { app.get("/api/connections/:connectionId/audit-logs", requireAuth, async (req, res, next) => {
try { try {
const connectionId = parseInt(req.params.connectionId, 10); const connectionId = parseInt(req.params.connectionId, 10);
const page = req.query.page ? parseInt(req.query.page as string) : 1;
const pageSize = req.query.pageSize ? parseInt(req.query.pageSize as string) : 10;
// Verify the connection exists // Verify the connection exists
const connection = await storage.getLdapConnection(connectionId); const connection = await storage.getLdapConnection(connectionId);
@@ -3644,8 +3714,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(404).json({ message: "Connection not found" }); return res.status(404).json({ message: "Connection not found" });
} }
const logs = await storage.getAuditLogs(connectionId); const result = await storage.getAuditLogs(connectionId, undefined, page, pageSize);
res.json(logs); res.json(result);
} catch (error) { } catch (error) {
next(error); next(error);
} }
+46 -15
View File
@@ -13,7 +13,7 @@ import session from "express-session";
import createMemoryStore from "memorystore"; import createMemoryStore from "memorystore";
import crypto from "crypto"; import crypto from "crypto";
import { db } from "./db"; import { db } from "./db";
import { eq, and, type SQL, desc } from "drizzle-orm"; import { eq, and, type SQL, desc, sql } from "drizzle-orm";
import connectPg from "connect-pg-simple"; import connectPg from "connect-pg-simple";
import { Pool } from "@neondatabase/serverless"; import { Pool } from "@neondatabase/serverless";
import { applyQueryOptions } from "./query-parser"; import { applyQueryOptions } from "./query-parser";
@@ -123,7 +123,7 @@ export interface IStorage {
// Audit logging // Audit logging
createAuditLogEntry(entry: InsertAuditLog): Promise<AuditLog>; createAuditLogEntry(entry: InsertAuditLog): Promise<AuditLog>;
getAuditLogs(connectionId?: number, userId?: number): Promise<AuditLog[]>; getAuditLogs(connectionId?: number, userId?: number, page?: number, pageSize?: number): Promise<{ data: AuditLog[], metadata: { currentPage: number, totalPages: number, totalRecords: number, nextPage: number | null, prevPage: number | null } }>;
// Session store // Session store
sessionStore: any; sessionStore: any;
@@ -978,28 +978,59 @@ export class DatabaseStorage implements IStorage {
return result[0]; return result[0];
} }
async getAuditLogs(connectionId?: number, userId?: number): Promise<AuditLog[]> { async getAuditLogs(connectionId?: number, userId?: number, page: number = 1, pageSize: number = 10): Promise<{ data: AuditLog[], metadata: { currentPage: number, totalPages: number, totalRecords: number, nextPage: number | null, prevPage: number | null } }> {
debug(`Getting audit logs: connectionId=${connectionId}, userId=${userId}`); debug(`Getting audit logs: connectionId=${connectionId}, userId=${userId}, page=${page}, pageSize=${pageSize}`);
// Define the base query // Define the base query for counting total records
let query = db.select().from(auditLogs); let countQuery = db.select({ count: sql`count(*)` }).from(auditLogs);
let dataQuery = db.select().from(auditLogs);
// Apply filters if provided // Apply filters if provided
if (connectionId !== undefined && userId !== undefined) { if (connectionId !== undefined && userId !== undefined) {
query = query.where( const filter = and(
and( eq(auditLogs.connectionId, connectionId),
eq(auditLogs.connectionId, connectionId), eq(auditLogs.userId, userId)
eq(auditLogs.userId, userId)
)
); );
countQuery = countQuery.where(filter);
dataQuery = dataQuery.where(filter);
} else if (connectionId !== undefined) { } else if (connectionId !== undefined) {
query = query.where(eq(auditLogs.connectionId, connectionId)); const filter = eq(auditLogs.connectionId, connectionId);
countQuery = countQuery.where(filter);
dataQuery = dataQuery.where(filter);
} else if (userId !== undefined) { } else if (userId !== undefined) {
query = query.where(eq(auditLogs.userId, userId)); const filter = eq(auditLogs.userId, userId);
countQuery = countQuery.where(filter);
dataQuery = dataQuery.where(filter);
} }
// Sort by most recent first // Get total count of records
return await query.orderBy(desc(auditLogs.timestamp)); const countResult = await countQuery;
const totalRecords = parseInt(countResult[0].count.toString());
// Calculate pagination values
const totalPages = Math.ceil(totalRecords / pageSize);
const currentPage = Math.max(1, Math.min(page, totalPages)); // Ensure page is within bounds
const offset = (currentPage - 1) * pageSize;
// Apply pagination and sorting to data query
dataQuery = dataQuery
.orderBy(desc(auditLogs.timestamp))
.limit(pageSize)
.offset(offset);
// Execute data query
const data = await dataQuery;
// Create pagination metadata
const metadata = {
currentPage,
totalPages,
totalRecords,
nextPage: currentPage < totalPages ? currentPage + 1 : null,
prevPage: currentPage > 1 ? currentPage - 1 : null
};
return { data, metadata };
} }
} }