????
Current Path : C:/opt/pgsql/pgAdmin 4/web/pgadmin/misc/cloud/static/js/ |
Current File : C:/opt/pgsql/pgAdmin 4/web/pgadmin/misc/cloud/static/js/CloudWizard.jsx |
///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2024, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// import gettext from 'sources/gettext'; import url_for from 'sources/url_for'; import React from 'react'; import { Box, Paper } from '@mui/material'; import { makeStyles } from '@mui/styles'; import Wizard from '../../../../static/js/helpers/wizard/Wizard'; import WizardStep from '../../../../static/js/helpers/wizard/WizardStep'; import {FormFooterMessage, MESSAGE_TYPE } from '../../../../static/js/components/FormComponents'; import getApiInstance from '../../../../static/js/api_instance'; import PropTypes from 'prop-types'; import pgAdmin from 'sources/pgadmin'; import {ToggleButtons, FinalSummary} from './cloud_components'; import { PrimaryButton } from '../../../../static/js/components/Buttons'; import {AwsCredentials, AwsInstanceDetails, AwsDatabaseDetails, validateCloudStep1, validateCloudStep2, validateCloudStep3} from './aws'; import {BigAnimalInstance, BigAnimalDatabase, BigAnimalClusterType, validateBigAnimal, validateBigAnimalStep2, validateBigAnimalStep3, validateBigAnimalStep4} from './biganimal'; import { isEmptyString } from 'sources/validators'; import { AWSIcon, BigAnimalIcon, AzureIcon, GoogleCloudIcon } from '../../../../static/js/components/ExternalIcon'; import {AzureCredentials, AzureInstanceDetails, AzureDatabaseDetails, checkClusternameAvailbility, validateAzureStep2, validateAzureStep3} from './azure'; import { GoogleCredentials, GoogleInstanceDetails, GoogleDatabaseDetails, validateGoogleStep2, validateGoogleStep3 } from './google'; import EventBus from '../../../../static/js/helpers/EventBus'; import { CLOUD_PROVIDERS, CLOUD_PROVIDERS_LABELS } from './cloud_constants'; import { LAYOUT_EVENTS } from '../../../../static/js/helpers/Layout'; const useStyles = makeStyles(() => ({ messageBox: { marginBottom: '1em', display: 'flex', }, messagePadding: { paddingTop: '10px', flex: 2.5, }, buttonMarginEDB: { position: 'relative', top: '20%', }, toggleButton: { height: '100px', }, summaryContainer: { flexGrow: 1, minHeight: 0, overflow: 'auto', }, boxText: { paddingBottom: '5px' }, authButton: { marginLeft: '12em' } }), ); export const CloudWizardEventsContext = React.createContext(); export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanelId}) { const classes = useStyles(); const eventBus = React.useRef(new EventBus()); let steps = [gettext('Cloud Provider'), gettext('Credentials'), gettext('Cluster Type'), gettext('Instance Specification'), gettext('Database Details'), gettext('Review')]; const [currentStep, setCurrentStep] = React.useState(''); const [cloudSelection, setCloudSelection] = React.useState(''); const [errMsg, setErrMsg] = React.useState(''); const [cloudInstanceDetails, setCloudInstanceDetails] = React.useState({}); const [cloudDBCred, setCloudDBCred] = React.useState({}); const [cloudDBDetails, setCloudDBDetails] = React.useState({}); const [callRDSAPI, setCallRDSAPI] = React.useState({}); const [hostIP, setHostIP] = React.useState('127.0.0.1/32'); const [cloudProvider, setCloudProvider] = React.useState(''); const [verificationIntiated, setVerificationIntiated] = React.useState(false); const [bigAnimalInstanceData, setBigAnimalInstanceData] = React.useState({}); const [bigAnimalDatabaseData, setBigAnimalDatabaseData] = React.useState({}); const [bigAnimalClusterTypeData, setBigAnimalClusterTypeData] = React.useState({}); const [azureCredData, setAzureCredData] = React.useState({}); const [azureInstanceData, setAzureInstanceData] = React.useState({}); const [azureDatabaseData, setAzureDatabaseData] = React.useState({}); const [googleCredData, setGoogleCredData] = React.useState({}); const [googleInstanceData, setGoogleInstanceData] = React.useState({}); const [googleDatabaseData, setGoogleDatabaseData] = React.useState({}); const axiosApi = getApiInstance(); const [verificationURI, setVerificationURI] = React.useState(''); const [verificationCode, setVerificationCode] = React.useState(''); const authInterval = React.useRef(); React.useEffect(()=>{ eventBus.current.registerListener('SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD', (msg) => { setErrMsg(msg); }); eventBus.current.registerListener('SET_CRED_VERIFICATION_INITIATED', (initiated) => { setVerificationIntiated(initiated); }); const onWizardClosing = (panelId)=>{ if(panelId == cloudPanelId) { clearInterval(authInterval.current); onClose(); } }; pgAdmin.Browser.docker.eventBus.registerListener(LAYOUT_EVENTS.CLOSING, onWizardClosing); return ()=>{ pgAdmin.Browser.docker.eventBus.deregisterListener(LAYOUT_EVENTS.CLOSING, onWizardClosing); }; }, []); React.useEffect(() => { let _url = url_for('cloud.get_host_ip') ; axiosApi.get(_url) .then((res) => { if (res.data.data) { setHostIP(res.data.data); } }) .catch((error) => { pgAdmin.Browser.notifier.error(gettext(`Error while getting the host ip: ${error.response.data.errormsg}`)); }); }, [cloudProvider]); const wizardStepChange = (data) => { setCurrentStep(data.currentStep); }; const onSave = () => { let _url = url_for('cloud.deploy_on_cloud'), post_data = {}; if (cloudProvider == CLOUD_PROVIDERS.AWS) { post_data = { gid: nodeInfo.server_group._id, cloud: cloudProvider, secret: cloudDBCred, instance_details:cloudInstanceDetails, db_details: cloudDBDetails }; } else if(cloudProvider == CLOUD_PROVIDERS.AZURE){ post_data = { gid: nodeInfo.server_group._id, secret: azureCredData, cloud: cloudProvider, instance_details:azureInstanceData, db_details: azureDatabaseData }; }else if(cloudProvider == CLOUD_PROVIDERS.GOOGLE){ post_data = { gid: nodeInfo.server_group._id, secret: googleCredData, cloud: cloudProvider, instance_details:googleInstanceData, db_details: googleDatabaseData }; }else { post_data = { gid: nodeInfo.server_group._id, cloud: cloudProvider, cluster_details: bigAnimalClusterTypeData, instance_details: bigAnimalInstanceData, db_details: bigAnimalDatabaseData }; } axiosApi.post(_url, post_data) .then((res) => { pgAdmin.Browser.Events.trigger('pgadmin:browser:tree:add', res.data.data.node, {'server_group': nodeInfo['server_group']}); pgAdmin.Browser.BgProcessManager.startProcess(res.data.data.job_id, res.data.data.desc); onClose(); }) .catch((error) => { pgAdmin.Browser.notifier.error(gettext(`Error while saving cloud wizard data: ${error.response.data.errormsg}`)); }); }; const disableNextCheck = () => { setCallRDSAPI(currentStep); let isError = (cloudProvider == ''); switch(cloudProvider) { case CLOUD_PROVIDERS.AWS: switch (currentStep) { case 0: setCloudSelection(CLOUD_PROVIDERS.AWS); break; case 1: isError = validateCloudStep1(cloudDBCred); break; case 2: break; case 3: isError = validateCloudStep2(cloudInstanceDetails, hostIP); break; case 4: isError = validateCloudStep3(cloudDBDetails, nodeInfo); break; default: break; } break; case CLOUD_PROVIDERS.BIGANIMAL: switch (currentStep) { case 0: setCloudSelection(CLOUD_PROVIDERS.BIGANIMAL); break; case 1: isError = !verificationIntiated; break; case 2: isError = validateBigAnimalStep2(bigAnimalClusterTypeData); break; case 3: isError = validateBigAnimalStep3(bigAnimalInstanceData); break; case 4: isError = validateBigAnimalStep4(bigAnimalDatabaseData, nodeInfo); break; default: break; } break; case CLOUD_PROVIDERS.AZURE: switch (currentStep) { case 0: setCloudSelection(CLOUD_PROVIDERS.AZURE); break; case 1: isError = !verificationIntiated; break; case 2: break; case 3: isError = validateAzureStep2(azureInstanceData); break; case 4: isError = validateAzureStep3(azureDatabaseData, nodeInfo); break; default: break; } break; case CLOUD_PROVIDERS.GOOGLE: switch (currentStep) { case 0: setCloudSelection(CLOUD_PROVIDERS.GOOGLE); break; case 1: isError = !verificationIntiated; break; case 2: break; case 3: isError = validateGoogleStep2(googleInstanceData); break; case 4: isError = validateGoogleStep3(googleDatabaseData, nodeInfo); break; default: break; } } return isError; }; const onBeforeBack = (activeStep) => { return new Promise((resolve)=>{ if(activeStep == 3 && (cloudProvider == CLOUD_PROVIDERS.AWS || cloudProvider == CLOUD_PROVIDERS.AZURE || cloudProvider == CLOUD_PROVIDERS.GOOGLE)) { resolve(true); } else if(activeStep == 1 && (cloudProvider == CLOUD_PROVIDERS.AWS || cloudProvider == CLOUD_PROVIDERS.AZURE || cloudProvider == CLOUD_PROVIDERS.GOOGLE)) { setVerificationIntiated(false); } setErrMsg(['', '']); resolve(); }); }; const onBeforeNext = (activeStep) => { return new Promise((resolve, reject)=>{ if(activeStep == 1 && cloudProvider == CLOUD_PROVIDERS.AWS) { setErrMsg([MESSAGE_TYPE.INFO, gettext('Validating credentials...')]); let _url = url_for('rds.verify_credentials'); const post_data = { cloud: cloudSelection, secret: cloudDBCred, }; axiosApi.post(_url, post_data) .then((res) => { if(!res.data.success) { setErrMsg([MESSAGE_TYPE.ERROR, res.data.info]); reject(new Error(res.data.info)); } else { setErrMsg(['', '']); if (activeStep == 1) { resolve(true); } else { resolve(false); } } }) .catch(() => { setErrMsg([MESSAGE_TYPE.ERROR, gettext('Error while checking cloud credentials')]); reject(new Error(gettext('Error while checking cloud credentials'))); }); } else if(activeStep == 0 && cloudProvider == CLOUD_PROVIDERS.BIGANIMAL) { if (!isEmptyString(verificationURI)) { resolve(); return; } setErrMsg([MESSAGE_TYPE.INFO, gettext('Getting EDB BigAnimal verification URL...')]); validateBigAnimal() .then((res) => { setVerificationURI(res); setVerificationCode(res.substring(res.indexOf('=')+1)); setErrMsg(['', '']); resolve(); }) .catch((error) => { setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]); reject(new Error(gettext(error))); }); } else if (cloudProvider == CLOUD_PROVIDERS.AZURE) { if (activeStep == 1) { // Skip the current step setErrMsg(['', '']); resolve(true); } else if (activeStep == 2) { setErrMsg([MESSAGE_TYPE.INFO, gettext('Checking cluster name availability...')]); checkClusternameAvailbility(azureInstanceData.name) .then((res)=>{ if (res.data && res.data.success == 0 ) { setErrMsg([MESSAGE_TYPE.ERROR, gettext('Specified cluster name is already used.')]); }else{ setErrMsg(['', '']); } resolve(); }).catch((error)=>{ setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]); reject(new Error(gettext(error))); }); } else { resolve(); } }else if (cloudProvider == CLOUD_PROVIDERS.GOOGLE) { if (activeStep == 1) { // Skip the current step setErrMsg(['', '']); resolve(true); } else if (activeStep == 2) { resolve(true);} else { resolve(); } } else { setErrMsg(['', '']); resolve(); } }); }; const authenticateBigAnimal = () => { let loading_icon_url = url_for( 'static', { 'filename': 'img/loading.gif'} ); setErrMsg([MESSAGE_TYPE.INFO, gettext('EDB BigAnimal authentication process is in progress...') + '<img src="' + loading_icon_url + '" alt="' + gettext('Loading...') + '">']); let child = window.open(verificationURI, 'edb_biganimal_authentication'); let _url = url_for('biganimal.verification_ack') ; let countdown = 60; authInterval.current = setInterval(() => { axiosApi.get(_url) .then((res) => { if (res.data && res.data.success == 1 ) { setErrMsg([MESSAGE_TYPE.SUCCESS, gettext('Authentication completed successfully. Click the Next button to proceed.')]); setVerificationIntiated(true); clearInterval(authInterval.current); } else if (res.data && res.data.success == 0 && res.data.errormsg == 'access_denied') { setErrMsg([MESSAGE_TYPE.INFO, gettext('Verification failed. Access Denied...')]); setVerificationIntiated(false); clearInterval(authInterval.current); } else if (res.data && res.data.success == 0 && res.data.errormsg == 'forbidden') { setErrMsg([MESSAGE_TYPE.INFO, gettext('Authentication completed successfully but you do not have permission to create the cluster.')]); setVerificationIntiated(false); clearInterval(authInterval.current); } else if (child.closed && !verificationIntiated && countdown <= 0) { setVerificationIntiated(false); setErrMsg([MESSAGE_TYPE.ERROR, gettext('Authentication is aborted.')]); clearInterval(authInterval.current); } authInterval.current = null; }) .catch((error) => { setErrMsg([MESSAGE_TYPE.ERROR, gettext(`Error while verifying EDB BigAnimal: ${error.response.data.errormsg}`)]); }); countdown = countdown - 1; }, 1000); }; const onDialogHelp = () => { window.open(url_for('help.static', { 'filename': 'cloud_deployment.html' }), 'pgadmin_help'); }; const onErrClose = React.useCallback(()=>{ setErrMsg([]); }); let cloud_providers = [ {label: gettext(CLOUD_PROVIDERS_LABELS.AWS), value: CLOUD_PROVIDERS.AWS, icon: <AWSIcon className={classes.icon} />}, {label: gettext(CLOUD_PROVIDERS_LABELS.BIGANIMAL), value: CLOUD_PROVIDERS.BIGANIMAL, icon: <BigAnimalIcon className={classes.icon} />}, {label: gettext(CLOUD_PROVIDERS_LABELS.AZURE), value: CLOUD_PROVIDERS.AZURE, icon: <AzureIcon className={classes.icon} /> }, {label: gettext(CLOUD_PROVIDERS_LABELS.GOOGLE), value: CLOUD_PROVIDERS.GOOGLE, icon: <GoogleCloudIcon className={classes.icon} /> }]; return ( <CloudWizardEventsContext.Provider value={eventBus.current}> <Wizard title={gettext('Deploy Cloud Instance')} stepList={steps} disableNextStep={disableNextCheck} onStepChange={wizardStepChange} onSave={onSave} onHelp={onDialogHelp} beforeNext={onBeforeNext} beforeBack={onBeforeBack}> <WizardStep stepId={0}> <Box className={classes.messageBox}> <Box className={classes.messagePadding}>{gettext('Select a cloud provider for PostgreSQL database.')}</Box> </Box> <Box className={classes.messageBox}> <ToggleButtons cloudProvider={cloudProvider} setCloudProvider={setCloudProvider} options={cloud_providers} ></ToggleButtons> </Box> <FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} /> </WizardStep> <WizardStep stepId={1} > <Box className={classes.buttonMarginEDB}> {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}> <Box>{gettext('The verification code to authenticate the pgAdmin to EDB BigAnimal is: ')} <strong>{verificationCode}</strong> <br/>{gettext('By clicking the below button, you will be redirected to the EDB BigAnimal authentication page in a new tab.')} </Box> </Box>} {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <PrimaryButton onClick={authenticateBigAnimal} disabled={verificationIntiated}> {gettext('Click here to authenticate yourself to EDB BigAnimal')} </PrimaryButton>} {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}> <Box ></Box> </Box>} </Box> {cloudProvider == CLOUD_PROVIDERS.AWS && <AwsCredentials cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setCloudDBCred={setCloudDBCred}/>} { cloudProvider == CLOUD_PROVIDERS.AZURE && <Box flexGrow={1}> <AzureCredentials cloudProvider={cloudProvider} setAzureCredData={setAzureCredData}/> </Box>} <Box flexGrow={1}> {cloudProvider == CLOUD_PROVIDERS.GOOGLE && <GoogleCredentials cloudProvider={cloudProvider} setGoogleCredData={setGoogleCredData}/>} </Box> <FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} /> </WizardStep> <WizardStep stepId={2} > {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 2 && <BigAnimalClusterType cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setBigAnimalClusterTypeData={setBigAnimalClusterTypeData} hostIP={hostIP} /> } <FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} /> </WizardStep> <WizardStep stepId={3} > {cloudProvider == CLOUD_PROVIDERS.AWS && callRDSAPI == 3 && <AwsInstanceDetails cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setCloudInstanceDetails={setCloudInstanceDetails} hostIP={hostIP} /> } {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 3 && <BigAnimalInstance cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setBigAnimalInstanceData={setBigAnimalInstanceData} hostIP={hostIP} bigAnimalClusterTypeData={bigAnimalClusterTypeData} /> } {cloudProvider == CLOUD_PROVIDERS.AZURE && callRDSAPI == 3 && <AzureInstanceDetails cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setAzureInstanceData={setAzureInstanceData} hostIP={hostIP} azureInstanceData = {azureInstanceData} /> } {cloudProvider == CLOUD_PROVIDERS.GOOGLE && callRDSAPI == 3 && <GoogleInstanceDetails cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setGoogleInstanceData={setGoogleInstanceData} hostIP={hostIP} googleInstanceData = {googleInstanceData} /> } <FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} /> </WizardStep> <WizardStep stepId={4} > {cloudProvider == CLOUD_PROVIDERS.AWS && <AwsDatabaseDetails cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setCloudDBDetails={setCloudDBDetails} /> } {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 4 && <BigAnimalDatabase cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setBigAnimalDatabaseData={setBigAnimalDatabaseData} bigAnimalClusterTypeData={bigAnimalClusterTypeData} /> } {cloudProvider == CLOUD_PROVIDERS.AZURE && <AzureDatabaseDetails cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setAzureDatabaseData={setAzureDatabaseData} /> } {cloudProvider == CLOUD_PROVIDERS.GOOGLE && <GoogleDatabaseDetails cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setGoogleDatabaseData={setGoogleDatabaseData} /> } </WizardStep> <WizardStep stepId={5} > <Box className={classes.boxText}>{gettext('Please review the details before creating the cloud instance.')}</Box> <Paper variant="outlined" elevation={0} className={classes.summaryContainer}> {cloudProvider == CLOUD_PROVIDERS.AWS && callRDSAPI == 5 && <FinalSummary cloudProvider={cloudProvider} instanceData={cloudInstanceDetails} databaseData={cloudDBDetails} /> } {cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 5 && <FinalSummary cloudProvider={cloudProvider} instanceData={bigAnimalInstanceData} databaseData={bigAnimalDatabaseData} clusterTypeData={bigAnimalClusterTypeData} /> } {cloudProvider == CLOUD_PROVIDERS.AZURE && callRDSAPI == 5 && <FinalSummary cloudProvider={cloudProvider} instanceData={azureInstanceData} databaseData={azureDatabaseData} /> } {cloudProvider == CLOUD_PROVIDERS.GOOGLE && callRDSAPI == 5 && <FinalSummary cloudProvider={cloudProvider} instanceData={googleInstanceData} databaseData={googleDatabaseData} /> } </Paper> </WizardStep> </Wizard> </CloudWizardEventsContext.Provider> ); } CloudWizard.propTypes = { nodeInfo: PropTypes.object, nodeData: PropTypes.object, onClose: PropTypes.func, cloudPanelId: PropTypes.string, };