github.com/minio/console@v1.4.1/web-app/src/screens/Console/Configurations/SiteReplication/SiteReplication.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2022 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 import React, { Fragment, useEffect, useState } from "react"; 18 import { useNavigate } from "react-router-dom"; 19 import { 20 ActionLink, 21 AddIcon, 22 Box, 23 Button, 24 ClustersIcon, 25 ConfirmDeleteIcon, 26 Grid, 27 HelpBox, 28 Loader, 29 PageLayout, 30 RecoverIcon, 31 SectionTitle, 32 TrashIcon, 33 } from "mds"; 34 import { ErrorResponseHandler } from "../../../../common/types"; 35 import { IAM_PAGES } from "../../../../common/SecureComponent/permissions"; 36 import { 37 setErrorSnackMessage, 38 setHelpName, 39 setSnackBarMessage, 40 } from "../../../../systemSlice"; 41 import { useAppDispatch } from "../../../../store"; 42 import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog"; 43 import useApi from "../../Common/Hooks/useApi"; 44 import ReplicationSites from "./ReplicationSites"; 45 import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; 46 import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper"; 47 import HelpMenu from "../../HelpMenu"; 48 49 export type ReplicationSite = { 50 deploymentID: string; 51 endpoint: string; 52 name: string; 53 isCurrent?: boolean; 54 }; 55 56 const SiteReplication = () => { 57 const dispatch = useAppDispatch(); 58 const navigate = useNavigate(); 59 60 const [sites, setSites] = useState([]); 61 62 const [deleteAll, setIsDeleteAll] = useState(false); 63 const [isSiteInfoLoading, invokeSiteInfoApi] = useApi( 64 (res: any) => { 65 const { sites: siteList, name: curSiteName } = res; 66 // current site name to be the fist one. 67 const foundIdx = siteList.findIndex((el: any) => el.name === curSiteName); 68 if (foundIdx !== -1) { 69 let curSite = siteList[foundIdx]; 70 curSite = { 71 ...curSite, 72 isCurrent: true, 73 }; 74 siteList.splice(foundIdx, 1, curSite); 75 } 76 77 siteList.sort((x: any, y: any) => { 78 return x.name === curSiteName ? -1 : y.name === curSiteName ? 1 : 0; 79 }); 80 setSites(siteList); 81 }, 82 (err: any) => { 83 setSites([]); 84 }, 85 ); 86 87 const getSites = () => { 88 invokeSiteInfoApi("GET", `api/v1/admin/site-replication`); 89 }; 90 91 const [isRemoving, invokeSiteRemoveApi] = useApi( 92 (res: any) => { 93 setIsDeleteAll(false); 94 dispatch(setSnackBarMessage(`Successfully deleted.`)); 95 getSites(); 96 }, 97 (err: ErrorResponseHandler) => { 98 dispatch(setErrorSnackMessage(err)); 99 }, 100 ); 101 102 const removeSites = (isAll: boolean = false, delSites: string[] = []) => { 103 invokeSiteRemoveApi("DELETE", `api/v1/admin/site-replication`, { 104 all: isAll, 105 sites: delSites, 106 }); 107 }; 108 109 useEffect(() => { 110 getSites(); 111 // eslint-disable-next-line react-hooks/exhaustive-deps 112 }, []); 113 114 const hasSites = sites?.length; 115 116 useEffect(() => { 117 dispatch(setHelpName("site-replication")); 118 // eslint-disable-next-line react-hooks/exhaustive-deps 119 }, []); 120 121 return ( 122 <Fragment> 123 <PageHeaderWrapper label={"Site Replication"} actions={<HelpMenu />} /> 124 <PageLayout> 125 <SectionTitle 126 separator={!!hasSites} 127 sx={{ marginBottom: 15 }} 128 actions={ 129 <Box 130 sx={{ 131 display: "flex", 132 alignItems: "center", 133 justifyContent: "flex-end", 134 gap: 8, 135 }} 136 > 137 {hasSites ? ( 138 <Fragment> 139 <TooltipWrapper tooltip={"Delete All"}> 140 <Button 141 id={"delete-all"} 142 label={"Delete All"} 143 variant="secondary" 144 disabled={isRemoving} 145 icon={<TrashIcon />} 146 onClick={() => { 147 setIsDeleteAll(true); 148 }} 149 /> 150 </TooltipWrapper> 151 <TooltipWrapper tooltip={"Replication Status"}> 152 <Button 153 id={"replication-status"} 154 label={"Replication Status"} 155 variant="regular" 156 icon={<RecoverIcon />} 157 onClick={(e) => { 158 e.preventDefault(); 159 navigate(IAM_PAGES.SITE_REPLICATION_STATUS); 160 }} 161 /> 162 </TooltipWrapper> 163 </Fragment> 164 ) : null} 165 <TooltipWrapper tooltip={"Add Replication Sites"}> 166 <Button 167 id={"add-replication-site"} 168 label={"Add Sites"} 169 variant="callAction" 170 disabled={isRemoving} 171 icon={<AddIcon />} 172 onClick={() => { 173 navigate(IAM_PAGES.SITE_REPLICATION_ADD); 174 }} 175 /> 176 </TooltipWrapper> 177 </Box> 178 } 179 > 180 {hasSites ? "List of Replicated Sites" : ""} 181 </SectionTitle> 182 {hasSites ? ( 183 <ReplicationSites 184 sites={sites} 185 onDeleteSite={removeSites} 186 onRefresh={getSites} 187 /> 188 ) : null} 189 {isSiteInfoLoading ? ( 190 <Box 191 sx={{ 192 display: "flex", 193 justifyContent: "center", 194 alignItems: "center", 195 height: "calc( 100vh - 450px )", 196 }} 197 > 198 <Loader style={{ width: 16, height: 16 }} /> 199 </Box> 200 ) : null} 201 {!hasSites && !isSiteInfoLoading ? ( 202 <Grid container> 203 <Grid item xs={8}> 204 <HelpBox 205 title={"Site Replication"} 206 iconComponent={<ClustersIcon />} 207 help={ 208 <Fragment> 209 This feature allows multiple independent MinIO sites (or 210 clusters) that are using the same external IDentity Provider 211 (IDP) to be configured as replicas. 212 <br /> 213 <br /> 214 To get started,{" "} 215 <ActionLink 216 isLoading={false} 217 label={""} 218 onClick={() => { 219 navigate(IAM_PAGES.SITE_REPLICATION_ADD); 220 }} 221 > 222 Add a Replication Site 223 </ActionLink> 224 . 225 <br /> 226 You can learn more at our{" "} 227 <a 228 href="https://min.io/docs/minio/linux/operations/install-deploy-manage/multi-site-replication.html?ref=con" 229 target="_blank" 230 rel="noopener" 231 > 232 documentation 233 </a> 234 . 235 </Fragment> 236 } 237 /> 238 </Grid> 239 </Grid> 240 ) : null} 241 {hasSites && !isSiteInfoLoading ? ( 242 <HelpBox 243 title={"Site Replication"} 244 iconComponent={<ClustersIcon />} 245 help={ 246 <Fragment> 247 This feature allows multiple independent MinIO sites (or 248 clusters) that are using the same external IDentity Provider 249 (IDP) to be configured as replicas. In this situation the set of 250 replica sites are referred to as peer sites or just sites. 251 <br /> 252 <br /> 253 Initially, only one of the sites added for replication may have 254 data. After site-replication is successfully configured, this 255 data is replicated to the other (initially empty) sites. 256 Subsequently, objects may be written to any of the sites, and 257 they will be replicated to all other sites. 258 <br /> 259 <br /> 260 All sites must have the same deployment credentials (i.e. 261 MINIO_ROOT_USER, MINIO_ROOT_PASSWORD). 262 <br /> 263 <br /> 264 All sites must be using the same external IDP(s) if any. 265 <br /> 266 <br /> 267 For SSE-S3 or SSE-KMS encryption via KMS, all sites must have 268 access to a central KMS deployment server. 269 <br /> 270 <br /> 271 You can learn more at our{" "} 272 <a 273 href="https://github.com/minio/minio/tree/master/docs/site-replication?ref=con" 274 target="_blank" 275 rel="noopener" 276 > 277 documentation 278 </a> 279 . 280 </Fragment> 281 } 282 /> 283 ) : null} 284 285 {deleteAll ? ( 286 <ConfirmDialog 287 title={`Delete All`} 288 confirmText={"Delete"} 289 isOpen={true} 290 titleIcon={<ConfirmDeleteIcon />} 291 isLoading={false} 292 onConfirm={() => { 293 const siteNames = sites.map((s: any) => s.name); 294 removeSites(true, siteNames); 295 }} 296 onClose={() => { 297 setIsDeleteAll(false); 298 }} 299 confirmationContent={ 300 <Fragment> 301 Are you sure you want to remove all the replication sites?. 302 </Fragment> 303 } 304 /> 305 ) : null} 306 </PageLayout> 307 </Fragment> 308 ); 309 }; 310 311 export default SiteReplication;