// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import _ from 'lodash' import { i18n } from '@/locales' import { getAPI } from '@/api' import { message, notification, Modal } from 'ant-design-vue' import eventBus from '@/config/eventBus' import store from '@/store' import { sourceToken } from '@/utils/request' import { toLocalDate, toLocaleDate } from '@/utils/date' export const pollJobPlugin = { install (app) { app.config.globalProperties.$pollJob = function (options) { /** * @param {String} jobId * @param {String} [name=''] * @param {String} [title=''] * @param {String} [description=''] * @param {Boolean} [showSuccessMessage=true] * @param {String} [successMessage=Success] * @param {Function} [successMethod=() => {}] * @param {String} [errorMessage=Error] * @param {Function} [errorMethod=() => {}] * @param {Object} [showLoading=true] * @param {String} [loadingMessage=Loading...] * @param {String} [catchMessage=Error caught] * @param {Function} [catchMethod=() => {}] * @param {Object} [action=null] * @param {Object} [bulkAction=false] * @param {String} resourceId */ const { jobId, name = '', title = '', description = '', showSuccessMessage = true, successMessage = i18n.global.t('label.success'), successMethod = () => {}, errorMessage = i18n.global.t('label.error'), errorMethod = () => {}, loadingMessage = `${i18n.global.t('label.loading')}...`, showLoading = true, catchMessage = i18n.global.t('label.error.caught'), catchMethod = () => {}, action = null, bulkAction = false, resourceId = null } = options store.dispatch('AddHeaderNotice', { key: jobId, title, description, status: 'progress', timestamp: new Date() }) eventBus.on('update-job-details', (args) => { const { jobId, resourceId } = args const fullPath = this.$route.fullPath const path = this.$route.path var jobs = this.$store.getters.headerNotices.map(job => { if (job.key === jobId) { if (resourceId && !path.includes(resourceId)) { job.path = path + '/' + resourceId } else { job.path = fullPath } } return job }) this.$store.commit('SET_HEADER_NOTICES', jobs) }) options.originalPage = options.originalPage || this.$router.currentRoute.value.path getAPI('queryAsyncJobResult', { jobId }).then(json => { const result = json.queryasyncjobresultresponse eventBus.emit('update-job-details', { jobId, resourceId }) if (result.jobstatus === 1) { if (showSuccessMessage) { var content = successMessage if (successMessage === 'Success' && action && action.label) { content = i18n.global.t(action.label) } if (name) { content = content + ' - ' + name } message.success({ content, key: jobId, duration: 2 }) } else { message.destroy(jobId) } store.dispatch('AddHeaderNotice', { key: jobId, title, description, status: 'done', duration: 2, timestamp: new Date() }) eventBus.emit('update-job-details', { jobId, resourceId }) // Ensure we refresh on the same / parent page const currentPage = this.$router.currentRoute.value.path const samePage = options.originalPage === currentPage || options.originalPage.startsWith(currentPage + '/') if (samePage && (!action || !('isFetchData' in action) || (action.isFetchData))) { eventBus.emit('async-job-complete', action) } successMethod(result) } else if (result.jobstatus === 2) { if (!bulkAction) { message.error({ content: errorMessage, key: jobId, duration: 1 }) } var errMessage = errorMessage if (action && action.label) { errMessage = i18n.global.t(action.label) } var desc = result.jobresult.errortext if (name) { desc = `(${name}) ${desc}` } let onClose = () => {} if (!bulkAction) { let countNotify = store.getters.countNotify countNotify++ store.commit('SET_COUNT_NOTIFY', countNotify) onClose = () => { let countNotify = store.getters.countNotify countNotify > 0 ? countNotify-- : countNotify = 0 store.commit('SET_COUNT_NOTIFY', countNotify) } } notification.error({ top: '65px', message: errMessage, description: desc, key: jobId, duration: 0, onClose: onClose }) store.dispatch('AddHeaderNotice', { key: jobId, title, description: desc, status: 'failed', duration: 2, timestamp: new Date() }) eventBus.emit('update-job-details', { jobId, resourceId }) // Ensure we refresh on the same / parent page const currentPage = this.$router.currentRoute.value.path const samePage = options.originalPage === currentPage || options.originalPage.startsWith(currentPage + '/') if (samePage && (!action || !('isFetchData' in action) || (action.isFetchData))) { eventBus.emit('async-job-complete', action) } errorMethod(result) } else if (result.jobstatus === 0) { if (showLoading) { message.loading({ content: loadingMessage, key: jobId, duration: 0 }) } setTimeout(() => { this.$pollJob(options, action) }, 3000) } }).catch(e => { console.error(`${catchMessage} - ${e}`) if (!sourceToken.isCancel(e)) { let countNotify = store.getters.countNotify countNotify++ store.commit('SET_COUNT_NOTIFY', countNotify) notification.error({ top: '65px', message: i18n.global.t('label.error'), description: catchMessage, duration: 0, onClose: () => { let countNotify = store.getters.countNotify countNotify > 0 ? countNotify-- : countNotify = 0 store.commit('SET_COUNT_NOTIFY', countNotify) } }) } catchMethod && catchMethod() }) } } } export const notifierPlugin = { install (app) { app.config.globalProperties.$notifyError = function (error) { console.log(error) var msg = i18n.global.t('message.request.failed') var desc = '' if (error && error.response) { if (error.response.status) { msg = `${i18n.global.t('message.request.failed')} (${error.response.status})` } if (error.message) { desc = error.message } if (error.response.headers && 'x-description' in error.response.headers) { desc = error.response.headers['x-description'] } if (desc === '' && error.response.data) { const responseKey = _.findKey(error.response.data, 'errortext') if (responseKey) { desc = error.response.data[responseKey].errortext } } } let countNotify = store.getters.countNotify countNotify++ store.commit('SET_COUNT_NOTIFY', countNotify) notification.error({ top: '65px', message: msg, description: desc, duration: 0, onClose: () => { let countNotify = store.getters.countNotify countNotify > 0 ? countNotify-- : countNotify = 0 store.commit('SET_COUNT_NOTIFY', countNotify) } }) } app.config.globalProperties.$notification = { defaultConfig: { top: '65px', onClose: () => { let countNotify = store.getters.countNotify countNotify > 0 ? countNotify-- : countNotify = 0 store.commit('SET_COUNT_NOTIFY', countNotify) } }, setCountNotify: () => { let countNotify = store.getters.countNotify countNotify++ store.commit('SET_COUNT_NOTIFY', countNotify) }, info: (config) => { app.config.globalProperties.$notification.setCountNotify() config = Object.assign({}, app.config.globalProperties.$notification.defaultConfig, config) notification.info(config) }, error: (config) => { app.config.globalProperties.$notification.setCountNotify() config = Object.assign({}, app.config.globalProperties.$notification.defaultConfig, config) notification.error(config) }, success: (config) => { app.config.globalProperties.$notification.setCountNotify() config = Object.assign({}, app.config.globalProperties.$notification.defaultConfig, config) notification.success(config) }, warning: (config) => { app.config.globalProperties.$notification.setCountNotify() config = Object.assign({}, app.config.globalProperties.$notification.defaultConfig, config) notification.warning(config) }, warn: (config) => { app.config.globalProperties.$notification.setCountNotify() config = Object.assign({}, app.config.globalProperties.$notification.defaultConfig, config) notification.warn(config) }, close: (key) => notification.close(key), destroy: () => notification.destroy() } app.config.globalProperties.$messageConfigSuccess = function (msg, configrecord) { if (configrecord.isdynamic) { msg += `. ${this.$t('message.setting.update.delay')}` } message.success(msg) } } } export const toLocaleDatePlugin = { install (app) { app.config.globalProperties.$toLocaleDate = function (date) { const { timezoneoffset, usebrowsertimezone } = this.$store.getters return toLocaleDate({ date, timezoneoffset, usebrowsertimezone }) } app.config.globalProperties.$toLocalDate = function (date) { const { timezoneoffset, usebrowsertimezone } = this.$store.getters return toLocalDate({ date, timezoneoffset, usebrowsertimezone }).toISOString() } } } export const configUtilPlugin = { install (app) { app.config.globalProperties.$applyDocHelpMappings = function (docHelp) { var docHelpMappings = this.$config.docHelpMappings if (docHelp && docHelpMappings && docHelpMappings.constructor === Object && Object.keys(docHelpMappings).length > 0) { for (var key in docHelpMappings) { if (docHelp.includes(key) && docHelp !== docHelpMappings[key]) { docHelp = docHelp.replace(key, docHelpMappings[key]) break } } } return docHelp } } } export const showIconPlugin = { install (app) { app.config.globalProperties.$showIcon = function (resource) { var resourceType = this.$route.path.split('/')[1] if (resource) { resourceType = resource } if (['zone', 'zones', 'template', 'iso', 'account', 'accountuser', 'vm', 'domain', 'project', 'vpc', 'guestnetwork', 'guestoscategory'].includes(resourceType)) { return true } else { return false } } } } export const resourceTypePlugin = { install (app) { app.config.globalProperties.$getResourceType = function () { const type = this.$route.path.split('/')[1] if (type === 'vm') { return 'UserVM' } else if (type === 'accountuser') { return 'User' } else if (type === 'guestnetwork') { return 'Network' } else { return type } } app.config.globalProperties.$getRouteFromResourceType = function (resourceType) { switch (resourceType) { case 'VirtualMachine': return 'vm' case 'DomainRouter': return 'router' case 'ConsoleProxy': return 'systemvm' case 'User': return 'accountuser' case 'Network': return 'guestnetwork' case 'ServiceOffering': return 'computeoffering' case 'IpAddress': return 'publicip' case 'NetworkAcl': return 'acllist' case 'KubernetesCluster': return 'kubernetes' case 'KubernetesSupportedVersion': return 'kubernetesiso' case 'ExtensionCustomAction': return 'customaction' case 'SystemVm': case 'PhysicalNetwork': case 'Backup': case 'SecurityGroup': case 'StoragePool': case 'ImageStore': case 'Template': case 'Iso': case 'Host': case 'Volume': case 'Account': case 'Snapshot': case 'Project': case 'Domain': case 'DiskOffering': case 'NetworkOffering': case 'VpcOffering': case 'BackupOffering': case 'Zone': case 'Vpc': case 'VmSnapshot': case 'Pod': case 'Cluster': case 'Role': case 'AffinityGroup': case 'VpnCustomerGateway': case 'AutoScaleVmGroup': case 'QuotaTariff': case 'GuestOsCategory': case 'Extension': return resourceType.toLowerCase() } return '' } app.config.globalProperties.$getIconFromResourceType = function (resourceType) { var routePath = this.$getRouteFromResourceType(resourceType) if (!routePath) return '' var route = this.$router.resolve('/' + routePath) if (routePath === 'kubernetes') { return route?.meta?.icon[0] } return route?.meta?.icon || '' } } } export const apiMetaUtilPlugin = { install (app) { app.config.globalProperties.$getApiParams = function () { var apiParams = {} for (var argument of arguments) { var apiConfig = this.$store.getters.apis[argument] || {} if (apiConfig && apiConfig.params) { apiConfig.params.forEach(param => { apiParams[param.name] = param }) } } return apiParams } } } export const localesPlugin = { install (app) { app.config.globalProperties.$t = i18n.global.t } } const KiB = 1024 const MiB = 1024 * KiB const GiB = 1024 * MiB const TiB = 1024 * GiB export const fileSizeUtilPlugin = { install (app) { app.config.globalProperties.$bytesToGiB = function (bytes) { if (bytes == null || bytes === 0) { return 0 } return (bytes / GiB).toFixed(2) } app.config.globalProperties.$bytesToHumanReadableSize = function (bytes) { if (bytes == null) { return '' } if (bytes < KiB && bytes >= 0) { return bytes + ' bytes' } if (bytes < MiB) { return (bytes / KiB).toFixed(2) + ' KiB' } else if (bytes < GiB) { return (bytes / MiB).toFixed(2) + ' MiB' } else if (bytes < TiB) { return (bytes / GiB).toFixed(2) + ' GiB' } else { return (bytes / TiB).toFixed(2) + ' TiB' } } } } function isBase64 (str) { try { const decoded = new TextDecoder().decode(Uint8Array.from(atob(str), c => c.charCodeAt(0))) return btoa(decoded) === str } catch (err) { return false } } export const genericUtilPlugin = { install (app) { app.config.globalProperties.$isValidUuid = function (uuid) { const regexExp = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi return regexExp.test(uuid) } app.config.globalProperties.$toBase64AndURIEncoded = function (text) { if (isBase64(text)) { return text } return encodeURI(btoa(unescape(encodeURIComponent(text)))) } } } export function createPathBasedOnVmType (vmtype, virtualmachineid) { let path = '' switch (vmtype) { case 'ConsoleProxy': case 'SecondaryStorageVm': path = '/systemvm/' break case 'DomainRouter': path = '/router/' break default: path = '/vm/' } return path + virtualmachineid } export const dialogUtilPlugin = { install (app) { app.config.globalProperties.$resetConfigurationValueConfirm = function (configRecord, callback) { Modal.confirm({ title: i18n.global.t('label.reset.config.value'), content: `${i18n.global.t('message.confirm.reset.configuration.value').replace('%x', configRecord.name)}`, okText: i18n.global.t('label.yes'), cancelText: i18n.global.t('label.no'), okType: 'primary', onOk: () => callback(configRecord) }) } } } export const cpuArchitectureUtilPlugin = { install (app) { app.config.globalProperties.$fetchCpuArchitectureTypes = function () { const architectures = [ { id: 'x86_64', name: 'Intel/AMD 64 bits (x86_64)' }, { id: 'aarch64', name: 'ARM 64 bits (aarch64)' } ] return architectures.map(item => ({ ...item, description: item.name })) } } } export const imagesUtilPlugin = { install (app) { app.config.globalProperties.$fetchTemplateTypes = function (hypervisor) { const baseTypes = ['USER'] if (hypervisor === 'External') { return baseTypes.map(type => ({ id: type, name: type, description: type })) } baseTypes.push('VNF') const adminTypes = ['SYSTEM', 'BUILTIN', 'ROUTING'] const types = [...baseTypes] if (store.getters.userInfo?.roletype === 'Admin') { types.push(...adminTypes) } return types.map(type => ({ id: type, name: type, description: type })) } } } export const extensionsUtilPlugin = { install (app) { app.config.globalProperties.$fetchCustomActionRoleTypes = function () { const roleTypes = [] const roleTypesList = ['Admin', 'Resource Admin', 'Domain Admin', 'User'] roleTypesList.forEach((item) => { roleTypes.push({ id: item.replace(' ', ''), description: item }) }) return roleTypes } } } export const backupUtilPlugin = { install (app) { app.config.globalProperties.$isBackupProviderSupportsQuiesceVm = function (provider) { if (!provider && typeof provider !== 'string') { return false } return ['nas'].includes(provider.toLowerCase()) } } }