diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/InspectObject.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/InspectObject.tsx
new file mode 100644
index 000000000..e0c481090
--- /dev/null
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/InspectObject.tsx
@@ -0,0 +1,185 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2022 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 .
+
+import React, { useState } from "react";
+import { connect } from "react-redux";
+import withStyles from "@mui/styles/withStyles";
+import { setErrorSnackMessage } from "../../../../../../actions";
+import {decodeFileName, deleteCookie, getCookieValue, performDownload} from "../../../../../../common/utils";
+import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
+import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
+import {InspectMenuIcon} from "../../../../../../icons/SidebarMenus";
+import Button from "@mui/material/Button";
+import Grid from "@mui/material/Grid";
+import {Theme} from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import {formFieldStyles, modalStyleUtils, spacingUtils} from "../../../../Common/FormComponents/common/styleLibrary";
+import {PasswordKeyIcon} from "../../../../../../icons";
+import {Box, DialogContentText} from "@mui/material";
+import KeyRevealer from "../../../../Tools/KeyRevealer";
+
+const styles = (theme: Theme) =>
+ createStyles({
+ ...formFieldStyles,
+ ...modalStyleUtils,
+ ...spacingUtils,
+ });
+
+interface IInspectObjectProps {
+ classes: any;
+ closeInspectModalAndRefresh: (refresh: boolean) => void;
+ inspectOpen: boolean;
+ inspectPath: string;
+ volumeName: string;
+ setErrorSnackMessage: typeof setErrorSnackMessage;
+}
+
+const InspectObject = ({
+ classes,
+ closeInspectModalAndRefresh,
+ inspectOpen,
+ inspectPath,
+ volumeName,
+ setErrorSnackMessage,
+}: IInspectObjectProps) => {
+ const onClose = () => closeInspectModalAndRefresh(false);
+ const [isEncrypt, setIsEncrypt] = useState(true);
+ const [decryptionKey, setDecryptionKey] = useState("");
+ const [insFileName, setInsFileName] = useState("");
+
+ if (!inspectPath) {
+ return null;
+ }
+ const makeRequest = async (url: string) => {
+ return await fetch(url, { method: "GET" });
+ };
+
+ const performInspect = async () => {
+ const file = encodeURIComponent(inspectPath+"/xl.meta");
+ const volume = encodeURIComponent(volumeName);
+
+ const urlOfInspectApi = `/api/v1/admin/inspect?volume=${volume}&file=${file}&encrypt=${isEncrypt}`;
+
+ makeRequest(urlOfInspectApi)
+ .then(async (res) => {
+ if (!res.ok) {
+ const resErr: any = await res.json();
+
+ setErrorSnackMessage({
+ errorMessage: resErr.message,
+ detailedError: resErr.code,
+ });
+ }
+ const blob: Blob = await res.blob();
+
+ //@ts-ignore
+ const filename = res.headers.get("content-disposition").split('"')[1];
+ const decryptKey = getCookieValue(filename) || "";
+
+ performDownload(blob, filename);
+ setInsFileName(filename);
+ if (decryptKey === "") {
+ onClose();
+ return;
+ }
+ setDecryptionKey(decryptKey);
+ })
+ .catch((err) => {
+ setErrorSnackMessage(err);
+ });
+ };
+
+ const onCloseDecKeyModal = () => {
+ deleteCookie(insFileName);
+ onClose();
+ setDecryptionKey("");
+ };
+
+ const onSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ };
+
+ return (
+
+ {!decryptionKey && }
+ title={`Inspect Object`}
+ onClose={onClose}
+ >
+
+ }
+ {decryptionKey ? (
+ }
+ >
+
+
+ This will be displayed only once. It cannot be recovered.
+
+ Use secure medium to share this key.
+
+
+
+
+
+
+ ) : null}
+
+ );
+};
+
+const mapDispatchToProps = {
+ setErrorSnackMessage,
+};
+
+const connector = connect(null, mapDispatchToProps);
+
+export default withStyles(styles)(connector(InspectObject));
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
index b02de775a..f3a75ac12 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
@@ -59,6 +59,7 @@ import {
TagsIcon,
VersionsIcon,
} from "../../../../../../icons";
+import { InspectMenuIcon } from "../../../../../../icons/SidebarMenus";
import { ShareIcon, DownloadIcon, DeleteIcon } from "../../../../../../icons";
import api from "../../../../../../common/api";
import ShareFile from "../ObjectDetails/ShareFile";
@@ -75,6 +76,7 @@ import ObjectMetaData from "../ObjectDetails/ObjectMetaData";
import ActionsListSection from "./ActionsListSection";
import { displayFileIconName } from "./utils";
import TagsModal from "../ObjectDetails/TagsModal";
+import InspectObject from "./InspectObject";
const styles = () =>
createStyles({
@@ -186,6 +188,7 @@ const ObjectDetailPanel = ({
const [retentionModalOpen, setRetentionModalOpen] = useState(false);
const [tagModalOpen, setTagModalOpen] = useState(false);
const [legalholdOpen, setLegalholdOpen] = useState(false);
+ const [inspectModalOpen, setInspectModalOpen] = useState(false);
const [actualInfo, setActualInfo] = useState(null);
const [allInfoElements, setAllInfoElements] = useState([]);
const [objectToShare, setObjectToShare] = useState(null);
@@ -344,6 +347,13 @@ const ObjectDetailPanel = ({
}
};
+ const closeInspectModal = (reloadObjectData: boolean) => {
+ setInspectModalOpen(false);
+ if (reloadObjectData) {
+ setLoadObjectData(true);
+ }
+ };
+
const closeLegalholdModal = (reload: boolean) => {
setLegalholdOpen(false);
if (reload) {
@@ -439,6 +449,18 @@ const ObjectDetailPanel = ({
icon: ,
tooltip: "Change Tags for this File",
},
+ {
+ action: () => {
+ setInspectModalOpen(true);
+ },
+ label: "Inspect",
+ disabled:
+ !!actualInfo.is_delete_marker ||
+ extensionPreview(currentItem) === "none" ||
+ selectedVersion !== "",
+ icon: ,
+ tooltip: "Inspect this file",
+ },
{
action: () => {
setVersionsModeEnabled(!versionsMode, objectName);
@@ -448,6 +470,7 @@ const ObjectDetailPanel = ({
disabled: !(actualInfo.version_id && actualInfo.version_id !== "null"),
tooltip: "Display Versions for this file",
},
+
];
const calculateLastModifyTime = (lastModified: string) => {
@@ -527,6 +550,14 @@ const ObjectDetailPanel = ({
onCloseAndUpdate={closeAddTagModal}
/>
)}
+ {inspectModalOpen && actualInfo && (
+
+ )}
{!actualInfo && (
diff --git a/portal-ui/src/screens/Console/Tools/Inspect.tsx b/portal-ui/src/screens/Console/Tools/Inspect.tsx
index f43697ef5..ab743e76a 100644
--- a/portal-ui/src/screens/Console/Tools/Inspect.tsx
+++ b/portal-ui/src/screens/Console/Tools/Inspect.tsx
@@ -1,10 +1,26 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2022 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 .
+
import React, { Fragment, useEffect, useState } from "react";
import { Box, Button, DialogContentText } from "@mui/material";
import PageHeader from "../Common/PageHeader/PageHeader";
import PageLayout from "../Common/Layout/PageLayout";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
-import { CopyIcon, FileBookIcon, PasswordKeyIcon } from "../../../icons";
+import { FileBookIcon, PasswordKeyIcon } from "../../../icons";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
@@ -24,6 +40,7 @@ import {
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import { AppState } from "../../../store";
import { InspectMenuIcon } from "../../../icons/SidebarMenus";
+import KeyRevealer from "./KeyRevealer";
const styles = (theme: Theme) =>
createStyles({
@@ -34,48 +51,6 @@ const styles = (theme: Theme) =>
...modalStyleUtils,
});
-const KeyRevealer = ({ value }: { value: string }) => {
- const [shown, setShown] = React.useState(false);
-
- return (
-
- {}}
- value={value}
- overlayIcon={}
- extraInputProps={{
- readOnly: true,
- }}
- overlayAction={() => navigator.clipboard.writeText(value)}
- />
-
-
-
- );
-};
-
const mapState = (state: AppState) => ({
distributedSetup: state.system.distributedSetup,
});
diff --git a/portal-ui/src/screens/Console/Tools/KeyRevealer.tsx b/portal-ui/src/screens/Console/Tools/KeyRevealer.tsx
new file mode 100644
index 000000000..21574374c
--- /dev/null
+++ b/portal-ui/src/screens/Console/Tools/KeyRevealer.tsx
@@ -0,0 +1,63 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2022 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 .
+
+import { Box, Button } from "@mui/material";
+import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
+import { CopyIcon } from "../../../icons";
+import {useState} from "react";
+const KeyRevealer = ({ value }: { value: string }) => {
+ const [shown, setShown] = useState(false);
+
+ return (
+
+ {}}
+ value={value}
+ overlayIcon={}
+ extraInputProps={{
+ readOnly: true,
+ }}
+ overlayAction={() => navigator.clipboard.writeText(value)}
+ />
+
+
+
+ );
+};
+
+export default KeyRevealer;