diff --git a/backend/src/controllers/v1/membershipController.ts b/backend/src/controllers/v1/membershipController.ts index a473f0c4a..48689be1c 100644 --- a/backend/src/controllers/v1/membershipController.ts +++ b/backend/src/controllers/v1/membershipController.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import { Types } from "mongoose"; -import { Key, Membership, MembershipOrg, User, IUser } from "../../models"; +import { IUser, Key, Membership, MembershipOrg, User } from "../../models"; import { EventType } from "../../ee/models"; import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership"; import { sendMail } from "../../helpers/nodemailer"; diff --git a/backend/src/controllers/v1/secretsFolderController.ts b/backend/src/controllers/v1/secretsFolderController.ts index 742852343..334df6d4f 100644 --- a/backend/src/controllers/v1/secretsFolderController.ts +++ b/backend/src/controllers/v1/secretsFolderController.ts @@ -17,8 +17,8 @@ import { } from "../../services/FolderService"; import { ADMIN, MEMBER } from "../../variables"; import { validateMembership } from "../../helpers/membership"; -import { FolderVersion, EventType } from "../../ee/models"; -import { EESecretService, EEAuditLogService } from "../../ee/services"; +import { EventType, FolderVersion } from "../../ee/models"; +import { EEAuditLogService, EESecretService } from "../../ee/services"; // verify workspace id/environment export const createFolder = async (req: Request, res: Response) => { diff --git a/backend/src/controllers/v2/environmentController.ts b/backend/src/controllers/v2/environmentController.ts index 20680f1c6..8cbf1a229 100644 --- a/backend/src/controllers/v2/environmentController.ts +++ b/backend/src/controllers/v2/environmentController.ts @@ -8,8 +8,8 @@ import { ServiceTokenData, Workspace, } from "../../models"; -import { SecretVersion, EventType } from "../../ee/models"; -import { EELicenseService, EEAuditLogService } from "../../ee/services"; +import { EventType, SecretVersion } from "../../ee/models"; +import { EEAuditLogService, EELicenseService } from "../../ee/services"; import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors"; import _ from "lodash"; import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables"; diff --git a/backend/src/ee/controllers/v1/workspaceController.ts b/backend/src/ee/controllers/v1/workspaceController.ts index 4db8c4855..9354c4f13 100644 --- a/backend/src/ee/controllers/v1/workspaceController.ts +++ b/backend/src/ee/controllers/v1/workspaceController.ts @@ -4,6 +4,7 @@ import { Membership, Secret, ServiceTokenData, User } from "../../../models"; import { ActorType, AuditLog, + EventType, FolderVersion, IPType, ISecretVersion, @@ -13,14 +14,13 @@ import { ServiceActor, TFolderRootVersionSchema, TrustedIP, - UserActor, - EventType + UserActor } from "../../models"; import { EESecretService } from "../../services"; import { getLatestSecretVersionIds } from "../../helpers/secretVersion"; import Folder, { TFolderSchema } from "../../../models/folder"; import { searchByFolderId } from "../../../services/FolderService"; -import { EELicenseService, EEAuditLogService } from "../../services"; +import { EEAuditLogService, EELicenseService } from "../../services"; import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip"; /** @@ -611,7 +611,10 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => { const offset: number = parseInt(req.query.offset as string); const limit: number = parseInt(req.query.limit as string); - const auditLogs = await AuditLog.find({ + const startDate = req.query.startDate as string; + const endDate = req.query.endDate as string; + + const query = { workspace: new Types.ObjectId(workspaceId), ...(eventType ? { "event.type": eventType @@ -626,14 +629,25 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => { } : { "actor.metadata.serviceId": actor.split("-", 2)[1] }) + } : {}), + ...(startDate || endDate ? { + createdAt: { + ...(startDate && { $gte: new Date(startDate) }), + ...(endDate && { $lte: new Date(endDate) }) + } } : {}) - }) + } + + const auditLogs = await AuditLog.find(query) .sort({ createdAt: -1 }) .skip(offset) .limit(limit); - + + const totalCount = await AuditLog.countDocuments(query); + return res.status(200).send({ - auditLogs + auditLogs, + totalCount }); } diff --git a/backend/src/ee/routes/v1/workspace.ts b/backend/src/ee/routes/v1/workspace.ts index 2143f0781..529ba0550 100644 --- a/backend/src/ee/routes/v1/workspace.ts +++ b/backend/src/ee/routes/v1/workspace.ts @@ -96,9 +96,11 @@ router.get( param("workspaceId").exists().trim(), query("eventType").isString().isIn(Object.values(EventType)).optional({ nullable: true }), query("userAgentType").isString().isIn(Object.values(UserAgentType)).optional({ nullable: true }), - query("actor").isString().optional({ nullable: true }), - query("offset").default("0"), - query("limit").default("20"), + query("actor").optional({ nullable: true }), + query("startDate").isISO8601().withMessage("Invalid start date format").optional({ nullable: true }), + query("endDate").isISO8601().withMessage("Invalid end date format").optional({ nullable: true }), + query("offset"), + query("limit"), validateRequest, workspaceController.getWorkspaceAuditLogs ); diff --git a/backend/src/services/FolderService.ts b/backend/src/services/FolderService.ts index 7d3e4bd59..8e1c00750 100644 --- a/backend/src/services/FolderService.ts +++ b/backend/src/services/FolderService.ts @@ -1,6 +1,6 @@ import { nanoid } from "nanoid"; import { Types } from "mongoose"; -import Folder, { TFolderSchema, TFolderRootSchema } from "../models/folder"; +import Folder, { TFolderRootSchema, TFolderSchema } from "../models/folder"; import { ResourceNotFoundError } from "../utils/errors"; type TAppendFolderDTO = { diff --git a/frontend/src/components/v2/Pagination/Pagination.tsx b/frontend/src/components/v2/Pagination/Pagination.tsx index 99d50a07f..f4a75eac7 100644 --- a/frontend/src/components/v2/Pagination/Pagination.tsx +++ b/frontend/src/components/v2/Pagination/Pagination.tsx @@ -21,7 +21,7 @@ export type PaginationProps = { perPage?: number; onChangePage: (pageNumber: number) => void; onChangePerPage: (newRows: number) => void; - className: string; + className?: string; perPageList?: number[]; }; diff --git a/frontend/src/hooks/api/auditLogs/queries.tsx b/frontend/src/hooks/api/auditLogs/queries.tsx index 930de7f0a..94df8bf8b 100644 --- a/frontend/src/hooks/api/auditLogs/queries.tsx +++ b/frontend/src/hooks/api/auditLogs/queries.tsx @@ -31,15 +31,19 @@ export const useGetAuditLogs = (workspaceId: string, filters: AuditLogFilters) = params.append("actor", filters.actor); } - if (filters.actor) { - params.append("actor", filters.actor); + if (filters.startDate) { + params.append("startDate", filters.startDate.toISOString()); } - params.append("offset ", String(filters.offset)); - params.append("limit ", String(filters.limit)); + if (filters.endDate) { + params.append("endDate", filters.endDate.toISOString()); + } + + params.append("offset", String(filters.offset)); + params.append("limit", String(filters.limit)); - const { data } = await apiRequest.get<{ auditLogs: AuditLog[] }>(`/api/v1/workspace/${workspaceId}/audit-logs`, { params }); - return data.auditLogs; + const { data } = await apiRequest.get<{ auditLogs: AuditLog[], totalCount: number }>(`/api/v1/workspace/${workspaceId}/audit-logs`, { params }); + return data; } }); } diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index b7c3cc47a..71cf01c5c 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -323,4 +323,6 @@ export type AuditLogFilters = { actor?: string; offset: number; limit: number; + startDate?: Date; + endDate?: Date; } \ No newline at end of file diff --git a/frontend/src/views/Project/LogsPage/components/LogsFilter.tsx b/frontend/src/views/Project/LogsPage/components/LogsFilter.tsx index d1273f3aa..dce7c4938 100644 --- a/frontend/src/views/Project/LogsPage/components/LogsFilter.tsx +++ b/frontend/src/views/Project/LogsPage/components/LogsFilter.tsx @@ -1,18 +1,19 @@ +import { useState } from "react"; import { Control, Controller, UseFormReset } from "react-hook-form"; import { faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button, + DatePicker, FormControl, Select, SelectItem} from "@app/components/v2"; import { useWorkspace } from "@app/context"; import { useGetAuditLogActorFilterOpts } from "@app/hooks/api"; - -import { eventToNameMap, userAgentTTypeoNameMap } from "~/hooks/api/auditLogs/constants"; -import { ActorType } from "~/hooks/api/auditLogs/enums"; -import { Actor } from "~/hooks/api/auditLogs/types"; +import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants"; +import { ActorType } from "@app/hooks/api/auditLogs/enums"; +import { Actor } from "@app/hooks/api/auditLogs/types"; import { AuditLogFilterFormData } from "./types"; @@ -28,6 +29,9 @@ export const LogsFilter = ({ control, reset }: Props) => { + const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false); + const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false); + const { currentWorkspace } = useWorkspace(); const { data, isLoading } = useGetAuditLogActorFilterOpts(currentWorkspace?._id ?? ""); @@ -58,65 +62,41 @@ export const LogsFilter = ({ return (