codekingpro/portable-devtools
114k
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9import React from 'react';10import {AzureCredSchema, AzureClusterSchema, AzureDatabaseSchema} from './azure_schema.ui';11import pgAdmin from 'sources/pgadmin';12import { getNodeAjaxOptions, getNodeListById } from 'pgbrowser/node_ajax';13import SchemaView from '../../../../static/js/SchemaView';14import url_for from 'sources/url_for';15import { isEmptyString } from 'sources/validators';16import PropTypes from 'prop-types';17import getApiInstance from '../../../../static/js/api_instance';18import { CloudWizardEventsContext } from './CloudWizard';19import {MESSAGE_TYPE } from '../../../../static/js/components/FormComponents';20import gettext from 'sources/gettext';21import { makeStyles } from '@mui/styles';22 23const useStyles = makeStyles(() =>24 ({25 formClass: {26 overflow: 'auto',27 }28 }),29);30 31// Azure credentials32export function AzureCredentials(props) {33 const [cloudDBCredInstance, setCloudDBCredInstance] = React.useState();34 35 let _eventBus = React.useContext(CloudWizardEventsContext);36 React.useMemo(() => {37 const azureCloudDBCredSchema = new AzureCredSchema({38 authenticateAzure:(auth_type, azure_tenant_id) => {39 let loading_icon_url = url_for(40 'static', { 'filename': 'img/loading.gif'}41 );42 const axiosApi = getApiInstance();43 _eventBus.fireEvent('SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD', [MESSAGE_TYPE.INFO, 'Microsoft Azure authentication process is in progress..<img src="' + loading_icon_url + '" alt="' + gettext('Loading...') + '">']);44 let _url = url_for('azure.verify_credentials');45 const post_data = {46 cloud: 'azure',47 secret: {'auth_type':auth_type, 'azure_tenant_id':azure_tenant_id}48 };49 return new Promise((resolve, reject)=>{axiosApi.post(_url, post_data)50 .then((res) => {51 if (res.data && res.data.success == 1 ) {52 _eventBus.fireEvent('SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD',[MESSAGE_TYPE.SUCCESS, gettext('Authentication completed successfully. Click the Next button to proceed.')]);53 _eventBus.fireEvent('SET_CRED_VERIFICATION_INITIATED',true);54 resolve(true);55 }56 else if (res.data && res.data.success == 0) {57 _eventBus.fireEvent('SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD',[MESSAGE_TYPE.ERROR, res.data.errormsg]);58 _eventBus.fireEvent('SET_CRED_VERIFICATION_INITIATED',false);59 resolve(false);60 }61 })62 .catch((error) => {63 _eventBus.fireEvent('SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD',[MESSAGE_TYPE.ERROR, gettext(`Error while verifying Microsoft Azure: ${error}`)]);64 reject(new Error(gettext(error)));65 });66 });67 },68 getAuthCode:()=>{69 let _url_get_azure_verification_codes = url_for('azure.get_azure_verification_codes');70 const axiosApi = getApiInstance();71 return new Promise((resolve, reject)=>{72 const interval = setInterval(()=>{73 axiosApi.get(_url_get_azure_verification_codes)74 .then((res)=>{75 if (res.data.success){76 clearInterval(interval);77 let params = 'scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, width=550,height=650,left=920,top=150';78 window.open(res.data.data.verification_uri, 'azure_authentication', params);79 resolve(res);80 }81 })82 .catch((error)=>{83 clearInterval(interval);84 reject(error);85 });86 }, 1000);87 });88 }89 }, {}, _eventBus);90 setCloudDBCredInstance(azureCloudDBCredSchema);91 }, [props.cloudProvider]);92 93 return <SchemaView94 formType={'dialog'}95 getInitData={() => { /*This is intentional (SonarQube)*/ }}96 viewHelperProps={{ mode: 'create' }}97 schema={cloudDBCredInstance}98 showFooter={false}99 isTabView={false}100 onDataChange={(isChanged, changedData) => {101 props.setAzureCredData(changedData);102 }}103 />;104}105AzureCredentials.propTypes = {106 cloudProvider: PropTypes.string,107 setAzureCredData: PropTypes.func108};109 110 111// Azure Instance112export function AzureInstanceDetails(props) {113 const [azureInstanceSchema, setAzureInstanceSchema] = React.useState();114 const classes = useStyles();115 116 React.useMemo(() => {117 const AzureSchema = new AzureClusterSchema({118 subscriptions: () => getNodeAjaxOptions('get_subscriptions', {}, {}, {},{119 useCache:false,120 cacheNode: 'server',121 customGenerateUrl: ()=>{122 return url_for('azure.subscriptions');123 }124 }),125 resourceGroups: (subscription)=>getNodeAjaxOptions('ge_resource_groups', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {126 useCache:false,127 cacheNode: 'server',128 customGenerateUrl: ()=>{129 return url_for('azure.resource_groups', {'subscription_id': subscription});130 }131 }),132 regions: (subscription)=>getNodeAjaxOptions('get_regions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData,{133 useCache:false,134 cacheNode: 'server',135 customGenerateUrl: ()=>{136 return url_for('azure.regions', {'subscription_id': subscription});137 }138 }),139 availabilityZones: (region)=>getNodeAjaxOptions('get_availability_zones', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {140 useCache:false,141 cacheNode: 'server',142 customGenerateUrl: ()=>{143 return url_for('azure.availability_zones', {'region_name': region});144 }145 }),146 versionOptions: (availabilityZone)=>getNodeAjaxOptions('get_db_versions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {147 useCache:false,148 cacheNode: 'server',149 customGenerateUrl: ()=>{150 return url_for('azure.db_versions', {'availability_zone': availabilityZone});151 }152 }),153 instanceOptions: (dbVersion, availabilityZone)=>{154 if (isEmptyString(dbVersion) || isEmptyString(availabilityZone) ) return [];155 return getNodeAjaxOptions('get_instance_types', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {156 useCache:false,157 cacheNode: 'server',158 customGenerateUrl: ()=>{159 return url_for('azure.instance_types', {'availability_zone':availabilityZone, 'db_version': dbVersion});160 }161 });},162 storageOptions: (dbVersion, availabilityZone)=>{163 if (isEmptyString(dbVersion) || isEmptyString(availabilityZone) ) return [];164 return getNodeAjaxOptions('get_instance_types', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {165 useCache:false,166 cacheNode: 'server',167 customGenerateUrl: ()=>{168 return url_for('azure.storage_types', {'availability_zone':availabilityZone, 'db_version': dbVersion});169 }170 });171 },172 zoneRedundantHaSupported: (region)=>getNodeAjaxOptions('is_zone_redundant_ha_supported', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData,{173 useCache:false,174 cacheNode: 'server',175 customGenerateUrl: ()=>{176 return url_for('azure.zone_redundant_ha_supported', {'region_name': region});177 }178 }),179 }, {180 nodeInfo: props.nodeInfo,181 nodeData: props.nodeData,182 hostIP: props.hostIP,183 ...props.azureInstanceData184 });185 setAzureInstanceSchema(AzureSchema);186 }, [props.cloudProvider]);187 188 return <SchemaView189 formType={'dialog'}190 getInitData={() => { /*This is intentional (SonarQube)*/ }}191 viewHelperProps={{ mode: 'create' }}192 schema={azureInstanceSchema}193 showFooter={false}194 isTabView={false}195 onDataChange={(isChanged, changedData) => {196 props.setAzureInstanceData(changedData);197 }}198 formClassName={classes.formClass}199 />;200}201AzureInstanceDetails.propTypes = {202 nodeInfo: PropTypes.object,203 nodeData: PropTypes.object,204 cloudProvider: PropTypes.string,205 setAzureInstanceData: PropTypes.func,206 hostIP: PropTypes.string,207 azureInstanceData: PropTypes.object208};209 210 211// Azure Database Details212export function AzureDatabaseDetails(props) {213 const [azureDBInstance, setAzureDBInstance] = React.useState();214 const classes = useStyles();215 216 React.useMemo(() => {217 const azureDBSchema = new AzureDatabaseSchema({218 server_groups: ()=>getNodeListById(pgAdmin.Browser.Nodes['server_group'], props.nodeInfo, props.nodeData),219 },220 {221 gid: props.nodeInfo['server_group']._id,222 }223 );224 setAzureDBInstance(azureDBSchema);225 226 }, [props.cloudProvider]);227 228 return <SchemaView229 formType={'dialog'}230 getInitData={() => { /*This is intentional (SonarQube)*/ }}231 viewHelperProps={{ mode: 'create' }}232 schema={azureDBInstance}233 showFooter={false}234 isTabView={false}235 onDataChange={(isChanged, changedData) => {236 props.setAzureDatabaseData(changedData);237 }}238 formClassName={classes.formClass}239 />;240}241AzureDatabaseDetails.propTypes = {242 nodeInfo: PropTypes.object,243 nodeData: PropTypes.object,244 cloudProvider: PropTypes.string,245 setAzureDatabaseData: PropTypes.func,246};247 248 249// Validation functions250export function validateAzureStep2(cloudInstanceDetails) {251 let isError = false;252 if (isEmptyString(cloudInstanceDetails.name) ||253 isEmptyString(cloudInstanceDetails.db_version) || isEmptyString(cloudInstanceDetails.instance_type) ||254 isEmptyString(cloudInstanceDetails.region)|| isEmptyString(cloudInstanceDetails.storage_size) || isEmptyString(cloudInstanceDetails.public_ips)) {255 isError = true;256 }257 return isError;258}259 260export function validateAzureStep3(cloudDBDetails, nodeInfo) {261 let isError = false;262 if (isEmptyString(cloudDBDetails.db_username) || isEmptyString(cloudDBDetails.db_password)) {263 isError = true;264 }265 266 if (cloudDBDetails.db_password != cloudDBDetails.db_confirm_password || !/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$@!%&*?])[A-Za-z\d#$@!%&*?]{8,128}$/.test(cloudDBDetails.db_confirm_password)) {267 isError = true;268 }269 270 if (isEmptyString(cloudDBDetails.gid)) cloudDBDetails.gid = nodeInfo['server_group']._id;271 return isError;272}273 274 275// Check cluster name avaiablity276export function checkClusternameAvailbility(clusterName){277 return new Promise((resolve, reject)=>{278 let _url = url_for('azure.check_cluster_name_availability');279 const axiosApi = getApiInstance();280 axiosApi.get(_url, {281 params: {282 'name': clusterName,283 }284 }).then((res)=>{285 if (res.data) {286 resolve(res.data);287 }288 }).catch((error) => {289 reject(gettext(`Error while checking server name availability with Microsoft Azure: ${error.response.data.errormsg}`));290 });291 });292}293 294// Summary creation295function createData(name, value) {296 if (typeof(value) == 'boolean') {297 value = (value === true) ? 'True' : 'False';298 }299 return { name, value };300}301 302// Summary section303export function getAzureSummary(cloud, cloudInstanceDetails, cloudDBDetails) {304 const rows1 = [305 createData(gettext('Cloud'), cloud),306 createData(gettext('Subscription'), cloudInstanceDetails.subscription),307 createData(gettext('Resource group'), cloudInstanceDetails.resource_group),308 createData(gettext('Region'), cloudInstanceDetails.region),309 createData(gettext('Availability zone'), cloudInstanceDetails.availability_zone),310 ];311 312 const rows2 = [313 createData(gettext('PostgreSQL version'), cloudInstanceDetails.db_version),314 createData(gettext('Instance type'), cloudInstanceDetails.instance_type),315 ];316 317 const rows3 = [318 createData(gettext('Allocated storage'), cloudInstanceDetails.storage_size + ' GiB'),319 ];320 321 const rows4 = [322 createData(gettext('Username'), cloudDBDetails.db_username),323 createData(gettext('Password'), 'xxxxxxx'),324 ];325 326 const rows5 = [327 createData(gettext('Public IP'), cloudInstanceDetails.public_ips),328 ];329 330 const rows6 = [331 createData(gettext('High availability'), cloudInstanceDetails.high_availability),332 ];333 334 return [rows1, rows2, rows3, rows4, rows5, rows6];335}336 