mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-21 23:57:19 +00:00
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:
+84
-14
@@ -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
@@ -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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user