Site replication support for different accesskey and secretkey in different sites (#2350)
This commit is contained in:
committed by
GitHub
parent
38015b4913
commit
ec3deed38e
@@ -59,6 +59,9 @@ type SessionResponse struct {
|
||||
// permissions
|
||||
Permissions map[string][]string `json:"permissions,omitempty"`
|
||||
|
||||
// server end point
|
||||
ServerEndPoint string `json:"serverEndPoint,omitempty"`
|
||||
|
||||
// status
|
||||
// Enum: [ok]
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
@@ -20,8 +20,7 @@ import { Box, LinearProgress } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button } from "mds";
|
||||
import useApi from "../../Common/Hooks/useApi";
|
||||
import { AddIcon, ClustersIcon, RemoveIcon } from "../../../../icons";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import { ClustersIcon } from "../../../../icons";
|
||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
import BackLink from "../../../../common/BackLink";
|
||||
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
|
||||
@@ -33,12 +32,10 @@ import {
|
||||
setSnackBarMessage,
|
||||
} from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
|
||||
type SiteInputRow = {
|
||||
name: string;
|
||||
endpoint: string;
|
||||
};
|
||||
import { useSelector } from "react-redux";
|
||||
import { selSession } from "../../consoleSlice";
|
||||
import SRSiteInputRow from "./SRSiteInputRow";
|
||||
import { SiteInputRow } from "./Types";
|
||||
|
||||
const isValidEndPoint = (ep: string) => {
|
||||
let isValidEndPointUrl = false;
|
||||
@@ -49,30 +46,74 @@ const isValidEndPoint = (ep: string) => {
|
||||
} catch (err) {
|
||||
isValidEndPointUrl = false;
|
||||
}
|
||||
if (isValidEndPointUrl || ep === "") {
|
||||
if (isValidEndPointUrl) {
|
||||
return "";
|
||||
} else {
|
||||
return "Invalid Endpoint";
|
||||
}
|
||||
};
|
||||
|
||||
const isEmptyValue = (value: string): boolean => {
|
||||
return value?.trim() === "";
|
||||
};
|
||||
|
||||
const TableHeader = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
marginLeft: "5px",
|
||||
}}
|
||||
>
|
||||
Site Name
|
||||
</Box>
|
||||
<Box sx={{ fontSize: "14px", marginLeft: "5px" }}>Endpoint {"*"}</Box>
|
||||
<Box sx={{ fontSize: "14px", marginLeft: "5px" }}>Access Key {"*"}</Box>
|
||||
<Box sx={{ fontSize: "14px", marginLeft: "5px" }}>Secret Key {"*"}</Box>
|
||||
<Box> </Box>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const SiteTypeHeader = ({ title }: { title: string }) => {
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Box
|
||||
sx={{
|
||||
marginBottom: "15px",
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const AddReplicationSites = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { serverEndPoint = "" } = useSelector(selSession);
|
||||
|
||||
const [currentSite, setCurrentSite] = useState<SiteInputRow[]>([
|
||||
{
|
||||
endpoint: serverEndPoint,
|
||||
name: "",
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
},
|
||||
]);
|
||||
|
||||
const [existingSites, setExistingSites] = useState<SiteInputRow[]>([]);
|
||||
|
||||
const [accessKey, setAccessKey] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
const [siteConfig, setSiteConfig] = useState<SiteInputRow[]>([]);
|
||||
|
||||
const setDefaultNewRows = () => {
|
||||
const defaultNewSites = existingSites?.length
|
||||
? [{ endpoint: "", name: "" }]
|
||||
: [
|
||||
{ endpoint: "", name: "" },
|
||||
{ endpoint: "", name: "" },
|
||||
];
|
||||
setSiteConfig(defaultNewSites);
|
||||
const defaultNewSites = [
|
||||
{ endpoint: "", name: "", accessKey: "", secretKey: "" },
|
||||
];
|
||||
setExistingSites(defaultNewSites);
|
||||
};
|
||||
|
||||
const [isSiteInfoLoading, invokeSiteInfoApi] = useApi(
|
||||
@@ -85,17 +126,34 @@ const AddReplicationSites = () => {
|
||||
curSite = {
|
||||
...curSite,
|
||||
isCurrent: true,
|
||||
isSaved: true,
|
||||
};
|
||||
siteList.splice(foundIdx, 1, curSite);
|
||||
|
||||
setCurrentSite([curSite]);
|
||||
siteList.splice(foundIdx, 1);
|
||||
}
|
||||
|
||||
siteList.sort((x: any, y: any) => {
|
||||
return x.name === curSiteName ? -1 : y.name === curSiteName ? 1 : 0;
|
||||
});
|
||||
setExistingSites(siteList);
|
||||
|
||||
let existingSiteList = siteList.map((si: any) => {
|
||||
return {
|
||||
...si,
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
isSaved: true,
|
||||
};
|
||||
});
|
||||
|
||||
if (existingSiteList.length) {
|
||||
setExistingSites(existingSiteList);
|
||||
} else {
|
||||
setDefaultNewRows();
|
||||
}
|
||||
},
|
||||
(err: any) => {
|
||||
setExistingSites([]);
|
||||
setDefaultNewRows();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -108,21 +166,36 @@ const AddReplicationSites = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setDefaultNewRows();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [existingSites]);
|
||||
|
||||
const isAllEndpointsValid =
|
||||
siteConfig.reduce((acc: string[], cv, i) => {
|
||||
const epValue = siteConfig[i].endpoint;
|
||||
const existingEndPointsValidity = existingSites.reduce(
|
||||
(acc: string[], cv, i) => {
|
||||
const epValue = existingSites[i].endpoint;
|
||||
const isEpValid = isValidEndPoint(epValue);
|
||||
|
||||
if (isEpValid === "" && epValue !== "") {
|
||||
acc.push(isEpValid);
|
||||
}
|
||||
return acc;
|
||||
}, []).length === siteConfig.length;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const isExistingCredsValidity = existingSites
|
||||
.map((site) => {
|
||||
return !isEmptyValue(site.accessKey) && !isEmptyValue(site.secretKey);
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const { accessKey: cAccessKey, secretKey: cSecretKey } = currentSite[0];
|
||||
|
||||
const isCurCredsValid =
|
||||
!isEmptyValue(cAccessKey) && !isEmptyValue(cSecretKey);
|
||||
const peerEndpointsValid =
|
||||
existingEndPointsValidity.length === existingSites.length;
|
||||
const peerCredsValid =
|
||||
isExistingCredsValidity.length === existingSites.length;
|
||||
|
||||
let isAllFieldsValid =
|
||||
isCurCredsValid && peerEndpointsValid && peerCredsValid;
|
||||
|
||||
const [isAdding, invokeSiteAddApi] = useApi(
|
||||
(res: any) => {
|
||||
@@ -146,37 +219,181 @@ const AddReplicationSites = () => {
|
||||
);
|
||||
|
||||
const resetForm = () => {
|
||||
setAccessKey("");
|
||||
setSecretKey("");
|
||||
setDefaultNewRows();
|
||||
setCurrentSite((prevItems) => {
|
||||
return prevItems.map((item, ix) => ({
|
||||
...item,
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
name: "",
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
const addSiteReplication = () => {
|
||||
const existingSitesToAdd = existingSites?.map((es, idx) => {
|
||||
const curSite: any[] = currentSite?.map((es, idx) => {
|
||||
return {
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
accessKey: es.accessKey,
|
||||
secretKey: es.secretKey,
|
||||
name: es.name,
|
||||
endpoint: es.endpoint,
|
||||
};
|
||||
});
|
||||
|
||||
const newSitesToAdd = siteConfig.reduce((acc: any, ns, idx) => {
|
||||
if (ns.endpoint) {
|
||||
acc.push({
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
name: ns.name || `dr-site-${idx}`,
|
||||
endpoint: ns.endpoint,
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
const newOrExistingSitesToAdd = existingSites.reduce(
|
||||
(acc: any, ns, idx) => {
|
||||
if (ns.endpoint) {
|
||||
acc.push({
|
||||
accessKey: ns.accessKey,
|
||||
secretKey: ns.secretKey,
|
||||
name: ns.name || `dr-site-${idx}`,
|
||||
endpoint: ns.endpoint,
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
invokeSiteAddApi("POST", `api/v1/admin/site-replication`, [
|
||||
...(existingSitesToAdd || []),
|
||||
...(newSitesToAdd || []),
|
||||
]);
|
||||
const sitesToAdd = curSite.concat(newOrExistingSitesToAdd);
|
||||
|
||||
invokeSiteAddApi("POST", `api/v1/admin/site-replication`, sitesToAdd);
|
||||
};
|
||||
|
||||
const renderCurrentSite = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
>
|
||||
<SiteTypeHeader title={"This Site"} />
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: ".8fr 1.2fr .8fr .8fr .2fr",
|
||||
border: "1px solid #eaeaea",
|
||||
padding: "15px",
|
||||
gap: "10px",
|
||||
maxHeight: "430px",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<TableHeader />
|
||||
|
||||
{currentSite.map((cs, index) => {
|
||||
const accessKeyError = isEmptyValue(cs.accessKey)
|
||||
? "AccessKey is required"
|
||||
: "";
|
||||
const secretKeyError = isEmptyValue(cs.secretKey)
|
||||
? "SecretKey is required"
|
||||
: "";
|
||||
return (
|
||||
<SRSiteInputRow
|
||||
key={`current-${index}`}
|
||||
disabledFields={["endpoint"].concat(cs.isSaved ? "name" : "")}
|
||||
rowData={cs}
|
||||
rowId={index}
|
||||
fieldErrors={{
|
||||
accessKey: accessKeyError,
|
||||
secretKey: secretKeyError,
|
||||
}}
|
||||
onFieldChange={(e, fieldName, index) => {
|
||||
const filedValue = e.target.value;
|
||||
if (fieldName !== "") {
|
||||
setCurrentSite((prevItems) => {
|
||||
return prevItems.map((item, ix) =>
|
||||
ix === index
|
||||
? { ...item, [fieldName]: filedValue }
|
||||
: item
|
||||
);
|
||||
});
|
||||
}
|
||||
}}
|
||||
showRowActions={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const renderPeerSites = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: "25px",
|
||||
}}
|
||||
>
|
||||
<SiteTypeHeader title={"Peer Sites"} />
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: ".8fr 1.2fr .8fr .8fr .2fr",
|
||||
border: "1px solid #eaeaea",
|
||||
padding: "15px",
|
||||
gap: "10px",
|
||||
maxHeight: "430px",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<TableHeader />
|
||||
|
||||
{existingSites.map((ps, index) => {
|
||||
const endPointError = isValidEndPoint(ps.endpoint);
|
||||
|
||||
const accessKeyError = isEmptyValue(ps.accessKey)
|
||||
? "AccessKey is required"
|
||||
: "";
|
||||
const secretKeyError = isEmptyValue(ps.secretKey)
|
||||
? "SecretKey is required"
|
||||
: "";
|
||||
|
||||
return (
|
||||
<SRSiteInputRow
|
||||
key={`exiting-${index}`}
|
||||
rowData={ps}
|
||||
rowId={index}
|
||||
fieldErrors={{
|
||||
endpoint: endPointError,
|
||||
accessKey: accessKeyError,
|
||||
secretKey: secretKeyError,
|
||||
}}
|
||||
disabledFields={ps.isSaved ? ["endpoint", "name"] : []}
|
||||
onFieldChange={(e, fieldName, index) => {
|
||||
const filedValue = e.target.value;
|
||||
setExistingSites((prevItems) => {
|
||||
return prevItems.map((item, ix) =>
|
||||
ix === index ? { ...item, [fieldName]: filedValue } : item
|
||||
);
|
||||
});
|
||||
}}
|
||||
canAdd={true}
|
||||
canRemove={index > 0 && !ps.isSaved}
|
||||
onAddClick={() => {
|
||||
const newRows = [...existingSites];
|
||||
//add at the next index
|
||||
newRows.splice(index + 1, 0, {
|
||||
name: "",
|
||||
endpoint: "",
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
});
|
||||
|
||||
setExistingSites(newRows);
|
||||
}}
|
||||
onRemoveClick={(index) => {
|
||||
setExistingSites(
|
||||
existingSites.filter((_, idx) => idx !== index)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -195,10 +412,7 @@ const AddReplicationSites = () => {
|
||||
display: "grid",
|
||||
padding: "25px",
|
||||
gap: "25px",
|
||||
gridTemplateColumns: {
|
||||
md: "2fr 1.2fr",
|
||||
xs: "1fr",
|
||||
},
|
||||
gridTemplateColumns: "1fr",
|
||||
border: "1px solid #eaeaea",
|
||||
}}
|
||||
>
|
||||
@@ -208,6 +422,18 @@ const AddReplicationSites = () => {
|
||||
</SectionTitle>
|
||||
|
||||
{isSiteInfoLoading || isAdding ? <LinearProgress /> : null}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
fontStyle: "italic",
|
||||
marginTop: "10px",
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
>
|
||||
Note: AccessKey and SecretKey values for every site is required
|
||||
while adding or editing peer sites
|
||||
</Box>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
@@ -216,254 +442,9 @@ const AddReplicationSites = () => {
|
||||
return addSiteReplication();
|
||||
}}
|
||||
>
|
||||
<Grid item xs={12} marginBottom={"15px"}>
|
||||
<Box
|
||||
sx={{
|
||||
fontStyle: "italic",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontSize: "12px",
|
||||
marginTop: 2,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ fontWeight: 600 }}>Note:</Box>{" "}
|
||||
<Box sx={{ marginLeft: 1 }}>
|
||||
Access Key and Secret Key should be same on all sites.
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
marginBottom={"15px"}
|
||||
sx={{
|
||||
"& label span": {
|
||||
fontWeight: "normal",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<InputBoxWrapper
|
||||
id="add-rep-peer-accKey"
|
||||
name="add-rep-peer-accKey"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccessKey(event.target.value);
|
||||
}}
|
||||
label="Access Key"
|
||||
required={true}
|
||||
value={accessKey}
|
||||
error={accessKey === "" ? "Access Key is required." : ""}
|
||||
data-test-id={"add-site-rep-acc-key"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
marginBottom={"30px"}
|
||||
sx={{
|
||||
"& label span": {
|
||||
fontWeight: "normal",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<InputBoxWrapper
|
||||
id="add-rep-peer-secKey"
|
||||
name="add-rep-peer-secKey"
|
||||
type={"password"}
|
||||
required={true}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSecretKey(event.target.value);
|
||||
}}
|
||||
error={secretKey === "" ? "Secret Key is required." : ""}
|
||||
label="Secret Key"
|
||||
value={secretKey}
|
||||
data-test-id={"add-site-rep-sec-key"}
|
||||
/>
|
||||
</Grid>
|
||||
{renderCurrentSite()}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box
|
||||
sx={{
|
||||
marginBottom: "15px",
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Peer Sites
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: ".8fr 1.2fr .2fr",
|
||||
border: "1px solid #eaeaea",
|
||||
padding: "15px",
|
||||
gap: "10px",
|
||||
maxHeight: "430px",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
marginLeft: "5px",
|
||||
}}
|
||||
>
|
||||
Site Name
|
||||
</Box>
|
||||
<Box sx={{ fontSize: "14px", marginLeft: "5px" }}>
|
||||
Endpoint {"*"}
|
||||
</Box>
|
||||
<Box> </Box>
|
||||
{existingSites?.map((si, index) => {
|
||||
return (
|
||||
<Fragment key={si.name}>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-ex-peer-site-${index}`}
|
||||
name={`add-rep-ex-peer-site-${index}`}
|
||||
extraInputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
label=""
|
||||
value={si.name}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-ex-peer-site-ep-${index}`}
|
||||
name={`add-rep-ex-peer-site-ep-${index}`}
|
||||
extraInputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
label=""
|
||||
value={si.endpoint}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</Box>
|
||||
<Grid item xs={12}>
|
||||
{" "}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
{siteConfig.map((sci, index) => {
|
||||
let isDelDisabled = false;
|
||||
|
||||
if (existingSites?.length && index === 0) {
|
||||
isDelDisabled = true;
|
||||
} else if (!existingSites?.length && index < 2) {
|
||||
isDelDisabled = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment key={`${index}`}>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-peer-site-${index}`}
|
||||
name={`add-rep-peer-site-${index}`}
|
||||
placeholder={`dr-site-${index}`}
|
||||
label=""
|
||||
value={`${sci.name}`}
|
||||
onChange={(e) => {
|
||||
const nameTxt = e.target.value;
|
||||
setSiteConfig((prevItems) => {
|
||||
return prevItems.map((item, ix) =>
|
||||
ix === index ? { ...item, name: nameTxt } : item
|
||||
);
|
||||
});
|
||||
}}
|
||||
data-test-id={`add-site-rep-peer-site-${index}`}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-peer-site-ep-${index}`}
|
||||
name={`add-rep-peer-site-ep-${index}`}
|
||||
placeholder={`https://dr.minio-storage:900${index}`}
|
||||
label=""
|
||||
error={isValidEndPoint(siteConfig[index].endpoint)}
|
||||
value={`${sci.endpoint}`}
|
||||
onChange={(e) => {
|
||||
const epTxt = e.target.value;
|
||||
setSiteConfig((prevItems) => {
|
||||
return prevItems.map((item, ix) =>
|
||||
ix === index
|
||||
? { ...item, endpoint: epTxt }
|
||||
: item
|
||||
);
|
||||
});
|
||||
}}
|
||||
data-test-id={`add-site-rep-peer-ep-${index}`}
|
||||
/>
|
||||
</Box>
|
||||
<Grid item xs={12} alignItems={"center"} display={"flex"}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignSelf: "baseline",
|
||||
marginTop: "4px",
|
||||
|
||||
"& button": {
|
||||
borderColor: "#696969",
|
||||
color: "#696969",
|
||||
borderRadius: "50%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TooltipWrapper tooltip={"Add a Row"}>
|
||||
<Button
|
||||
id={`add-row-${index}`}
|
||||
variant="regular"
|
||||
icon={<AddIcon />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
const newRows = [...siteConfig];
|
||||
//add at the next index
|
||||
newRows.splice(index + 1, 0, {
|
||||
name: "",
|
||||
endpoint: "",
|
||||
});
|
||||
|
||||
setSiteConfig(newRows);
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
padding: 0,
|
||||
}}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
<TooltipWrapper tooltip={"Remove Row"}>
|
||||
<Button
|
||||
id={`remove-row-${index}`}
|
||||
variant="regular"
|
||||
disabled={isDelDisabled}
|
||||
icon={<RemoveIcon />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setSiteConfig(
|
||||
siteConfig.filter((_, idx) => idx !== index)
|
||||
);
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
padding: 0,
|
||||
marginLeft: 8,
|
||||
}}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
{renderPeerSites()}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box
|
||||
@@ -488,12 +469,7 @@ const AddReplicationSites = () => {
|
||||
id={"save"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
disabled={
|
||||
isAdding ||
|
||||
!accessKey ||
|
||||
!secretKey ||
|
||||
!isAllEndpointsValid
|
||||
}
|
||||
disabled={isAdding || !isAllFieldsValid}
|
||||
label={"Save"}
|
||||
/>
|
||||
</Box>
|
||||
@@ -557,11 +533,12 @@ const AddReplicationSites = () => {
|
||||
borderRadius: "50%",
|
||||
},
|
||||
|
||||
"& .step-row": {
|
||||
"& li": {
|
||||
fontSize: "14px",
|
||||
display: "flex",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px",
|
||||
width: "100%",
|
||||
|
||||
"&.step-text": {
|
||||
fontWeight: 400,
|
||||
@@ -593,21 +570,52 @@ const AddReplicationSites = () => {
|
||||
owned by the root user)
|
||||
</li>
|
||||
<li>
|
||||
Changes to Bucket features such as
|
||||
<ul>
|
||||
<li>Bucket Policies</li>
|
||||
<li>Bucket Tags</li>
|
||||
<li>Bucket Object-Lock configurations</li>
|
||||
<li>Bucket Encryption configuration</li>
|
||||
</ul>
|
||||
<Box
|
||||
style={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
paddingTop: "1px",
|
||||
}}
|
||||
>
|
||||
Changes to Bucket features such as
|
||||
</div>
|
||||
<ul>
|
||||
<li>Bucket Policies</li>
|
||||
<li>Bucket Tags</li>
|
||||
<li>Bucket Object-Lock configurations</li>
|
||||
<li>Bucket Encryption configuration</li>
|
||||
</ul>
|
||||
</Box>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The following Bucket features will NOT be replicated
|
||||
<ul>
|
||||
<li>Bucket notification configuration</li>
|
||||
<li>Bucket lifecycle (ILM) configuration</li>
|
||||
</ul>
|
||||
<Box
|
||||
style={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
paddingTop: "1px",
|
||||
}}
|
||||
>
|
||||
The following Bucket features will NOT be replicated
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li>Bucket notification configuration</li>
|
||||
<li>Bucket lifecycle (ILM) configuration</li>
|
||||
</ul>
|
||||
</Box>
|
||||
</li>
|
||||
</ul>
|
||||
</Box>
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { EditIcon } from "../../../../icons";
|
||||
import { Box, DialogContentText } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import { Button } from "mds";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import useApi from "../../Common/Hooks/useApi";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
setSnackBarMessage,
|
||||
} from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import {
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
spacingUtils,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...modalStyleUtils,
|
||||
...formFieldStyles,
|
||||
...spacingUtils,
|
||||
});
|
||||
const EditSiteEndPoint = ({
|
||||
editSite = {},
|
||||
onClose,
|
||||
onComplete,
|
||||
classes = {},
|
||||
}: {
|
||||
editSite: any;
|
||||
onClose: () => void;
|
||||
onComplete: () => void;
|
||||
classes: any;
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [editEndPointName, setEditEndPointName] = useState<string>("");
|
||||
|
||||
const [isEditing, invokeSiteEditApi] = useApi(
|
||||
(res: any) => {
|
||||
if (res.success) {
|
||||
dispatch(setSnackBarMessage(res.status));
|
||||
} else {
|
||||
dispatch(
|
||||
setErrorSnackMessage({
|
||||
errorMessage: "Error",
|
||||
detailedError: res.status,
|
||||
})
|
||||
);
|
||||
}
|
||||
onComplete();
|
||||
},
|
||||
(err: any) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
onComplete();
|
||||
}
|
||||
);
|
||||
|
||||
const updatePeerSite = () => {
|
||||
invokeSiteEditApi("PUT", `api/v1/admin/site-replication`, {
|
||||
endpoint: editEndPointName,
|
||||
name: editSite.name,
|
||||
deploymentId: editSite.deploymentID, // readonly
|
||||
});
|
||||
};
|
||||
|
||||
let isValidEndPointUrl = false;
|
||||
|
||||
try {
|
||||
new URL(editEndPointName);
|
||||
isValidEndPointUrl = true;
|
||||
} catch (err) {
|
||||
isValidEndPointUrl = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
title={`Edit Replication Endpoint `}
|
||||
modalOpen={true}
|
||||
titleIcon={<EditIcon />}
|
||||
onClose={onClose}
|
||||
>
|
||||
<DialogContentText>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ marginBottom: "10px" }}>
|
||||
<strong>Site:</strong> {" "}
|
||||
{editSite.name}
|
||||
</Box>
|
||||
<Box sx={{ marginBottom: "10px" }}>
|
||||
<strong>Current Endpoint:</strong> {" "}
|
||||
{editSite.endpoint}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ marginBottom: "5px" }}> New Endpoint:</Box>
|
||||
<InputBoxWrapper
|
||||
id="edit-rep-peer-endpoint"
|
||||
name="edit-rep-peer-endpoint"
|
||||
placeholder={"https://dr.minio-storage:9000"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEditEndPointName(event.target.value);
|
||||
}}
|
||||
label=""
|
||||
value={editEndPointName}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} marginBottom={"15px"}>
|
||||
<Box
|
||||
sx={{
|
||||
fontStyle: "italic",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontSize: "12px",
|
||||
marginTop: 2,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ fontWeight: 600 }}>Note:</Box>{" "}
|
||||
<Box sx={{ marginLeft: 1, color: "red" }}>
|
||||
Access Key and Secret Key should be same on the new site/endpoint.
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</DialogContentText>
|
||||
|
||||
<Grid item xs={12} className={classes.modalButtonBar}>
|
||||
<Button
|
||||
id={"close"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
onClick={onClose}
|
||||
label={"Close"}
|
||||
/>
|
||||
<Button
|
||||
id={"update"}
|
||||
type="button"
|
||||
variant="callAction"
|
||||
disabled={isEditing || !isValidEndPointUrl}
|
||||
onClick={updatePeerSite}
|
||||
label={"Update"}
|
||||
/>
|
||||
</Grid>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
export default withStyles(styles)(EditSiteEndPoint);
|
||||
@@ -23,11 +23,6 @@ import { ReplicationSite } from "./SiteReplication";
|
||||
import TrashIcon from "../../../../icons/TrashIcon";
|
||||
import { CircleIcon, ConfirmDeleteIcon, EditIcon } from "../../../../icons";
|
||||
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import useApi from "../../Common/Hooks/useApi";
|
||||
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
@@ -36,12 +31,8 @@ import {
|
||||
modalStyleUtils,
|
||||
spacingUtils,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
setSnackBarMessage,
|
||||
} from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
import EditSiteEndPoint from "./EditSiteEndPoint";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -54,54 +45,14 @@ const ReplicationSites = ({
|
||||
sites,
|
||||
onDeleteSite,
|
||||
onRefresh,
|
||||
classes,
|
||||
}: {
|
||||
sites: ReplicationSite[];
|
||||
onDeleteSite: (isAll: boolean, sites: string[]) => void;
|
||||
onRefresh: () => void;
|
||||
classes: any;
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [deleteSiteKey, setIsDeleteSiteKey] = useState<string>("");
|
||||
const [editSite, setEditSite] = useState<any>(null);
|
||||
const [editEndPointName, setEditEndPointName] = useState<string>("");
|
||||
|
||||
const [isEditing, invokeSiteEditApi] = useApi(
|
||||
(res: any) => {
|
||||
if (res.success) {
|
||||
setEditSite(null);
|
||||
dispatch(setSnackBarMessage(res.status));
|
||||
} else {
|
||||
dispatch(
|
||||
setErrorSnackMessage({
|
||||
errorMessage: "Error",
|
||||
detailedError: res.status,
|
||||
})
|
||||
);
|
||||
}
|
||||
onRefresh();
|
||||
},
|
||||
(err: any) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
onRefresh();
|
||||
}
|
||||
);
|
||||
const updatePeerSite = () => {
|
||||
invokeSiteEditApi("PUT", `api/v1/admin/site-replication`, {
|
||||
endpoint: editEndPointName,
|
||||
name: editSite.name,
|
||||
deploymentId: editSite.deploymentID, // readonly
|
||||
});
|
||||
};
|
||||
|
||||
let isValidEndPointUrl = false;
|
||||
|
||||
try {
|
||||
new URL(editEndPointName);
|
||||
isValidEndPointUrl = true;
|
||||
} catch (err) {
|
||||
isValidEndPointUrl = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -229,17 +180,11 @@ const ReplicationSites = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TooltipWrapper
|
||||
tooltip={
|
||||
sites.length <= 2
|
||||
? "Minimum two sites are required for replication"
|
||||
: "Delete Site"
|
||||
}
|
||||
>
|
||||
<TooltipWrapper tooltip="Delete Site">
|
||||
<Button
|
||||
id={`delete-site-${key}-${index}`}
|
||||
variant="secondary"
|
||||
disabled={sites.length <= 2}
|
||||
disabled={siteInfo.isCurrent}
|
||||
icon={<TrashIcon />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -256,6 +201,7 @@ const ReplicationSites = ({
|
||||
<Button
|
||||
id={`edit-icon-${key}-${index}`}
|
||||
variant="regular"
|
||||
disabled={siteInfo.isCurrent}
|
||||
icon={<EditIcon />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -295,71 +241,16 @@ const ReplicationSites = ({
|
||||
) : null}
|
||||
|
||||
{editSite?.name === key ? (
|
||||
<ModalWrapper
|
||||
title={`Edit Replication Endpoint `}
|
||||
modalOpen={true}
|
||||
titleIcon={<EditIcon />}
|
||||
<EditSiteEndPoint
|
||||
onComplete={() => {
|
||||
setEditSite(null);
|
||||
onRefresh();
|
||||
}}
|
||||
editSite={editSite}
|
||||
onClose={() => {
|
||||
setEditSite(null);
|
||||
}}
|
||||
>
|
||||
<DialogContentText>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ marginBottom: "10px" }}>
|
||||
<strong>Site:</strong> {" "}
|
||||
{editSite.name}
|
||||
</Box>
|
||||
<Box sx={{ marginBottom: "10px" }}>
|
||||
<strong>Current Endpoint:</strong> {" "}
|
||||
{editSite.endpoint}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ marginBottom: "5px" }}> New Endpoint:</Box>
|
||||
<InputBoxWrapper
|
||||
id="edit-rep-peer-endpoint"
|
||||
name="edit-rep-peer-endpoint"
|
||||
placeholder={"https://dr.minio-storage:9000"}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setEditEndPointName(event.target.value);
|
||||
}}
|
||||
label=""
|
||||
value={editEndPointName}
|
||||
/>
|
||||
</Grid>
|
||||
</DialogContentText>
|
||||
|
||||
<Grid item xs={12} className={classes.modalButtonBar}>
|
||||
<Button
|
||||
id={"close"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
onClick={() => {
|
||||
setEditSite(null);
|
||||
}}
|
||||
label={"Close"}
|
||||
/>
|
||||
<Button
|
||||
id={"update"}
|
||||
type="button"
|
||||
variant="callAction"
|
||||
disabled={isEditing || !isValidEndPointUrl}
|
||||
onClick={() => {
|
||||
updatePeerSite();
|
||||
}}
|
||||
label={"Update"}
|
||||
/>
|
||||
</Grid>
|
||||
</ModalWrapper>
|
||||
/>
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
import { Button } from "mds";
|
||||
import { AddIcon, RemoveIcon } from "../../../../icons";
|
||||
import { SiteInputRow } from "./Types";
|
||||
|
||||
const SRSiteInputRow = ({
|
||||
rowData,
|
||||
rowId: index,
|
||||
onFieldChange,
|
||||
onAddClick,
|
||||
onRemoveClick,
|
||||
canAdd = true,
|
||||
canRemove = true,
|
||||
showRowActions = true,
|
||||
disabledFields = [],
|
||||
fieldErrors = {},
|
||||
}: {
|
||||
rowData: SiteInputRow;
|
||||
rowId: number;
|
||||
onFieldChange: (e: any, fieldName: string, index: number) => void;
|
||||
onAddClick?: (index: number) => void;
|
||||
onRemoveClick?: (index: number) => void;
|
||||
canAdd?: boolean;
|
||||
canRemove?: boolean;
|
||||
showRowActions?: boolean;
|
||||
disabledFields?: string[];
|
||||
fieldErrors?: Record<string, string>;
|
||||
}) => {
|
||||
const { endpoint = "", accessKey = "", secretKey = "", name = "" } = rowData;
|
||||
return (
|
||||
<Fragment key={`${index}`}>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-peer-site-${index}`}
|
||||
name={`add-rep-peer-site-${index}`}
|
||||
placeholder={`site-name`}
|
||||
label=""
|
||||
extraInputProps={{
|
||||
readOnly: disabledFields.includes("name"),
|
||||
}}
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
onFieldChange(e, "name", index);
|
||||
}}
|
||||
data-test-id={`add-site-rep-peer-site-${index}`}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-peer-site-ep-${index}`}
|
||||
name={`add-rep-peer-site-ep-${index}`}
|
||||
placeholder={`https://dr.minio-storage:900${index}`}
|
||||
label=""
|
||||
extraInputProps={{
|
||||
readOnly: disabledFields.includes("endpoint"),
|
||||
}}
|
||||
error={fieldErrors["endpoint"]}
|
||||
value={endpoint}
|
||||
onChange={(e) => {
|
||||
onFieldChange(e, "endpoint", index);
|
||||
}}
|
||||
data-test-id={`add-site-rep-peer-ep-${index}`}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-peer-site-ac-${index}`}
|
||||
name={`add-rep-peer-site-ac-${index}`}
|
||||
label=""
|
||||
required={true}
|
||||
disabled={disabledFields.includes("accessKey")}
|
||||
value={accessKey}
|
||||
error={fieldErrors["accessKey"]}
|
||||
onChange={(e) => {
|
||||
onFieldChange(e, "accessKey", index);
|
||||
}}
|
||||
data-test-id={`add-rep-peer-site-ac-${index}`}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<InputBoxWrapper
|
||||
id={`add-rep-peer-site-sk-${index}`}
|
||||
name={`add-rep-peer-site-sk-${index}`}
|
||||
label=""
|
||||
required={true}
|
||||
type={"password"}
|
||||
value={secretKey}
|
||||
error={fieldErrors["secretKey"]}
|
||||
disabled={disabledFields.includes("secretKey")}
|
||||
onChange={(e) => {
|
||||
onFieldChange(e, "secretKey", index);
|
||||
}}
|
||||
data-test-id={`add-rep-peer-site-sk-${index}`}
|
||||
/>
|
||||
</Box>
|
||||
<Grid item xs={12} alignItems={"center"} display={"flex"}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignSelf: "baseline",
|
||||
marginTop: "4px",
|
||||
|
||||
"& button": {
|
||||
borderColor: "#696969",
|
||||
color: "#696969",
|
||||
borderRadius: "50%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{showRowActions ? (
|
||||
<React.Fragment>
|
||||
<TooltipWrapper tooltip={"Add a Row"}>
|
||||
<Button
|
||||
id={`add-row-${index}`}
|
||||
variant="regular"
|
||||
disabled={!canAdd}
|
||||
icon={<AddIcon />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onAddClick?.(index);
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
padding: 0,
|
||||
}}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
<TooltipWrapper tooltip={"Remove Row"}>
|
||||
<Button
|
||||
id={`remove-row-${index}`}
|
||||
variant="regular"
|
||||
disabled={!canRemove}
|
||||
icon={<RemoveIcon />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onRemoveClick?.(index);
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
padding: 0,
|
||||
marginLeft: 8,
|
||||
}}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
</Box>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default SRSiteInputRow;
|
||||
@@ -0,0 +1,8 @@
|
||||
export type SiteInputRow = {
|
||||
name: string;
|
||||
endpoint: string;
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
isCurrent?: boolean;
|
||||
isSaved?: boolean;
|
||||
};
|
||||
@@ -32,6 +32,7 @@ const initialState: ConsoleState = {
|
||||
allowResources: null,
|
||||
customStyles: null,
|
||||
envConstants: null,
|
||||
serverEndPoint: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface ISessionResponse {
|
||||
allowResources: IAllowResources[] | null;
|
||||
customStyles?: string | null;
|
||||
envConstants?: IEnvironmentContants | null;
|
||||
serverEndPoint?: string | undefined;
|
||||
}
|
||||
|
||||
export interface ButtonProps {
|
||||
|
||||
@@ -7289,6 +7289,9 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serverEndPoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -15542,6 +15545,9 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serverEndPoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
@@ -254,6 +254,7 @@ func getSessionResponse(ctx context.Context, session *models.Principal) (*models
|
||||
AllowResources: allowResources,
|
||||
CustomStyles: customStyles,
|
||||
EnvConstants: &envConstants,
|
||||
ServerEndPoint: getMinIOServer(),
|
||||
}
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
@@ -4143,6 +4143,8 @@ definitions:
|
||||
type: boolean
|
||||
distributedMode:
|
||||
type: boolean
|
||||
serverEndPoint:
|
||||
type: string
|
||||
permissions:
|
||||
type: object
|
||||
additionalProperties:
|
||||
|
||||
Reference in New Issue
Block a user