diff --git a/backend/src/ee/controllers/v1/workspaceController.ts b/backend/src/ee/controllers/v1/workspaceController.ts index b26882d74..9bcad4c8e 100644 --- a/backend/src/ee/controllers/v1/workspaceController.ts +++ b/backend/src/ee/controllers/v1/workspaceController.ts @@ -577,7 +577,7 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => { ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs ); - + const query = { workspace: new Types.ObjectId(workspaceId), ...(eventType @@ -611,14 +611,9 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => { } : {}) }; - 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, - totalCount + auditLogs }); }; @@ -666,7 +661,7 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res name: serviceTokenData.name } })); - + const serviceV3Actors: ServiceActorV3[] = ( await ServiceTokenDataV3.find({ workspace: new Types.ObjectId(workspaceId) @@ -678,12 +673,8 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res name: serviceTokenData.name } })); - - const actors = [ - ...userActors, - ...serviceActors, - ...serviceV3Actors - ]; + + const actors = [...userActors, ...serviceActors, ...serviceV3Actors]; return res.status(200).send({ actors diff --git a/backend/src/ee/models/auditLog/auditLog.ts b/backend/src/ee/models/auditLog/auditLog.ts index a38527e85..c2c5aba68 100644 --- a/backend/src/ee/models/auditLog/auditLog.ts +++ b/backend/src/ee/models/auditLog/auditLog.ts @@ -1,76 +1,70 @@ import { Schema, Types, model } from "mongoose"; -import { - ActorType, - EventType, - UserAgentType -} from "./enums"; -import { - Actor, - Event -} from "./types"; +import { ActorType, EventType, UserAgentType } from "./enums"; +import { Actor, Event } from "./types"; export interface IAuditLog { - actor: Actor; - organization: Types.ObjectId; - workspace: Types.ObjectId; - ipAddress: string; - event: Event; - userAgent: string; - userAgentType: UserAgentType; - expiresAt: Date; + actor: Actor; + organization: Types.ObjectId; + workspace: Types.ObjectId; + ipAddress: string; + event: Event; + userAgent: string; + userAgentType: UserAgentType; + expiresAt: Date; } const auditLogSchema = new Schema( - { - actor: { - type: { - type: String, - enum: ActorType, - required: true - }, - metadata: { - type: Schema.Types.Mixed - } - }, - organization: { - type: Schema.Types.ObjectId, - required: false - }, - workspace: { - type: Schema.Types.ObjectId, - required: false - }, - ipAddress: { - type: String, - required: true - }, - event: { - type: { - type: String, - enum: EventType, - required: true - }, - metadata: { - type: Schema.Types.Mixed - } - }, - userAgent: { - type: String, - required: true - }, - userAgentType: { - type: String, - enum: UserAgentType, - required: true - }, - expiresAt: { - type: Date, - expires: 0 - } + { + actor: { + type: { + type: String, + enum: ActorType, + required: true + }, + metadata: { + type: Schema.Types.Mixed + } }, - { - timestamps: true + organization: { + type: Schema.Types.ObjectId, + required: false + }, + workspace: { + type: Schema.Types.ObjectId, + required: false, + index: true + }, + ipAddress: { + type: String, + required: true + }, + event: { + type: { + type: String, + enum: EventType, + required: true + }, + metadata: { + type: Schema.Types.Mixed + } + }, + userAgent: { + type: String, + required: true + }, + userAgentType: { + type: String, + enum: UserAgentType, + required: true + }, + expiresAt: { + type: Date, + expires: 0 } + }, + { + timestamps: true + } ); -export const AuditLog = model("AuditLog", auditLogSchema); +export const AuditLog = model("AuditLog", auditLogSchema); diff --git a/backend/src/validation/workspace.ts b/backend/src/validation/workspace.ts index 5c4ae25c7..fe13ea4db 100644 --- a/backend/src/validation/workspace.ts +++ b/backend/src/validation/workspace.ts @@ -58,7 +58,7 @@ export const validateClientForWorkspace = async ({ environment, requiredPermissions }); - return { membership, workspace}; + return { membership, workspace }; case ActorType.SERVICE_V3: throw UnauthorizedRequestError({ message: "Failed service token authorization for organization" @@ -121,8 +121,8 @@ export const GetWorkspaceAuditLogsV1 = z.object({ userAgentType: z.nativeEnum(UserAgentType).nullable().optional(), startDate: z.string().datetime().nullable().optional(), endDate: z.string().datetime().nullable().optional(), - offset: z.coerce.number(), - limit: z.coerce.number(), + offset: z.coerce.number().default(0), + limit: z.coerce.number().default(20), actor: z.string().nullish().optional() }) }); @@ -309,4 +309,4 @@ export const GetWorkspaceServiceTokenDataV3 = z.object({ params: z.object({ workspaceId: z.string().trim() }) -}); \ No newline at end of file +}); diff --git a/frontend/src/hooks/api/auditLogs/queries.tsx b/frontend/src/hooks/api/auditLogs/queries.tsx index 94df8bf8b..3517d4435 100644 --- a/frontend/src/hooks/api/auditLogs/queries.tsx +++ b/frontend/src/hooks/api/auditLogs/queries.tsx @@ -1,59 +1,46 @@ -import { useQuery } from "@tanstack/react-query"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; -import { - Actor, - AuditLog, - AuditLogFilters -} from "./types"; +import { Actor, AuditLog, AuditLogFilters } from "./types"; export const workspaceKeys = { - getAuditLogs: (workspaceId: string, filters: AuditLogFilters) => [{ workspaceId, filters }, "audit-logs"] as const, - getAuditLogActorFilterOpts: (workspaceId: string) => [{ workspaceId }, "audit-log-actor-filters"] as const -} + getAuditLogs: (workspaceId: string, filters: AuditLogFilters) => + [{ workspaceId, filters }, "audit-logs"] as const, + getAuditLogActorFilterOpts: (workspaceId: string) => + [{ workspaceId }, "audit-log-actor-filters"] as const +}; export const useGetAuditLogs = (workspaceId: string, filters: AuditLogFilters) => { - return useQuery({ - queryKey: workspaceKeys.getAuditLogs(workspaceId, filters), - queryFn: async () => { - - const params = new URLSearchParams(); - if (filters.eventType) { - params.append("eventType", filters.eventType); - } - - if (filters.userAgentType) { - params.append("userAgentType", filters.userAgentType); - } - - if (filters.actor) { - params.append("actor", filters.actor); - } - - if (filters.startDate) { - params.append("startDate", filters.startDate.toISOString()); - } - - 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[], totalCount: number }>(`/api/v1/workspace/${workspaceId}/audit-logs`, { params }); - return data; + return useInfiniteQuery({ + queryKey: workspaceKeys.getAuditLogs(workspaceId, filters), + queryFn: async ({ pageParam }) => { + const { data } = await apiRequest.get<{ auditLogs: AuditLog[] }>( + `/api/v1/workspace/${workspaceId}/audit-logs`, + { + params: { + ...filters, + offset: pageParam, + startDate: filters?.startDate?.toISOString(), + endDate: filters?.endDate?.toISOString() + } } - }); -} + ); + return data.auditLogs; + }, + getNextPageParam: (lastPage, pages) => + lastPage.length !== 0 ? pages.length * filters.limit : undefined + }); +}; export const useGetAuditLogActorFilterOpts = (workspaceId: string) => { - return useQuery({ - queryKey: workspaceKeys.getAuditLogActorFilterOpts(workspaceId), - queryFn: async () => { - const { data } = await apiRequest.get<{ actors: Actor[] }>(`/api/v1/workspace/${workspaceId}/audit-logs/filters/actors`); - return data.actors; - } - }); -} \ No newline at end of file + return useQuery({ + queryKey: workspaceKeys.getAuditLogActorFilterOpts(workspaceId), + queryFn: async () => { + const { data } = await apiRequest.get<{ actors: Actor[] }>( + `/api/v1/workspace/${workspaceId}/audit-logs/filters/actors` + ); + return data.actors; + } + }); +}; diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index f28f9004c..060f1b423 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -1,18 +1,14 @@ -import { - ActorType, - EventType, - UserAgentType -} from "./enums"; +import { ActorType, EventType, UserAgentType } from "./enums"; enum Permission { - READ = "read", - READ_WRITE = "readWrite" + READ = "read", + READ_WRITE = "readWrite" } interface Scope { - environment: string; - secretPath: string; - permission: Permission; + environment: string; + secretPath: string; + permission: Permission; } interface UserActorMetadata { @@ -36,428 +32,424 @@ export interface ServiceActor { } export interface ServiceActorV3 { - type: ActorType.SERVICE_V3; - metadata: ServiceActorMetadata; + type: ActorType.SERVICE_V3; + metadata: ServiceActorMetadata; } -export type Actor = - | UserActor - | ServiceActor - | ServiceActorV3; +export type Actor = UserActor | ServiceActor | ServiceActorV3; interface GetSecretsEvent { - type: EventType.GET_SECRETS; - metadata: { - environment: string; - secretPath: string; - numberOfSecrets: number; - }; + type: EventType.GET_SECRETS; + metadata: { + environment: string; + secretPath: string; + numberOfSecrets: number; + }; } interface GetSecretEvent { - type: EventType.GET_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - }; + type: EventType.GET_SECRET; + metadata: { + environment: string; + secretPath: string; + secretId: string; + secretKey: string; + secretVersion: number; + }; } interface CreateSecretEvent { - type: EventType.CREATE_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - } + type: EventType.CREATE_SECRET; + metadata: { + environment: string; + secretPath: string; + secretId: string; + secretKey: string; + secretVersion: number; + }; } interface UpdateSecretEvent { - type: EventType.UPDATE_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - } + type: EventType.UPDATE_SECRET; + metadata: { + environment: string; + secretPath: string; + secretId: string; + secretKey: string; + secretVersion: number; + }; } interface DeleteSecretEvent { - type: EventType.DELETE_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - } + type: EventType.DELETE_SECRET; + metadata: { + environment: string; + secretPath: string; + secretId: string; + secretKey: string; + secretVersion: number; + }; } interface GetWorkspaceKeyEvent { - type: EventType.GET_WORKSPACE_KEY, - metadata: { - keyId: string; - } + type: EventType.GET_WORKSPACE_KEY; + metadata: { + keyId: string; + }; } interface AuthorizeIntegrationEvent { - type: EventType.AUTHORIZE_INTEGRATION; - metadata: { - integration: string; - } + type: EventType.AUTHORIZE_INTEGRATION; + metadata: { + integration: string; + }; } interface UnauthorizeIntegrationEvent { - type: EventType.UNAUTHORIZE_INTEGRATION; - metadata: { - integration: string; - } + type: EventType.UNAUTHORIZE_INTEGRATION; + metadata: { + integration: string; + }; } interface CreateIntegrationEvent { - type: EventType.CREATE_INTEGRATION; - metadata: { - integrationId: string; - integration: string; - environment: string; - secretPath: string; - url?: string; - app?: string; - appId?: string; - targetEnvironment?: string; - targetEnvironmentId?: string; - targetService?: string; - targetServiceId?: string; - path?: string; - region?: string; - } + type: EventType.CREATE_INTEGRATION; + metadata: { + integrationId: string; + integration: string; + environment: string; + secretPath: string; + url?: string; + app?: string; + appId?: string; + targetEnvironment?: string; + targetEnvironmentId?: string; + targetService?: string; + targetServiceId?: string; + path?: string; + region?: string; + }; } interface DeleteIntegrationEvent { - type: EventType.DELETE_INTEGRATION; - metadata: { - integrationId: string; - integration: string; - environment: string; - secretPath: string; - url?: string; - app?: string; - appId?: string; - targetEnvironment?: string; - targetEnvironmentId?: string; - targetService?: string; - targetServiceId?: string; - path?: string; - region?: string; - } + type: EventType.DELETE_INTEGRATION; + metadata: { + integrationId: string; + integration: string; + environment: string; + secretPath: string; + url?: string; + app?: string; + appId?: string; + targetEnvironment?: string; + targetEnvironmentId?: string; + targetService?: string; + targetServiceId?: string; + path?: string; + region?: string; + }; } interface AddTrustedIPEvent { - type: EventType.ADD_TRUSTED_IP; - metadata: { - trustedIpId: string; - ipAddress: string; - prefix?: number; - } + type: EventType.ADD_TRUSTED_IP; + metadata: { + trustedIpId: string; + ipAddress: string; + prefix?: number; + }; } interface UpdateTrustedIPEvent { - type: EventType.UPDATE_TRUSTED_IP; - metadata: { - trustedIpId: string; - ipAddress: string; - prefix?: number; - } + type: EventType.UPDATE_TRUSTED_IP; + metadata: { + trustedIpId: string; + ipAddress: string; + prefix?: number; + }; } interface DeleteTrustedIPEvent { - type: EventType.DELETE_TRUSTED_IP; - metadata: { - trustedIpId: string; - ipAddress: string; - prefix?: number; - } + type: EventType.DELETE_TRUSTED_IP; + metadata: { + trustedIpId: string; + ipAddress: string; + prefix?: number; + }; } interface CreateServiceTokenEvent { - type: EventType.CREATE_SERVICE_TOKEN; - metadata: { - name: string; - scopes: Array<{ - environment: string; - secretPath: string; - }>; - } + type: EventType.CREATE_SERVICE_TOKEN; + metadata: { + name: string; + scopes: Array<{ + environment: string; + secretPath: string; + }>; + }; } interface DeleteServiceTokenEvent { - type: EventType.DELETE_SERVICE_TOKEN; - metadata: { - name: string; - scopes: Array<{ - environment: string; - secretPath: string; - }>; - } + type: EventType.DELETE_SERVICE_TOKEN; + metadata: { + name: string; + scopes: Array<{ + environment: string; + secretPath: string; + }>; + }; } interface CreateServiceTokenV3Event { - type: EventType.CREATE_SERVICE_TOKEN_V3; - metadata: { - name: string; - isActive: boolean; - scopes: Array; - expiresAt?: Date; - } + type: EventType.CREATE_SERVICE_TOKEN_V3; + metadata: { + name: string; + isActive: boolean; + scopes: Array; + expiresAt?: Date; + }; } interface UpdateServiceTokenV3Event { - type: EventType.UPDATE_SERVICE_TOKEN_V3; - metadata: { - name?: string; - isActive?: boolean; - scopes?: Array; - expiresAt?: Date; - } + type: EventType.UPDATE_SERVICE_TOKEN_V3; + metadata: { + name?: string; + isActive?: boolean; + scopes?: Array; + expiresAt?: Date; + }; } interface DeleteServiceTokenV3Event { - type: EventType.DELETE_SERVICE_TOKEN_V3; - metadata: { - name: string; - isActive: boolean; - scopes: Array; - expiresAt?: Date; - } + type: EventType.DELETE_SERVICE_TOKEN_V3; + metadata: { + name: string; + isActive: boolean; + scopes: Array; + expiresAt?: Date; + }; } interface CreateEnvironmentEvent { - type: EventType.CREATE_ENVIRONMENT; - metadata: { - name: string; - slug: string; - } + type: EventType.CREATE_ENVIRONMENT; + metadata: { + name: string; + slug: string; + }; } interface UpdateEnvironmentEvent { - type: EventType.UPDATE_ENVIRONMENT; - metadata: { - oldName: string; - newName: string; - oldSlug: string; - newSlug: string; - } + type: EventType.UPDATE_ENVIRONMENT; + metadata: { + oldName: string; + newName: string; + oldSlug: string; + newSlug: string; + }; } interface DeleteEnvironmentEvent { - type: EventType.DELETE_ENVIRONMENT; - metadata: { - name: string; - slug: string; - } + type: EventType.DELETE_ENVIRONMENT; + metadata: { + name: string; + slug: string; + }; } interface AddWorkspaceMemberEvent { - type: EventType.ADD_WORKSPACE_MEMBER; - metadata: { - userId: string; - email: string; - } + type: EventType.ADD_WORKSPACE_MEMBER; + metadata: { + userId: string; + email: string; + }; } interface RemoveWorkspaceMemberEvent { - type: EventType.REMOVE_WORKSPACE_MEMBER; - metadata: { - userId: string; - email: string; - } + type: EventType.REMOVE_WORKSPACE_MEMBER; + metadata: { + userId: string; + email: string; + }; } interface CreateFolderEvent { - type: EventType.CREATE_FOLDER; - metadata: { - environment: string; - folderId: string; - folderName: string; - folderPath: string; - } + type: EventType.CREATE_FOLDER; + metadata: { + environment: string; + folderId: string; + folderName: string; + folderPath: string; + }; } interface UpdateFolderEvent { - type: EventType.UPDATE_FOLDER; - metadata: { - environment: string; - folderId: string; - oldFolderName: string; - newFolderName: string; - folderPath: string; - } + type: EventType.UPDATE_FOLDER; + metadata: { + environment: string; + folderId: string; + oldFolderName: string; + newFolderName: string; + folderPath: string; + }; } interface DeleteFolderEvent { - type: EventType.DELETE_FOLDER; - metadata: { - environment: string; - folderId: string; - folderName: string; - folderPath: string; - } + type: EventType.DELETE_FOLDER; + metadata: { + environment: string; + folderId: string; + folderName: string; + folderPath: string; + }; } interface CreateWebhookEvent { - type: EventType.CREATE_WEBHOOK, - metadata: { - webhookId: string; - environment: string; - secretPath: string; - webhookUrl: string; - isDisabled: boolean; - } + type: EventType.CREATE_WEBHOOK; + metadata: { + webhookId: string; + environment: string; + secretPath: string; + webhookUrl: string; + isDisabled: boolean; + }; } interface UpdateWebhookStatusEvent { - type: EventType.UPDATE_WEBHOOK_STATUS, - metadata: { - webhookId: string; - environment: string; - secretPath: string; - webhookUrl: string; - isDisabled: boolean; - } + type: EventType.UPDATE_WEBHOOK_STATUS; + metadata: { + webhookId: string; + environment: string; + secretPath: string; + webhookUrl: string; + isDisabled: boolean; + }; } interface DeleteWebhookEvent { - type: EventType.DELETE_WEBHOOK, - metadata: { - webhookId: string; - environment: string; - secretPath: string; - webhookUrl: string; - isDisabled: boolean; - } + type: EventType.DELETE_WEBHOOK; + metadata: { + webhookId: string; + environment: string; + secretPath: string; + webhookUrl: string; + isDisabled: boolean; + }; } interface GetSecretImportsEvent { - type: EventType.GET_SECRET_IMPORTS, - metadata: { - environment: string; - secretImportId: string; - folderId: string; - numberOfImports: number; - } + type: EventType.GET_SECRET_IMPORTS; + metadata: { + environment: string; + secretImportId: string; + folderId: string; + numberOfImports: number; + }; } interface CreateSecretImportEvent { - type: EventType.CREATE_SECRET_IMPORT, - metadata: { - secretImportId: string; - folderId: string; - importFromEnvironment: string; - importFromSecretPath: string; - importToEnvironment: string; - importToSecretPath: string; - } + type: EventType.CREATE_SECRET_IMPORT; + metadata: { + secretImportId: string; + folderId: string; + importFromEnvironment: string; + importFromSecretPath: string; + importToEnvironment: string; + importToSecretPath: string; + }; } interface UpdateSecretImportEvent { - type: EventType.UPDATE_SECRET_IMPORT, - metadata: { - secretImportId: string; - folderId: string; - importToEnvironment: string; - importToSecretPath: string; - orderBefore: { - environment: string; - secretPath: string; - }[], - orderAfter: { - environment: string; - secretPath: string; - }[] - } + type: EventType.UPDATE_SECRET_IMPORT; + metadata: { + secretImportId: string; + folderId: string; + importToEnvironment: string; + importToSecretPath: string; + orderBefore: { + environment: string; + secretPath: string; + }[]; + orderAfter: { + environment: string; + secretPath: string; + }[]; + }; } interface DeleteSecretImportEvent { - type: EventType.DELETE_SECRET_IMPORT, - metadata: { - secretImportId: string; - folderId: string; - importFromEnvironment: string; - importFromSecretPath: string; - importToEnvironment: string; - importToSecretPath: string; - } + type: EventType.DELETE_SECRET_IMPORT; + metadata: { + secretImportId: string; + folderId: string; + importFromEnvironment: string; + importFromSecretPath: string; + importToEnvironment: string; + importToSecretPath: string; + }; } interface UpdateUserRole { - type: EventType.UPDATE_USER_WORKSPACE_ROLE, - metadata: { - userId: string; - email: string; - oldRole: string; - newRole: string; - } + type: EventType.UPDATE_USER_WORKSPACE_ROLE; + metadata: { + userId: string; + email: string; + oldRole: string; + newRole: string; + }; } interface UpdateUserDeniedPermissions { - type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS, - metadata: { - userId: string; - email: string; - deniedPermissions: { - environmentSlug: string; - ability: string; - }[] - } + type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS; + metadata: { + userId: string; + email: string; + deniedPermissions: { + environmentSlug: string; + ability: string; + }[]; + }; } - -export type Event = - | GetSecretsEvent - | GetSecretEvent - | CreateSecretEvent - | UpdateSecretEvent - | DeleteSecretEvent - | GetWorkspaceKeyEvent - | AuthorizeIntegrationEvent - | UnauthorizeIntegrationEvent - | CreateIntegrationEvent - | DeleteIntegrationEvent - | AddTrustedIPEvent - | UpdateTrustedIPEvent - | DeleteTrustedIPEvent - | CreateServiceTokenEvent - | DeleteServiceTokenEvent - | CreateServiceTokenV3Event - | UpdateServiceTokenV3Event - | DeleteServiceTokenV3Event - | CreateEnvironmentEvent - | UpdateEnvironmentEvent - | DeleteEnvironmentEvent - | AddWorkspaceMemberEvent - | RemoveWorkspaceMemberEvent - | CreateFolderEvent - | UpdateFolderEvent - | DeleteFolderEvent - | CreateWebhookEvent - | UpdateWebhookStatusEvent - | DeleteWebhookEvent - | GetSecretImportsEvent - | CreateSecretImportEvent - | UpdateSecretImportEvent - | DeleteSecretImportEvent - | UpdateUserRole - | UpdateUserDeniedPermissions; +export type Event = + | GetSecretsEvent + | GetSecretEvent + | CreateSecretEvent + | UpdateSecretEvent + | DeleteSecretEvent + | GetWorkspaceKeyEvent + | AuthorizeIntegrationEvent + | UnauthorizeIntegrationEvent + | CreateIntegrationEvent + | DeleteIntegrationEvent + | AddTrustedIPEvent + | UpdateTrustedIPEvent + | DeleteTrustedIPEvent + | CreateServiceTokenEvent + | DeleteServiceTokenEvent + | CreateServiceTokenV3Event + | UpdateServiceTokenV3Event + | DeleteServiceTokenV3Event + | CreateEnvironmentEvent + | UpdateEnvironmentEvent + | DeleteEnvironmentEvent + | AddWorkspaceMemberEvent + | RemoveWorkspaceMemberEvent + | CreateFolderEvent + | UpdateFolderEvent + | DeleteFolderEvent + | CreateWebhookEvent + | UpdateWebhookStatusEvent + | DeleteWebhookEvent + | GetSecretImportsEvent + | CreateSecretImportEvent + | UpdateSecretImportEvent + | DeleteSecretImportEvent + | UpdateUserRole + | UpdateUserDeniedPermissions; export type AuditLog = { _id: string; actor: Actor; - organization: string; + organization: string; workspace: string; ipAddress: string; event: Event; @@ -465,14 +457,13 @@ export type AuditLog = { userAgentType: UserAgentType; createdAt: string; updatedAt: string; -} +}; export type AuditLogFilters = { - eventType?: EventType; - userAgentType?: UserAgentType; - actor?: string; - offset: number; - limit: number; - startDate?: Date; - endDate?: Date; -} \ No newline at end of file + eventType?: EventType; + userAgentType?: UserAgentType; + actor?: string; + limit: number; + startDate?: Date; + endDate?: Date; +}; diff --git a/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx b/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx index ce3ce4ece..d0a09688d 100644 --- a/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx +++ b/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx @@ -8,7 +8,7 @@ export const AuditLogsPage = withProjectPermission( return (
-
+

Audit Logs

diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx index ddc271ef0..b48cce4e3 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx @@ -3,13 +3,7 @@ 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 { Button, DatePicker, FormControl, Select, SelectItem } from "@app/components/v2"; import { useWorkspace } from "@app/context"; import { useGetAuditLogActorFilterOpts } from "@app/hooks/api"; import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants"; @@ -19,201 +13,207 @@ import { Actor } from "@app/hooks/api/auditLogs/types"; import { AuditLogFilterFormData } from "./types"; const eventTypes = Object.entries(eventToNameMap).map(([value, label]) => ({ label, value })); -const userAgentTypes = Object.entries(userAgentTTypeoNameMap).map(([value, label]) => ({ label, value })); +const userAgentTypes = Object.entries(userAgentTTypeoNameMap).map(([value, label]) => ({ + label, + value +})); type Props = { - control: Control; - reset: UseFormReset; -} + control: Control; + reset: UseFormReset; +}; -export const LogsFilter = ({ - control, - reset -}: Props) => { - const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false); - const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false); +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 ?? ""); - - const renderActorSelectItem = (actor: Actor) => { - switch (actor.type) { - case ActorType.USER: - return ( - - {actor.metadata.email} - - ); - case ActorType.SERVICE: - return ( - - {actor.metadata.name} - - ); - case ActorType.SERVICE_V3: - return ( - - {actor.metadata.name} - - ); - default: - return ( - - N/A - - ); - } + const { currentWorkspace } = useWorkspace(); + const { data, isLoading } = useGetAuditLogActorFilterOpts(currentWorkspace?._id ?? ""); + const renderActorSelectItem = (actor: Actor) => { + switch (actor.type) { + case ActorType.USER: + return ( + + {actor.metadata.email} + + ); + case ActorType.SERVICE: + return ( + + {actor.metadata.name} + + ); + case ActorType.SERVICE_V3: + return ( + + {actor.metadata.name} + + ); + default: + return ( + + N/A + + ); } + }; - return ( -
-
- ( - - - - )} - /> - {!isLoading && data && data.length > 0 && ( - ( - - - - )} - /> - )} - ( - - - - )} - /> - { - return ( - - { - onChange(date); - setIsStartDatePickerOpen(false); - }} - popUpProps={{ - open: isStartDatePickerOpen, - onOpenChange: setIsStartDatePickerOpen - }} - popUpContentProps={{}} - /> - - ); - }} - /> - { - return ( - - { - onChange(date); - setIsEndDatePickerOpen(false); - }} - popUpProps={{ - open: isEndDatePickerOpen, - onOpenChange: setIsEndDatePickerOpen - }} - popUpContentProps={{}} - /> - - ); - }} - /> -
-
- +
+
+ ); +}; diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx index 898943f87..f21de95bb 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx @@ -13,78 +13,54 @@ import { LogsTable } from "./LogsTable"; import { AuditLogFilterFormData, auditLogFilterFormSchema } from "./types"; export const LogsSection = () => { - const { subscription } = useSubscription(); - const router = useRouter(); - - const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([ - "upgradePlan" - ] as const); - - const { - control, - reset, - watch, - setValue, - } = useForm({ - resolver: yupResolver(auditLogFilterFormSchema), - defaultValues: { - page: 1, - perPage: 10 - } - }); - - useEffect(() => { - if (subscription && !subscription.auditLogs) { - handlePopUpOpen("upgradePlan"); - } - }, [subscription]); + const { subscription } = useSubscription(); + const router = useRouter(); - const eventType = watch("eventType") as EventType | undefined; - const userAgentType = watch("userAgentType") as UserAgentType | undefined; - const actor = watch("actor"); + const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const); - const startDate = watch("startDate"); - const endDate = watch("endDate"); + const { control, reset, watch } = useForm({ + resolver: yupResolver(auditLogFilterFormSchema), + defaultValues: { + page: 1, + perPage: 10 + } + }); - const page = watch("page") as number; - const perPage = watch("perPage") as number; - - return ( -
- {/*
-

- Audit Logs -

-
*/} - - - { - - if (!isOpen) { - router.back(); - return; - } + useEffect(() => { + if (subscription && !subscription.auditLogs) { + handlePopUpOpen("upgradePlan"); + } + }, [subscription]); - handlePopUpToggle("upgradePlan", isOpen) - }} - text="You can use audit logs if you switch to a paid Infisical plan." - /> -
- ); - } \ No newline at end of file + const eventType = watch("eventType") as EventType | undefined; + const userAgentType = watch("userAgentType") as UserAgentType | undefined; + const actor = watch("actor"); + + const startDate = watch("startDate"); + const endDate = watch("endDate"); + + return ( +
+ + + { + if (!isOpen) { + router.back(); + return; + } + + handlePopUpToggle("upgradePlan", isOpen); + }} + text="You can use audit logs if you switch to a paid Infisical plan." + /> +
+ ); +}; diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsTable.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsTable.tsx index f6d38bf7c..273347ae2 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsTable.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsTable.tsx @@ -1,96 +1,95 @@ +import { Fragment } from "react"; import { faFile } from "@fortawesome/free-solid-svg-icons"; import { - EmptyState, - Pagination, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tr} from "@app/components/v2"; + Button, + EmptyState, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr +} from "@app/components/v2"; import { useWorkspace } from "@app/context"; import { useGetAuditLogs } from "@app/hooks/api"; import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums"; import { LogsTableRow } from "./LogsTableRow"; -import { SetValueType } from "./types"; type Props = { - eventType?: EventType; - userAgentType?: UserAgentType; - actor?: string; - startDate?: Date; - endDate?: Date; - page: number; - perPage: number; - setValue: SetValueType; -} + eventType?: EventType; + userAgentType?: UserAgentType; + actor?: string; + startDate?: Date; + endDate?: Date; +}; -export const LogsTable = ({ - eventType, - userAgentType, - actor, - startDate, - endDate, - page, - perPage, - setValue -}: Props) => { - const { currentWorkspace } = useWorkspace(); - const { data, isLoading } = useGetAuditLogs(currentWorkspace?._id ?? "", { - eventType, - userAgentType, - actor, - startDate, - endDate, - offset: (page - 1) * perPage, - limit: perPage - }); - - return ( - - - - - - - - - - - - - {!isLoading && data?.auditLogs && data?.auditLogs.map((auditLog) => ( - - ))} - {isLoading && } - {!isLoading && data?.auditLogs && data?.auditLogs.length === 0 && ( - - - - )} - -
TimestampEventActorSourceMetadata
- -
- {!isLoading && data?.totalCount !== undefined && ( - setValue("page", newPage)} - onChangePerPage={(newPerPage) => setValue("perPage", newPerPage)} - /> +const AUDIT_LOG_LIMIT = 15; + +export const LogsTable = ({ eventType, userAgentType, actor, startDate, endDate }: Props) => { + const { currentWorkspace } = useWorkspace(); + const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useGetAuditLogs( + currentWorkspace?._id ?? "", + { + eventType, + userAgentType, + actor, + startDate, + endDate, + limit: AUDIT_LOG_LIMIT + } + ); + + const isEmpty = !isLoading && !data?.pages?.[0].length; + + return ( +
+ + + + + + + + + + + + + {!isLoading && + data?.pages?.map((group, i) => ( + + {group.map((auditLog) => ( + + ))} + + ))} + {isLoading && } + {isEmpty && ( + + + )} - - ); -} \ No newline at end of file + +
TimestampEventActorSourceMetadata
+ +
+
+ {!isEmpty && ( + + )} +
+ ); +};