diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index 5b755567b..d1647af3d 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -27,8 +27,8 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ customRateLimits: false, customAlerts: false, secretAccessInsights: false, - auditLogs: false, - auditLogsRetentionDays: 0, + auditLogs: true, + auditLogsRetentionDays: 30, auditLogStreams: false, auditLogStreamLimit: 3, samlSSO: false, diff --git a/frontend/src/components/v2/DatePicker/DatePicker.tsx b/frontend/src/components/v2/DatePicker/DatePicker.tsx index 88520f31a..37f22f59e 100644 --- a/frontend/src/components/v2/DatePicker/DatePicker.tsx +++ b/frontend/src/components/v2/DatePicker/DatePicker.tsx @@ -18,6 +18,7 @@ export type DatePickerProps = Omit & { popUpProps: PopoverProps; popUpContentProps: PopoverContentProps; dateFormat?: "PPP" | "PP" | "P"; // extend as needed + buttonClassName?: string; }; // Doc: https://react-day-picker.js.org/ @@ -27,6 +28,7 @@ export const DatePicker = ({ popUpProps, popUpContentProps, dateFormat = "PPP", + buttonClassName, ...props }: DatePickerProps) => { const [timeValue, setTimeValue] = useState(value ? format(value, "HH:mm") : "00:00"); @@ -57,7 +59,11 @@ export const DatePicker = ({ return ( - diff --git a/frontend/src/components/v2/Select/Select.tsx b/frontend/src/components/v2/Select/Select.tsx index 91c376662..f114c0f02 100644 --- a/frontend/src/components/v2/Select/Select.tsx +++ b/frontend/src/components/v2/Select/Select.tsx @@ -19,6 +19,7 @@ type Props = { icon?: IconProp; isMulti?: boolean; iconClassName?: string; + dropdownContainerStyle?: React.CSSProperties; }; export type SelectProps = Omit & Props; @@ -35,6 +36,7 @@ export const Select = forwardRef( position, containerClassName, iconClassName, + dropdownContainerStyle, ...props }, ref @@ -82,7 +84,7 @@ export const Select = forwardRef( dropdownContainerClassName )} position={position} - style={{ width: "var(--radix-select-trigger-width)" }} + style={dropdownContainerStyle ?? { width: "var(--radix-select-trigger-width)" }} >
diff --git a/frontend/src/hooks/api/auditLogs/queries.tsx b/frontend/src/hooks/api/auditLogs/queries.tsx index 52a35cb85..b254b46d3 100644 --- a/frontend/src/hooks/api/auditLogs/queries.tsx +++ b/frontend/src/hooks/api/auditLogs/queries.tsx @@ -57,6 +57,7 @@ export const useGetAuditLogs = ( }, getNextPageParam: (lastPage, pages) => lastPage.length !== 0 ? pages.length * filters.limit : undefined, + placeholderData: (prev) => prev, ...options }); }; diff --git a/frontend/src/pages/organization/AuditLogsPage/AuditLogsPage.tsx b/frontend/src/pages/organization/AuditLogsPage/AuditLogsPage.tsx index bec3bfd77..25459719f 100644 --- a/frontend/src/pages/organization/AuditLogsPage/AuditLogsPage.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/AuditLogsPage.tsx @@ -18,7 +18,7 @@ export const AuditLogsPage = () => { title="Audit logs" description="Audit logs for security and compliance teams to monitor information access." /> - +
diff --git a/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx b/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx index 87957ad22..bcdb3e8b7 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; -import { faArrowRight, faCalendar, faChevronRight } from "@fortawesome/free-solid-svg-icons"; +import { faCalendar, faChevronRight } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import { format } from "date-fns"; @@ -22,15 +22,26 @@ import { import { auditLogDateFilterFormSchema, AuditLogDateFilterType, - TAuditLogDateFilterFormData + TAuditLogDateFilterFormData, + Timezone } from "./types"; type Props = { setFilter: (data: TAuditLogDateFilterFormData) => void; filter: TAuditLogDateFilterFormData; + setTimezone: (timezone: Timezone) => void; + timezone: Timezone; }; const RELATIVE_VALUES = ["5m", "30m", "1h", "3h", "12h"]; -export const LogsDateFilter = ({ setFilter, filter }: Props) => { + +const RELATIVE_OPTIONS = [ + { label: "Minutes", unit: "m", values: [5, 10, 15, 30, 45] }, + { label: "Hours", unit: "h", values: [1, 2, 3, 6, 8, 12] }, + { label: "Days", unit: "d", values: [1, 2, 3, 4, 5, 6] }, + { label: "Weeks", unit: "w", values: [1, 2, 3, 4] } +]; + +export const LogsDateFilter = ({ setFilter, filter, timezone, setTimezone }: Props) => { const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false); const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false); const [isPopupOpen, setIsPopOpen] = useState(false); @@ -59,154 +70,244 @@ export const LogsDateFilter = ({ setFilter, filter }: Props) => { }; return ( - setIsPopOpen(el)}> -
- {filter.type === AuditLogDateFilterType.Relative ? ( - <> - {RELATIVE_VALUES.map((el) => ( - - ))} - - ) : ( - <> -
- {format(filter.startDate, "yyyy-MM-dd HH:mm")} -
-
- -
-
- {format(filter.endDate, "yyyy-MM-dd HH:mm")} -
- - )} - - - -
- -
- ( - - - - )} - /> - {selectType === AuditLogDateFilterType.Relative && ( + {el} + + ))} + + ) : ( +
+
{format(filter.startDate, "yyyy-MM-dd HH:mm")}
+
+ +
+
{format(filter.endDate, "yyyy-MM-dd HH:mm")}
+
+ )} + + + + + + ( - - - +
+ + +
)} /> - )} - {selectType === AuditLogDateFilterType.Absolute && ( -
+ {selectType === AuditLogDateFilterType.Relative && ( { + name="relativeModeValue" + render={({ field }) => { + const duration = field.value?.substring(0, field.value.length - 1); + const unitOfTime = field.value?.at(-1); return ( - - - +
+ {RELATIVE_OPTIONS.map(({ label, unit, values }) => ( +
+
{label}
+ {values.map((v) => { + const value = `${v}${unit}`; + return ( + + ); + })} +
+ ))} +
+ + field.onChange(`${val}${unitOfTime}`)} + max={60} + min={1} + /> + + + + +
+
); }} /> -
-
- + )} + {selectType === AuditLogDateFilterType.Absolute && ( +
+ { + return ( + + + + ); + }} + /> + + { + return ( + + + + ); + }} + />
- { - return ( - - - - ); - }} - /> + )} +
+
- )} -
- -
- - - + + + + + ); }; diff --git a/frontend/src/pages/organization/AuditLogsPage/components/LogsSection.tsx b/frontend/src/pages/organization/AuditLogsPage/components/LogsSection.tsx index 7f676121f..ea6bb0e84 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/LogsSection.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/LogsSection.tsx @@ -1,4 +1,6 @@ import { useEffect, useState } from "react"; +import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import ms from "ms"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; @@ -13,17 +15,19 @@ import { AuditLogDateFilterType, Presets, TAuditLogDateFilterFormData, - TAuditLogFilterFormData + TAuditLogFilterFormData, + Timezone } from "./types"; type Props = { presets?: Presets; refetchInterval?: number; showFilters?: boolean; + pageView?: boolean; }; export const LogsSection = withPermission( - ({ presets, refetchInterval, showFilters = true }: Props) => { + ({ presets, refetchInterval, showFilters = true, pageView = false }: Props) => { const { subscription } = useSubscription(); const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const); @@ -31,6 +35,8 @@ export const LogsSection = withPermission( eventType: presets?.eventType || [], actor: presets?.actorId }); + const [timezone, setTimezone] = useState(Timezone.Local); + const [dateFilter, setDateFilter] = useState({ startDate: new Date(Number(new Date()) - ms("1h")), endDate: new Date(), @@ -43,10 +49,85 @@ export const LogsSection = withPermission( handlePopUpOpen("upgradePlan"); } }, [subscription]); + + if (pageView) + return ( +
+
+
+
+

Audit History

+ +
+ + Docs + +
+
+
+
+
+ {showFilters && ( + + )} + {showFilters && ( + + )} +
+
+
+ + { + handlePopUpToggle("upgradePlan", isOpen); + }} + text="You can use audit logs if you switch to a paid Infisical plan." + /> +
+
+ ); + return (
- {showFilters && } + {showFilters && ( + + )} {showFilters && ( )} @@ -67,6 +148,7 @@ export const LogsSection = withPermission( environment: logFilter?.environment?.slug, actor: logFilter?.actor }} + timezone={timezone} /> { +export const LogsTable = ({ filter, refetchInterval, timezone }: Props) => { // Determine the project ID for filtering const filterProjectId = // Use the projectId from the filter if it exists @@ -57,16 +57,7 @@ export const LogsTable = ({ filter, refetchInterval }: Props) => { - - Timestamp - - - - + Timestamp Event @@ -79,6 +70,7 @@ export const LogsTable = ({ filter, refetchInterval }: Props) => { rowNumber={index + i * AUDIT_LOG_LIMIT + 1} auditLog={auditLog} key={`audit-log-${auditLog.id}`} + timezone={timezone} /> ))} @@ -96,7 +88,7 @@ export const LogsTable = ({ filter, refetchInterval }: Props) => { {!isEmpty && (