mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 16:27:40 +00:00
feat(audit-log): added index over workspace key and changed pagination to load more type
This commit is contained in:
@@ -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 auditLogs = await AuditLog.find(query).sort({ createdAt: -1 }).skip(offset).limit(limit);
|
||||||
|
|
||||||
const totalCount = await AuditLog.countDocuments(query);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
auditLogs,
|
auditLogs
|
||||||
totalCount
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -679,11 +674,7 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const actors = [
|
const actors = [...userActors, ...serviceActors, ...serviceV3Actors];
|
||||||
...userActors,
|
|
||||||
...serviceActors,
|
|
||||||
...serviceV3Actors
|
|
||||||
];
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
actors
|
actors
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import { Schema, Types, model } from "mongoose";
|
import { Schema, Types, model } from "mongoose";
|
||||||
import {
|
import { ActorType, EventType, UserAgentType } from "./enums";
|
||||||
ActorType,
|
import { Actor, Event } from "./types";
|
||||||
EventType,
|
|
||||||
UserAgentType
|
|
||||||
} from "./enums";
|
|
||||||
import {
|
|
||||||
Actor,
|
|
||||||
Event
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
export interface IAuditLog {
|
export interface IAuditLog {
|
||||||
actor: Actor;
|
actor: Actor;
|
||||||
@@ -38,7 +31,8 @@ const auditLogSchema = new Schema<IAuditLog>(
|
|||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
required: false
|
required: false,
|
||||||
|
index: true
|
||||||
},
|
},
|
||||||
ipAddress: {
|
ipAddress: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const validateClientForWorkspace = async ({
|
|||||||
environment,
|
environment,
|
||||||
requiredPermissions
|
requiredPermissions
|
||||||
});
|
});
|
||||||
return { membership, workspace};
|
return { membership, workspace };
|
||||||
case ActorType.SERVICE_V3:
|
case ActorType.SERVICE_V3:
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: "Failed service token authorization for organization"
|
message: "Failed service token authorization for organization"
|
||||||
@@ -121,8 +121,8 @@ export const GetWorkspaceAuditLogsV1 = z.object({
|
|||||||
userAgentType: z.nativeEnum(UserAgentType).nullable().optional(),
|
userAgentType: z.nativeEnum(UserAgentType).nullable().optional(),
|
||||||
startDate: z.string().datetime().nullable().optional(),
|
startDate: z.string().datetime().nullable().optional(),
|
||||||
endDate: z.string().datetime().nullable().optional(),
|
endDate: z.string().datetime().nullable().optional(),
|
||||||
offset: z.coerce.number(),
|
offset: z.coerce.number().default(0),
|
||||||
limit: z.coerce.number(),
|
limit: z.coerce.number().default(20),
|
||||||
actor: z.string().nullish().optional()
|
actor: z.string().nullish().optional()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,59 +1,46 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import {
|
import { Actor, AuditLog, AuditLogFilters } from "./types";
|
||||||
Actor,
|
|
||||||
AuditLog,
|
|
||||||
AuditLogFilters
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
export const workspaceKeys = {
|
export const workspaceKeys = {
|
||||||
getAuditLogs: (workspaceId: string, filters: AuditLogFilters) => [{ workspaceId, filters }, "audit-logs"] as const,
|
getAuditLogs: (workspaceId: string, filters: AuditLogFilters) =>
|
||||||
getAuditLogActorFilterOpts: (workspaceId: string) => [{ workspaceId }, "audit-log-actor-filters"] as const
|
[{ workspaceId, filters }, "audit-logs"] as const,
|
||||||
}
|
getAuditLogActorFilterOpts: (workspaceId: string) =>
|
||||||
|
[{ workspaceId }, "audit-log-actor-filters"] as const
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetAuditLogs = (workspaceId: string, filters: AuditLogFilters) => {
|
export const useGetAuditLogs = (workspaceId: string, filters: AuditLogFilters) => {
|
||||||
return useQuery({
|
return useInfiniteQuery({
|
||||||
queryKey: workspaceKeys.getAuditLogs(workspaceId, filters),
|
queryKey: workspaceKeys.getAuditLogs(workspaceId, filters),
|
||||||
queryFn: async () => {
|
queryFn: async ({ pageParam }) => {
|
||||||
|
const { data } = await apiRequest.get<{ auditLogs: AuditLog[] }>(
|
||||||
const params = new URLSearchParams();
|
`/api/v1/workspace/${workspaceId}/audit-logs`,
|
||||||
if (filters.eventType) {
|
{
|
||||||
params.append("eventType", filters.eventType);
|
params: {
|
||||||
|
...filters,
|
||||||
|
offset: pageParam,
|
||||||
|
startDate: filters?.startDate?.toISOString(),
|
||||||
|
endDate: filters?.endDate?.toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
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 data.auditLogs;
|
||||||
|
},
|
||||||
|
getNextPageParam: (lastPage, pages) =>
|
||||||
|
lastPage.length !== 0 ? pages.length * filters.limit : undefined
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useGetAuditLogActorFilterOpts = (workspaceId: string) => {
|
export const useGetAuditLogActorFilterOpts = (workspaceId: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: workspaceKeys.getAuditLogActorFilterOpts(workspaceId),
|
queryKey: workspaceKeys.getAuditLogActorFilterOpts(workspaceId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<{ actors: Actor[] }>(`/api/v1/workspace/${workspaceId}/audit-logs/filters/actors`);
|
const { data } = await apiRequest.get<{ actors: Actor[] }>(
|
||||||
|
`/api/v1/workspace/${workspaceId}/audit-logs/filters/actors`
|
||||||
|
);
|
||||||
return data.actors;
|
return data.actors;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import { ActorType, EventType, UserAgentType } from "./enums";
|
||||||
ActorType,
|
|
||||||
EventType,
|
|
||||||
UserAgentType
|
|
||||||
} from "./enums";
|
|
||||||
|
|
||||||
enum Permission {
|
enum Permission {
|
||||||
READ = "read",
|
READ = "read",
|
||||||
@@ -40,10 +36,7 @@ export interface ServiceActorV3 {
|
|||||||
metadata: ServiceActorMetadata;
|
metadata: ServiceActorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Actor =
|
export type Actor = UserActor | ServiceActor | ServiceActorV3;
|
||||||
| UserActor
|
|
||||||
| ServiceActor
|
|
||||||
| ServiceActorV3;
|
|
||||||
|
|
||||||
interface GetSecretsEvent {
|
interface GetSecretsEvent {
|
||||||
type: EventType.GET_SECRETS;
|
type: EventType.GET_SECRETS;
|
||||||
@@ -73,7 +66,7 @@ interface CreateSecretEvent {
|
|||||||
secretId: string;
|
secretId: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
secretVersion: number;
|
secretVersion: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateSecretEvent {
|
interface UpdateSecretEvent {
|
||||||
@@ -84,7 +77,7 @@ interface UpdateSecretEvent {
|
|||||||
secretId: string;
|
secretId: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
secretVersion: number;
|
secretVersion: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteSecretEvent {
|
interface DeleteSecretEvent {
|
||||||
@@ -95,28 +88,28 @@ interface DeleteSecretEvent {
|
|||||||
secretId: string;
|
secretId: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
secretVersion: number;
|
secretVersion: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetWorkspaceKeyEvent {
|
interface GetWorkspaceKeyEvent {
|
||||||
type: EventType.GET_WORKSPACE_KEY,
|
type: EventType.GET_WORKSPACE_KEY;
|
||||||
metadata: {
|
metadata: {
|
||||||
keyId: string;
|
keyId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthorizeIntegrationEvent {
|
interface AuthorizeIntegrationEvent {
|
||||||
type: EventType.AUTHORIZE_INTEGRATION;
|
type: EventType.AUTHORIZE_INTEGRATION;
|
||||||
metadata: {
|
metadata: {
|
||||||
integration: string;
|
integration: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UnauthorizeIntegrationEvent {
|
interface UnauthorizeIntegrationEvent {
|
||||||
type: EventType.UNAUTHORIZE_INTEGRATION;
|
type: EventType.UNAUTHORIZE_INTEGRATION;
|
||||||
metadata: {
|
metadata: {
|
||||||
integration: string;
|
integration: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateIntegrationEvent {
|
interface CreateIntegrationEvent {
|
||||||
@@ -135,7 +128,7 @@ interface CreateIntegrationEvent {
|
|||||||
targetServiceId?: string;
|
targetServiceId?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteIntegrationEvent {
|
interface DeleteIntegrationEvent {
|
||||||
@@ -154,7 +147,7 @@ interface DeleteIntegrationEvent {
|
|||||||
targetServiceId?: string;
|
targetServiceId?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddTrustedIPEvent {
|
interface AddTrustedIPEvent {
|
||||||
@@ -163,7 +156,7 @@ interface AddTrustedIPEvent {
|
|||||||
trustedIpId: string;
|
trustedIpId: string;
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
prefix?: number;
|
prefix?: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateTrustedIPEvent {
|
interface UpdateTrustedIPEvent {
|
||||||
@@ -172,7 +165,7 @@ interface UpdateTrustedIPEvent {
|
|||||||
trustedIpId: string;
|
trustedIpId: string;
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
prefix?: number;
|
prefix?: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteTrustedIPEvent {
|
interface DeleteTrustedIPEvent {
|
||||||
@@ -181,7 +174,7 @@ interface DeleteTrustedIPEvent {
|
|||||||
trustedIpId: string;
|
trustedIpId: string;
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
prefix?: number;
|
prefix?: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateServiceTokenEvent {
|
interface CreateServiceTokenEvent {
|
||||||
@@ -192,7 +185,7 @@ interface CreateServiceTokenEvent {
|
|||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteServiceTokenEvent {
|
interface DeleteServiceTokenEvent {
|
||||||
@@ -203,7 +196,7 @@ interface DeleteServiceTokenEvent {
|
|||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateServiceTokenV3Event {
|
interface CreateServiceTokenV3Event {
|
||||||
@@ -213,7 +206,7 @@ interface CreateServiceTokenV3Event {
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
scopes: Array<Scope>;
|
scopes: Array<Scope>;
|
||||||
expiresAt?: Date;
|
expiresAt?: Date;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateServiceTokenV3Event {
|
interface UpdateServiceTokenV3Event {
|
||||||
@@ -223,7 +216,7 @@ interface UpdateServiceTokenV3Event {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
scopes?: Array<Scope>;
|
scopes?: Array<Scope>;
|
||||||
expiresAt?: Date;
|
expiresAt?: Date;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteServiceTokenV3Event {
|
interface DeleteServiceTokenV3Event {
|
||||||
@@ -233,7 +226,7 @@ interface DeleteServiceTokenV3Event {
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
scopes: Array<Scope>;
|
scopes: Array<Scope>;
|
||||||
expiresAt?: Date;
|
expiresAt?: Date;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateEnvironmentEvent {
|
interface CreateEnvironmentEvent {
|
||||||
@@ -241,7 +234,7 @@ interface CreateEnvironmentEvent {
|
|||||||
metadata: {
|
metadata: {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateEnvironmentEvent {
|
interface UpdateEnvironmentEvent {
|
||||||
@@ -251,7 +244,7 @@ interface UpdateEnvironmentEvent {
|
|||||||
newName: string;
|
newName: string;
|
||||||
oldSlug: string;
|
oldSlug: string;
|
||||||
newSlug: string;
|
newSlug: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteEnvironmentEvent {
|
interface DeleteEnvironmentEvent {
|
||||||
@@ -259,7 +252,7 @@ interface DeleteEnvironmentEvent {
|
|||||||
metadata: {
|
metadata: {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddWorkspaceMemberEvent {
|
interface AddWorkspaceMemberEvent {
|
||||||
@@ -267,7 +260,7 @@ interface AddWorkspaceMemberEvent {
|
|||||||
metadata: {
|
metadata: {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RemoveWorkspaceMemberEvent {
|
interface RemoveWorkspaceMemberEvent {
|
||||||
@@ -275,7 +268,7 @@ interface RemoveWorkspaceMemberEvent {
|
|||||||
metadata: {
|
metadata: {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateFolderEvent {
|
interface CreateFolderEvent {
|
||||||
@@ -285,7 +278,7 @@ interface CreateFolderEvent {
|
|||||||
folderId: string;
|
folderId: string;
|
||||||
folderName: string;
|
folderName: string;
|
||||||
folderPath: string;
|
folderPath: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateFolderEvent {
|
interface UpdateFolderEvent {
|
||||||
@@ -296,7 +289,7 @@ interface UpdateFolderEvent {
|
|||||||
oldFolderName: string;
|
oldFolderName: string;
|
||||||
newFolderName: string;
|
newFolderName: string;
|
||||||
folderPath: string;
|
folderPath: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteFolderEvent {
|
interface DeleteFolderEvent {
|
||||||
@@ -306,54 +299,54 @@ interface DeleteFolderEvent {
|
|||||||
folderId: string;
|
folderId: string;
|
||||||
folderName: string;
|
folderName: string;
|
||||||
folderPath: string;
|
folderPath: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateWebhookEvent {
|
interface CreateWebhookEvent {
|
||||||
type: EventType.CREATE_WEBHOOK,
|
type: EventType.CREATE_WEBHOOK;
|
||||||
metadata: {
|
metadata: {
|
||||||
webhookId: string;
|
webhookId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
webhookUrl: string;
|
webhookUrl: string;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateWebhookStatusEvent {
|
interface UpdateWebhookStatusEvent {
|
||||||
type: EventType.UPDATE_WEBHOOK_STATUS,
|
type: EventType.UPDATE_WEBHOOK_STATUS;
|
||||||
metadata: {
|
metadata: {
|
||||||
webhookId: string;
|
webhookId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
webhookUrl: string;
|
webhookUrl: string;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteWebhookEvent {
|
interface DeleteWebhookEvent {
|
||||||
type: EventType.DELETE_WEBHOOK,
|
type: EventType.DELETE_WEBHOOK;
|
||||||
metadata: {
|
metadata: {
|
||||||
webhookId: string;
|
webhookId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
webhookUrl: string;
|
webhookUrl: string;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetSecretImportsEvent {
|
interface GetSecretImportsEvent {
|
||||||
type: EventType.GET_SECRET_IMPORTS,
|
type: EventType.GET_SECRET_IMPORTS;
|
||||||
metadata: {
|
metadata: {
|
||||||
environment: string;
|
environment: string;
|
||||||
secretImportId: string;
|
secretImportId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
numberOfImports: number;
|
numberOfImports: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateSecretImportEvent {
|
interface CreateSecretImportEvent {
|
||||||
type: EventType.CREATE_SECRET_IMPORT,
|
type: EventType.CREATE_SECRET_IMPORT;
|
||||||
metadata: {
|
metadata: {
|
||||||
secretImportId: string;
|
secretImportId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
@@ -361,11 +354,11 @@ interface CreateSecretImportEvent {
|
|||||||
importFromSecretPath: string;
|
importFromSecretPath: string;
|
||||||
importToEnvironment: string;
|
importToEnvironment: string;
|
||||||
importToSecretPath: string;
|
importToSecretPath: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateSecretImportEvent {
|
interface UpdateSecretImportEvent {
|
||||||
type: EventType.UPDATE_SECRET_IMPORT,
|
type: EventType.UPDATE_SECRET_IMPORT;
|
||||||
metadata: {
|
metadata: {
|
||||||
secretImportId: string;
|
secretImportId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
@@ -374,16 +367,16 @@ interface UpdateSecretImportEvent {
|
|||||||
orderBefore: {
|
orderBefore: {
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
}[],
|
}[];
|
||||||
orderAfter: {
|
orderAfter: {
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
}[]
|
}[];
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteSecretImportEvent {
|
interface DeleteSecretImportEvent {
|
||||||
type: EventType.DELETE_SECRET_IMPORT,
|
type: EventType.DELETE_SECRET_IMPORT;
|
||||||
metadata: {
|
metadata: {
|
||||||
secretImportId: string;
|
secretImportId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
@@ -391,32 +384,31 @@ interface DeleteSecretImportEvent {
|
|||||||
importFromSecretPath: string;
|
importFromSecretPath: string;
|
||||||
importToEnvironment: string;
|
importToEnvironment: string;
|
||||||
importToSecretPath: string;
|
importToSecretPath: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateUserRole {
|
interface UpdateUserRole {
|
||||||
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
|
type: EventType.UPDATE_USER_WORKSPACE_ROLE;
|
||||||
metadata: {
|
metadata: {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
oldRole: string;
|
oldRole: string;
|
||||||
newRole: string;
|
newRole: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateUserDeniedPermissions {
|
interface UpdateUserDeniedPermissions {
|
||||||
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS,
|
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS;
|
||||||
metadata: {
|
metadata: {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
deniedPermissions: {
|
deniedPermissions: {
|
||||||
environmentSlug: string;
|
environmentSlug: string;
|
||||||
ability: string;
|
ability: string;
|
||||||
}[]
|
}[];
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
@@ -465,14 +457,13 @@ export type AuditLog = {
|
|||||||
userAgentType: UserAgentType;
|
userAgentType: UserAgentType;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AuditLogFilters = {
|
export type AuditLogFilters = {
|
||||||
eventType?: EventType;
|
eventType?: EventType;
|
||||||
userAgentType?: UserAgentType;
|
userAgentType?: UserAgentType;
|
||||||
actor?: string;
|
actor?: string;
|
||||||
offset: number;
|
|
||||||
limit: number;
|
limit: number;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const AuditLogsPage = withProjectPermission(
|
|||||||
return (
|
return (
|
||||||
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
|
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
|
||||||
<div className="max-w-7xl px-6 w-full">
|
<div className="max-w-7xl px-6 w-full">
|
||||||
<div className="my-6">
|
<div className="py-6 sticky top-0 z-10 bg-bunker-800">
|
||||||
<p className="text-3xl font-semibold text-gray-200">Audit Logs</p>
|
<p className="text-3xl font-semibold text-gray-200">Audit Logs</p>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,13 +3,7 @@ import { Control, Controller, UseFormReset } from "react-hook-form";
|
|||||||
import { faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import {
|
import { Button, DatePicker, FormControl, Select, SelectItem } from "@app/components/v2";
|
||||||
Button,
|
|
||||||
DatePicker,
|
|
||||||
FormControl,
|
|
||||||
Select,
|
|
||||||
SelectItem
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
|
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
|
||||||
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
||||||
@@ -19,17 +13,17 @@ import { Actor } from "@app/hooks/api/auditLogs/types";
|
|||||||
import { AuditLogFilterFormData } from "./types";
|
import { AuditLogFilterFormData } from "./types";
|
||||||
|
|
||||||
const eventTypes = Object.entries(eventToNameMap).map(([value, label]) => ({ label, value }));
|
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 = {
|
type Props = {
|
||||||
control: Control<AuditLogFilterFormData>;
|
control: Control<AuditLogFilterFormData>;
|
||||||
reset: UseFormReset<AuditLogFilterFormData>;
|
reset: UseFormReset<AuditLogFilterFormData>;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const LogsFilter = ({
|
export const LogsFilter = ({ control, reset }: Props) => {
|
||||||
control,
|
|
||||||
reset
|
|
||||||
}: Props) => {
|
|
||||||
const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false);
|
const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false);
|
||||||
const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false);
|
const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false);
|
||||||
|
|
||||||
@@ -40,19 +34,28 @@ export const LogsFilter = ({
|
|||||||
switch (actor.type) {
|
switch (actor.type) {
|
||||||
case ActorType.USER:
|
case ActorType.USER:
|
||||||
return (
|
return (
|
||||||
<SelectItem value={`${actor.type}-${actor.metadata.userId}`} key={`user-actor-filter-${actor.metadata.userId}`}>
|
<SelectItem
|
||||||
|
value={`${actor.type}-${actor.metadata.userId}`}
|
||||||
|
key={`user-actor-filter-${actor.metadata.userId}`}
|
||||||
|
>
|
||||||
{actor.metadata.email}
|
{actor.metadata.email}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
);
|
);
|
||||||
case ActorType.SERVICE:
|
case ActorType.SERVICE:
|
||||||
return (
|
return (
|
||||||
<SelectItem value={`${actor.type}-${actor.metadata.serviceId}`} key={`service-actor-filter-${actor.metadata.serviceId}`}>
|
<SelectItem
|
||||||
|
value={`${actor.type}-${actor.metadata.serviceId}`}
|
||||||
|
key={`service-actor-filter-${actor.metadata.serviceId}`}
|
||||||
|
>
|
||||||
{actor.metadata.name}
|
{actor.metadata.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
);
|
);
|
||||||
case ActorType.SERVICE_V3:
|
case ActorType.SERVICE_V3:
|
||||||
return (
|
return (
|
||||||
<SelectItem value={`${actor.type}-${actor.metadata.serviceId}`} key={`service-actor-v3-filter-${actor.metadata.serviceId}`}>
|
<SelectItem
|
||||||
|
value={`${actor.type}-${actor.metadata.serviceId}`}
|
||||||
|
key={`service-actor-v3-filter-${actor.metadata.serviceId}`}
|
||||||
|
>
|
||||||
{actor.metadata.name}
|
{actor.metadata.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
);
|
);
|
||||||
@@ -63,11 +66,10 @@ export const LogsFilter = ({
|
|||||||
</SelectItem>
|
</SelectItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center sticky top-20 z-10 bg-bunker-800">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -155,7 +157,7 @@ export const LogsFilter = ({
|
|||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={field.value || undefined}
|
value={field.value || undefined}
|
||||||
onChange={date => {
|
onChange={(date) => {
|
||||||
onChange(date);
|
onChange(date);
|
||||||
setIsStartDatePickerOpen(false);
|
setIsStartDatePickerOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -174,14 +176,10 @@ export const LogsFilter = ({
|
|||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
||||||
return (
|
return (
|
||||||
<FormControl
|
<FormControl label="End date" errorText={error?.message} isError={Boolean(error)}>
|
||||||
label="End date"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={field.value || undefined}
|
value={field.value || undefined}
|
||||||
onChange={date => {
|
onChange={(date) => {
|
||||||
onChange(date);
|
onChange(date);
|
||||||
setIsEndDatePickerOpen(false);
|
setIsEndDatePickerOpen(false);
|
||||||
}}
|
}}
|
||||||
@@ -203,17 +201,19 @@ export const LogsFilter = ({
|
|||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
leftIcon={<FontAwesomeIcon icon={faFilterCircleXmark} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faFilterCircleXmark} className="mr-2" />}
|
||||||
onClick={() => reset({
|
onClick={() =>
|
||||||
|
reset({
|
||||||
eventType: undefined,
|
eventType: undefined,
|
||||||
actor: undefined,
|
actor: undefined,
|
||||||
userAgentType: undefined,
|
userAgentType: undefined,
|
||||||
startDate: undefined,
|
startDate: undefined,
|
||||||
endDate: undefined
|
endDate: undefined
|
||||||
})}
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Clear filters
|
Clear filters
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -16,16 +16,9 @@ export const LogsSection = () => {
|
|||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||||
"upgradePlan"
|
|
||||||
] as const);
|
|
||||||
|
|
||||||
const {
|
const { control, reset, watch } = useForm<AuditLogFilterFormData>({
|
||||||
control,
|
|
||||||
reset,
|
|
||||||
watch,
|
|
||||||
setValue,
|
|
||||||
} = useForm<AuditLogFilterFormData>({
|
|
||||||
resolver: yupResolver(auditLogFilterFormSchema),
|
resolver: yupResolver(auditLogFilterFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -46,45 +39,28 @@ export const LogsSection = () => {
|
|||||||
const startDate = watch("startDate");
|
const startDate = watch("startDate");
|
||||||
const endDate = watch("endDate");
|
const endDate = watch("endDate");
|
||||||
|
|
||||||
const page = watch("page") as number;
|
|
||||||
const perPage = watch("perPage") as number;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div>
|
||||||
// className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
|
<LogsFilter control={control} reset={reset} />
|
||||||
>
|
|
||||||
{/* <div className="flex items-center mb-8">
|
|
||||||
<h2 className="text-xl font-semibold flex-1 text-white">
|
|
||||||
Audit Logs
|
|
||||||
</h2>
|
|
||||||
</div> */}
|
|
||||||
<LogsFilter
|
|
||||||
control={control}
|
|
||||||
reset={reset}
|
|
||||||
/>
|
|
||||||
<LogsTable
|
<LogsTable
|
||||||
eventType={eventType}
|
eventType={eventType}
|
||||||
userAgentType={userAgentType}
|
userAgentType={userAgentType}
|
||||||
actor={actor}
|
actor={actor}
|
||||||
startDate={startDate}
|
startDate={startDate}
|
||||||
endDate={endDate}
|
endDate={endDate}
|
||||||
page={page}
|
|
||||||
perPage={perPage}
|
|
||||||
setValue={setValue}
|
|
||||||
/>
|
/>
|
||||||
<UpgradePlanModal
|
<UpgradePlanModal
|
||||||
isOpen={popUp.upgradePlan.isOpen}
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
onOpenChange={(isOpen) => {
|
onOpenChange={(isOpen) => {
|
||||||
|
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
router.back();
|
router.back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpToggle("upgradePlan", isOpen)
|
handlePopUpToggle("upgradePlan", isOpen);
|
||||||
}}
|
}}
|
||||||
text="You can use audit logs if you switch to a paid Infisical plan."
|
text="You can use audit logs if you switch to a paid Infisical plan."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import { Fragment } from "react";
|
||||||
import { faFile } from "@fortawesome/free-solid-svg-icons";
|
import { faFile } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Button,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
Pagination,
|
|
||||||
Table,
|
Table,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
TableSkeleton,
|
TableSkeleton,
|
||||||
@@ -10,13 +11,13 @@ import {
|
|||||||
Td,
|
Td,
|
||||||
Th,
|
Th,
|
||||||
THead,
|
THead,
|
||||||
Tr} from "@app/components/v2";
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { useGetAuditLogs } from "@app/hooks/api";
|
import { useGetAuditLogs } from "@app/hooks/api";
|
||||||
import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums";
|
import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums";
|
||||||
|
|
||||||
import { LogsTableRow } from "./LogsTableRow";
|
import { LogsTableRow } from "./LogsTableRow";
|
||||||
import { SetValueType } from "./types";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
eventType?: EventType;
|
eventType?: EventType;
|
||||||
@@ -24,33 +25,28 @@ type Props = {
|
|||||||
actor?: string;
|
actor?: string;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
page: number;
|
};
|
||||||
perPage: number;
|
|
||||||
setValue: SetValueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LogsTable = ({
|
const AUDIT_LOG_LIMIT = 15;
|
||||||
eventType,
|
|
||||||
userAgentType,
|
export const LogsTable = ({ eventType, userAgentType, actor, startDate, endDate }: Props) => {
|
||||||
actor,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
page,
|
|
||||||
perPage,
|
|
||||||
setValue
|
|
||||||
}: Props) => {
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { data, isLoading } = useGetAuditLogs(currentWorkspace?._id ?? "", {
|
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useGetAuditLogs(
|
||||||
|
currentWorkspace?._id ?? "",
|
||||||
|
{
|
||||||
eventType,
|
eventType,
|
||||||
userAgentType,
|
userAgentType,
|
||||||
actor,
|
actor,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
offset: (page - 1) * perPage,
|
limit: AUDIT_LOG_LIMIT
|
||||||
limit: perPage
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const isEmpty = !isLoading && !data?.pages?.[0].length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
<THead>
|
<THead>
|
||||||
@@ -63,34 +59,37 @@ export const LogsTable = ({
|
|||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
{!isLoading && data?.auditLogs && data?.auditLogs.map((auditLog) => (
|
{!isLoading &&
|
||||||
<LogsTableRow
|
data?.pages?.map((group, i) => (
|
||||||
auditLog={auditLog}
|
<Fragment key={`auditlog-item-${i + 1}`}>
|
||||||
key={`audit-log-${auditLog._id}`}
|
{group.map((auditLog) => (
|
||||||
/>
|
<LogsTableRow auditLog={auditLog} key={`audit-log-${auditLog._id}`} />
|
||||||
|
))}
|
||||||
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
{isLoading && <TableSkeleton innerKey="logs-table" columns={5} key="logs" />}
|
{isLoading && <TableSkeleton innerKey="logs-table" columns={5} key="logs" />}
|
||||||
{!isLoading && data?.auditLogs && data?.auditLogs.length === 0 && (
|
{isEmpty && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td colSpan={5}>
|
<Td colSpan={5}>
|
||||||
<EmptyState
|
<EmptyState title="No audit logs on file" icon={faFile} />
|
||||||
title="No audit logs on file"
|
|
||||||
icon={faFile}
|
|
||||||
/>
|
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
)}
|
)}
|
||||||
</TBody>
|
</TBody>
|
||||||
</Table>
|
</Table>
|
||||||
{!isLoading && data?.totalCount !== undefined && (
|
|
||||||
<Pagination
|
|
||||||
count={data?.totalCount}
|
|
||||||
page={page}
|
|
||||||
perPage={perPage}
|
|
||||||
onChangePage={(newPage) => setValue("page", newPage)}
|
|
||||||
onChangePerPage={(newPerPage) => setValue("perPage", newPerPage)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
{!isEmpty && (
|
||||||
|
<Button
|
||||||
|
className="mt-4 mb-20 py-3 px-4 text-sm"
|
||||||
|
isFullWidth
|
||||||
|
variant="star"
|
||||||
|
isLoading={isFetchingNextPage}
|
||||||
|
isDisabled={isFetchingNextPage || !hasNextPage}
|
||||||
|
onClick={() => fetchNextPage()}
|
||||||
|
>
|
||||||
|
{hasNextPage ? "Load More" : "End of logs"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user