Fixed some details on audit logs page (#1156)
- Changed default port to 5005 due 5000 port is not available to use in new MacOS versions - Added an option to show full log information in a modal - Fixed issue with column selector dropdown & mui v5 - Fixed advanced filters table population - Changed date range selector picker for audit logs Signed-off-by: Benjamin Perez <benjamin@bexsoft.net> Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -61,7 +61,7 @@
|
||||
"websocket": "^1.0.31"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "PORT=5000 react-app-rewired start",
|
||||
"start": "PORT=5005 react-app-rewired start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
|
||||
@@ -31,7 +31,7 @@ interface IDateRangeSelector {
|
||||
setTimeStart: (date: any) => void;
|
||||
timeEnd: any;
|
||||
setTimeEnd: (date: any) => void;
|
||||
triggerSync: () => void;
|
||||
triggerSync?: () => void;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -97,16 +97,18 @@ const DateRangeSelector = ({
|
||||
noInputIcon
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={triggerSync}
|
||||
endIcon={<SyncIcon />}
|
||||
className={classes.syncButton}
|
||||
>
|
||||
Sync
|
||||
</Button>
|
||||
{triggerSync && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={triggerSync}
|
||||
endIcon={<SyncIcon />}
|
||||
className={classes.syncButton}
|
||||
>
|
||||
Sync
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -208,9 +208,6 @@ const styles = () =>
|
||||
right: 0,
|
||||
top: 0,
|
||||
},
|
||||
popoverContainer: {
|
||||
position: "relative",
|
||||
},
|
||||
popoverContent: {
|
||||
maxHeight: 250,
|
||||
overflowY: "auto",
|
||||
@@ -573,7 +570,6 @@ const TableWrapper = ({
|
||||
horizontal: "left",
|
||||
}}
|
||||
onClose={closeColumnSelector}
|
||||
className={classes.popoverContainer}
|
||||
>
|
||||
<div className={classes.shownColumnsLabel}>Shown Columns</div>
|
||||
<div className={classes.popoverContent}>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import get from "lodash/get";
|
||||
import { Button, Grid } from "@mui/material";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { IReqInfoSearchResults } from "./types";
|
||||
import { LogSearchColumnLabels } from "./utils";
|
||||
|
||||
interface ILogSearchFullModal {
|
||||
modalOpen: boolean;
|
||||
logSearchElement: IReqInfoSearchResults;
|
||||
onClose: () => void;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
pathLabel: {
|
||||
marginTop: 0,
|
||||
marginBottom: 32,
|
||||
},
|
||||
objectKeyCol: {
|
||||
fontWeight: 700,
|
||||
paddingRight: "10px",
|
||||
textAlign: "left",
|
||||
},
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
const LogSearchFullModal = ({
|
||||
modalOpen,
|
||||
logSearchElement,
|
||||
onClose,
|
||||
classes,
|
||||
}: ILogSearchFullModal) => {
|
||||
const jsonItems = Object.keys(logSearchElement);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ModalWrapper
|
||||
modalOpen={modalOpen}
|
||||
title="Full Log Information"
|
||||
onClose={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<table>
|
||||
<tbody>
|
||||
{jsonItems.map((objectKey: string, index: number) => (
|
||||
<tr key={`logSearch-${index.toString()}`}>
|
||||
<th className={classes.objectKeyCol}>
|
||||
{get(LogSearchColumnLabels, objectKey, `${objectKey}`)}
|
||||
</th>
|
||||
<td>{get(logSearchElement, objectKey, "")}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={onClose}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ModalWrapper>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(LogSearchFullModal);
|
||||
@@ -38,6 +38,9 @@ import api from "../../../../common/api";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import FilterInputWrapper from "../../Common/FormComponents/FilterInputWrapper/FilterInputWrapper";
|
||||
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
|
||||
import LogSearchFullModal from "./LogSearchFullModal";
|
||||
import { LogSearchColumnLabels } from "./utils";
|
||||
import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector";
|
||||
|
||||
interface ILogSearchProps {
|
||||
classes: any;
|
||||
@@ -154,6 +157,10 @@ const LogsSearchMain = ({
|
||||
]);
|
||||
const [nextPage, setNextPage] = useState<number>(0);
|
||||
const [alreadyFetching, setAlreadyFetching] = useState<boolean>(false);
|
||||
const [logSearchExtrasOpen, setLogSearchExtrasOpen] =
|
||||
useState<boolean>(false);
|
||||
const [selectedItem, setSelectedItem] =
|
||||
useState<IReqInfoSearchResults | null>(null);
|
||||
|
||||
let recordsResp: any = null;
|
||||
const logSearchEnabled = features && features.includes("log-search");
|
||||
@@ -188,11 +195,10 @@ const LogsSearchMain = ({
|
||||
)
|
||||
.then((res: ISearchResponse) => {
|
||||
const fetchedResults = res.results || [];
|
||||
const newResultSet = [...records, ...fetchedResults];
|
||||
|
||||
setLoading(false);
|
||||
setAlreadyFetching(false);
|
||||
setRecords(newResultSet);
|
||||
setRecords(fetchedResults);
|
||||
setNextPage(nextPage + 1);
|
||||
|
||||
if (recordsResp !== null) {
|
||||
@@ -218,7 +224,6 @@ const LogsSearchMain = ({
|
||||
sortOrder,
|
||||
timeStart,
|
||||
timeEnd,
|
||||
records,
|
||||
recordsResp,
|
||||
setErrorSnackMessage,
|
||||
]);
|
||||
@@ -262,27 +267,36 @@ const LogsSearchMain = ({
|
||||
});
|
||||
};
|
||||
|
||||
const openExtraInformation = (item: IReqInfoSearchResults) => {
|
||||
setSelectedItem(item);
|
||||
setLogSearchExtrasOpen(true);
|
||||
};
|
||||
|
||||
const closeViewExtraInformation = () => {
|
||||
setSelectedItem(null);
|
||||
setLogSearchExtrasOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{logSearchExtrasOpen && selectedItem !== null && (
|
||||
<LogSearchFullModal
|
||||
logSearchElement={selectedItem}
|
||||
modalOpen={logSearchExtrasOpen}
|
||||
onClose={closeViewExtraInformation}
|
||||
/>
|
||||
)}
|
||||
<Grid container className={classes.logsSubContainer}>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.actionsTray} ${classes.timeContainers}`}
|
||||
>
|
||||
<span className={classes.label}>Start Time</span>
|
||||
<DateTimePickerWrapper
|
||||
value={timeStart}
|
||||
onChange={setTimeStart}
|
||||
forSearchBlock
|
||||
id="stTime"
|
||||
/>
|
||||
<span className={classes.label}>End Time</span>
|
||||
<DateTimePickerWrapper
|
||||
value={timeEnd}
|
||||
onChange={setTimeEnd}
|
||||
forSearchBlock
|
||||
id="endTime"
|
||||
<DateRangeSelector
|
||||
setTimeEnd={setTimeEnd}
|
||||
setTimeStart={setTimeStart}
|
||||
timeEnd={timeEnd}
|
||||
timeStart={timeStart}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={`${classes.advancedLabelContainer}`}>
|
||||
@@ -376,15 +390,28 @@ const LogsSearchMain = ({
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
columns={[
|
||||
{ label: "Timestamp", elementKey: "time", enableSort: true },
|
||||
{ label: "API Name", elementKey: "api_name" },
|
||||
{ label: "Bucket", elementKey: "bucket" },
|
||||
{ label: "Object", elementKey: "object" },
|
||||
{ label: "Remote Host", elementKey: "remote_host" },
|
||||
{ label: "Request ID", elementKey: "request_id" },
|
||||
{ label: "User Agent", elementKey: "user_agent" },
|
||||
{
|
||||
label: "Response Status",
|
||||
label: LogSearchColumnLabels.time,
|
||||
elementKey: "time",
|
||||
enableSort: true,
|
||||
},
|
||||
{ label: LogSearchColumnLabels.api_name, elementKey: "api_name" },
|
||||
{ label: LogSearchColumnLabels.bucket, elementKey: "bucket" },
|
||||
{ label: LogSearchColumnLabels.object, elementKey: "object" },
|
||||
{
|
||||
label: LogSearchColumnLabels.remote_host,
|
||||
elementKey: "remote_host",
|
||||
},
|
||||
{
|
||||
label: LogSearchColumnLabels.request_id,
|
||||
elementKey: "request_id",
|
||||
},
|
||||
{
|
||||
label: LogSearchColumnLabels.user_agent,
|
||||
elementKey: "user_agent",
|
||||
},
|
||||
{
|
||||
label: LogSearchColumnLabels.response_status,
|
||||
elementKey: "response_status",
|
||||
renderFunction: (element) => (
|
||||
<Fragment>
|
||||
@@ -396,17 +423,17 @@ const LogsSearchMain = ({
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Request Content Length",
|
||||
label: LogSearchColumnLabels.request_content_length,
|
||||
elementKey: "request_content_length",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
label: "Response Content Length",
|
||||
label: LogSearchColumnLabels.response_content_length,
|
||||
elementKey: "response_content_length",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
label: "Time to Response NS",
|
||||
label: LogSearchColumnLabels.time_to_response_ns,
|
||||
elementKey: "time_to_response_ns",
|
||||
renderFunction: nsToSeconds,
|
||||
contentTextAlign: "right",
|
||||
@@ -432,6 +459,12 @@ const LogsSearchMain = ({
|
||||
recordsCount: 1000000,
|
||||
loadMoreRecords: loadMoreRecords,
|
||||
}}
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: openExtraInformation,
|
||||
},
|
||||
]}
|
||||
textSelectable
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
30
portal-ui/src/screens/Console/Logs/LogSearch/utils.ts
Normal file
30
portal-ui/src/screens/Console/Logs/LogSearch/utils.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export const LogSearchColumnLabels = {
|
||||
time: "Timestamp",
|
||||
api_name: "API Name",
|
||||
bucket: "Bucket",
|
||||
object: "Object",
|
||||
remote_host: "Remote Host",
|
||||
request_id: "Request ID",
|
||||
user_agent: "User Agent",
|
||||
response_status: "Response Status",
|
||||
response_status_code: "Response Status Code",
|
||||
request_content_length: "Request Content Length",
|
||||
response_content_length: "Response Content Length",
|
||||
time_to_response_ns: "Time to Response NS",
|
||||
};
|
||||
Reference in New Issue
Block a user