github.com/minio/console@v1.4.1/web-app/src/screens/Console/Support/OfflineRegistration.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2023 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, useState } from "react";
    18  import {
    19    Box,
    20    Button,
    21    CommentBox,
    22    CopyIcon,
    23    FormLayout,
    24    OfflineRegistrationIcon,
    25  } from "mds";
    26  import { ClusterRegistered } from "./utils";
    27  import { AppState, useAppDispatch } from "../../../store";
    28  import { useSelector } from "react-redux";
    29  import { fetchLicenseInfo } from "./registerThunks";
    30  import {
    31    setErrorSnackMessage,
    32    setServerNeedsRestart,
    33  } from "../../../systemSlice";
    34  import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
    35  import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
    36  import CopyToClipboard from "react-copy-to-clipboard";
    37  import RegisterHelpBox from "./RegisterHelpBox";
    38  import { api } from "api";
    39  import { ApiError, HttpResponse, SetConfigResponse } from "api/consoleApi";
    40  import { errorToHandler } from "api/errors";
    41  
    42  const OfflineRegistration = () => {
    43    const dispatch = useAppDispatch();
    44    const subnetRegToken = useSelector(
    45      (state: AppState) => state.register.subnetRegToken,
    46    );
    47    const clusterRegistered = useSelector(
    48      (state: AppState) => state.register.clusterRegistered,
    49    );
    50    const licenseInfo = useSelector(
    51      (state: AppState) => state.register.licenseInfo,
    52    );
    53  
    54    const offlineRegUrl = `https://subnet.min.io/cluster/register?token=${subnetRegToken}`;
    55  
    56    const [licenseKey, setLicenseKey] = useState("");
    57    const [loadingSave, setLoadingSave] = useState<boolean>(false);
    58  
    59    const applyAirGapLicense = () => {
    60      setLoadingSave(true);
    61      api.configs
    62        .setConfig("subnet", {
    63          key_values: [{ key: "license", value: licenseKey }],
    64        })
    65        .then((_) => {
    66          dispatch(fetchLicenseInfo());
    67          dispatch(setServerNeedsRestart(true));
    68        })
    69        .catch((res: HttpResponse<SetConfigResponse, ApiError>) => {
    70          dispatch(setErrorSnackMessage(errorToHandler(res.error)));
    71        })
    72        .finally(() => setLoadingSave(false));
    73    };
    74  
    75    return (
    76      <Fragment>
    77        <Box
    78          withBorders
    79          sx={{
    80            display: "flex",
    81            flexFlow: "column",
    82            padding: "43px",
    83          }}
    84        >
    85          {clusterRegistered && licenseInfo ? (
    86            <ClusterRegistered email={licenseInfo.email} />
    87          ) : (
    88            <FormLayout
    89              title={"Register cluster in an Air-gap environment"}
    90              icon={<OfflineRegistrationIcon />}
    91              helpBox={<RegisterHelpBox />}
    92              withBorders={false}
    93              containerPadding={false}
    94            >
    95              <Box
    96                sx={{
    97                  display: "flex",
    98                  flexFlow: "column",
    99                  flex: "2",
   100                  marginTop: "15px",
   101                  "& .step-row": {
   102                    fontSize: 14,
   103                    display: "flex",
   104                    marginTop: "15px",
   105                    marginBottom: "15px",
   106                  },
   107                }}
   108              >
   109                <Box>
   110                  <Box className="step-row">
   111                    <Box className="step-text">
   112                      Click on the link to register this cluster in SUBNET and get
   113                      a License Key for this Air-Gap deployment
   114                    </Box>
   115                  </Box>
   116  
   117                  <Box
   118                    sx={{
   119                      flex: "1",
   120                      display: "flex",
   121                      alignItems: "center",
   122                      gap: 3,
   123                    }}
   124                  >
   125                    <a href={offlineRegUrl} target="_blank">
   126                      https://subnet.min.io/cluster/register
   127                    </a>
   128  
   129                    <TooltipWrapper tooltip={"Copy to Clipboard"}>
   130                      <CopyToClipboard text={offlineRegUrl}>
   131                        <Button
   132                          type={"button"}
   133                          id={"copy-ult-to-clip-board"}
   134                          icon={<CopyIcon />}
   135                          color={"primary"}
   136                          variant={"regular"}
   137                        />
   138                      </CopyToClipboard>
   139                    </TooltipWrapper>
   140                  </Box>
   141  
   142                  <Box
   143                    className={"muted"}
   144                    sx={{
   145                      marginTop: "25px",
   146                    }}
   147                  >
   148                    Note: If this machine does not have internet connection, Copy
   149                    paste the following URL in a browser where you access SUBNET
   150                    and follow the instructions to complete the registration
   151                  </Box>
   152  
   153                  <Box
   154                    sx={{
   155                      marginTop: "25px",
   156                      display: "flex",
   157                      flexDirection: "column",
   158                    }}
   159                  >
   160                    <label style={{ fontWeight: "bold", marginBottom: "10px" }}>
   161                      Paste the License Key{" "}
   162                    </label>
   163                    <CommentBox
   164                      value={licenseKey}
   165                      disabled={loadingSave}
   166                      label={""}
   167                      id={"licenseKey"}
   168                      name={"licenseKey"}
   169                      placeholder={"License Key"}
   170                      onChange={(e) => {
   171                        setLicenseKey(e.target.value);
   172                      }}
   173                    />
   174                  </Box>
   175                  <Box sx={modalStyleUtils.modalButtonBar}>
   176                    <Button
   177                      id={"apply-license-key"}
   178                      onClick={applyAirGapLicense}
   179                      variant={"callAction"}
   180                      disabled={!licenseKey || loadingSave}
   181                      label={"Apply Cluster License"}
   182                    />
   183                  </Box>
   184                </Box>
   185              </Box>
   186            </FormLayout>
   187          )}
   188        </Box>
   189      </Fragment>
   190    );
   191  };
   192  
   193  export default OfflineRegistration;