mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-25 23:21:29 +03:00
parent
64dc6deac6
commit
7bfcf01c67
@ -39,7 +39,7 @@ export const getSeverityFromData = data =>
|
||||
export const getMessageInfo = (data = '') => {
|
||||
try {
|
||||
const { message, timestamp, severity } = JSON.parse(data)
|
||||
const decryptMessage = atob(message)
|
||||
const decryptMessage = decodeURIComponent(escape(atob(message)))
|
||||
|
||||
return { timestamp, severity, message: decryptMessage }
|
||||
} catch {
|
||||
|
@ -0,0 +1,54 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/schema'
|
||||
import { Step } from 'client/utils'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'configuration'
|
||||
|
||||
const Content = () => (
|
||||
<FormWithSchema
|
||||
cy='export-app-configuration'
|
||||
id={STEP_ID}
|
||||
fields={FIELDS}
|
||||
/>
|
||||
)
|
||||
|
||||
/**
|
||||
* Step to configure the marketplace app.
|
||||
*
|
||||
* @returns {Step} Configuration step
|
||||
*/
|
||||
const ConfigurationStep = () => ({
|
||||
id: STEP_ID,
|
||||
label: T.Configuration,
|
||||
resolver: SCHEMA,
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: Content
|
||||
})
|
||||
|
||||
Content.propTypes = {
|
||||
data: PropTypes.any,
|
||||
setFormData: PropTypes.func,
|
||||
nics: PropTypes.array
|
||||
}
|
||||
|
||||
export default ConfigurationStep
|
@ -0,0 +1,66 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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 { boolean, string, lazy, object, ObjectSchema } from 'yup'
|
||||
|
||||
import { Field, getValidationFromFields } from 'client/utils'
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
|
||||
/** @type {Field} Name field */
|
||||
const NAME_FIELD = {
|
||||
name: 'name',
|
||||
label: T.Name,
|
||||
tooltip: T.ExportAppNameConcept,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: lazy((_, { context }) => {
|
||||
return string()
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => context.app.NAME)
|
||||
})
|
||||
}
|
||||
|
||||
/** @type {Field} Template name field */
|
||||
const TEMPLATE_NAME_FIELD = {
|
||||
name: 'vmname',
|
||||
label: T.VMTemplate,
|
||||
tooltip: T.ExportTemplateNameConcept,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: lazy((_, { context }) => {
|
||||
return string()
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => context.app.NAME)
|
||||
})
|
||||
}
|
||||
|
||||
/** @type {Field} Associate field */
|
||||
const ASSOCIATED_FIELD = {
|
||||
name: 'associated',
|
||||
label: T.DontAssociateApp,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
/** @type {Field[]} List of fields */
|
||||
export const FIELDS = [
|
||||
NAME_FIELD,
|
||||
TEMPLATE_NAME_FIELD,
|
||||
ASSOCIATED_FIELD
|
||||
]
|
||||
|
||||
/** @type {ObjectSchema} Advanced options schema */
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,76 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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 { useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
||||
import { DatastoresTable } from 'client/components/Tables'
|
||||
import { SCHEMA } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema'
|
||||
import { Step, decodeBase64 } from 'client/utils'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'datastore'
|
||||
|
||||
const Content = ({ data, app }) => {
|
||||
const { NAME } = data?.[0] ?? {}
|
||||
const { setValue } = useFormContext()
|
||||
|
||||
const isKernelType = useMemo(() => {
|
||||
const appTemplate = String(decodeBase64(app?.TEMPLATE?.APPTEMPLATE64, ''))
|
||||
return appTemplate.includes('TYPE="KERNEL"')
|
||||
}, [])
|
||||
|
||||
const handleSelectedRows = rows => {
|
||||
const { original = {} } = rows?.[0] ?? {}
|
||||
|
||||
setValue(STEP_ID, original.ID !== undefined ? [original] : [])
|
||||
}
|
||||
|
||||
return (
|
||||
<DatastoresTable
|
||||
singleSelect
|
||||
onlyGlobalSearch
|
||||
onlyGlobalSelectedRows
|
||||
getRowId={row => String(row.NAME)}
|
||||
initialState={{
|
||||
selectedRowIds: { [NAME]: true },
|
||||
filters: [{ id: 'TYPE', value: isKernelType ? 'FILE' : 'IMAGE' }]
|
||||
}}
|
||||
onSelectedRowsChange={handleSelectedRows}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to select the Datastore.
|
||||
*
|
||||
* @param {object} app - Marketplace App resource
|
||||
* @returns {Step} Datastore step
|
||||
*/
|
||||
const DatastoreStep = app => ({
|
||||
id: STEP_ID,
|
||||
label: T.SelectDatastore,
|
||||
resolver: SCHEMA,
|
||||
content: props => Content({ ...props, app })
|
||||
})
|
||||
|
||||
Content.propTypes = {
|
||||
data: PropTypes.any,
|
||||
setFormData: PropTypes.func,
|
||||
app: PropTypes.object
|
||||
}
|
||||
|
||||
export default DatastoreStep
|
@ -0,0 +1,24 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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 { array, object, ArraySchema } from 'yup'
|
||||
|
||||
/** @type {ArraySchema} Datastore table schema */
|
||||
export const SCHEMA = array(object())
|
||||
.min(1)
|
||||
.max(1)
|
||||
.required()
|
||||
.ensure()
|
||||
.default(() => [])
|
@ -0,0 +1,38 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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 BasicConfiguration, { STEP_ID as BASIC_ID } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration'
|
||||
import DatastoresTable, { STEP_ID as DATASTORE_ID } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable'
|
||||
import { createSteps } from 'client/utils'
|
||||
|
||||
const Steps = createSteps(
|
||||
[BasicConfiguration, DatastoresTable],
|
||||
{
|
||||
transformInitialValue: (app, schema) => schema.cast({}, { context: { app } }),
|
||||
transformBeforeSubmit: formData => {
|
||||
const {
|
||||
[BASIC_ID]: configuration,
|
||||
[DATASTORE_ID]: [datastore] = []
|
||||
} = formData
|
||||
|
||||
return {
|
||||
datastore: datastore?.ID,
|
||||
...configuration
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export default Steps
|
@ -0,0 +1,16 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
export { default } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps'
|
@ -0,0 +1,20 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed 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 ExportForm from 'client/components/Forms/MarketplaceApp/ExportForm'
|
||||
|
||||
export {
|
||||
ExportForm
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
import { string, boolean, ref, ObjectSchema } from 'yup'
|
||||
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
import { Field, getObjectSchemaFromFields } from 'client/utils'
|
||||
import { Field, getObjectSchemaFromFields, decodeBase64 } from 'client/utils'
|
||||
|
||||
const switchField = {
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
@ -86,7 +86,7 @@ export const START_SCRIPT = {
|
||||
.when(
|
||||
'$extra.CONTEXT.START_SCRIPT_BASE64',
|
||||
(scriptEncoded, schema) => scriptEncoded
|
||||
? schema.default(() => decodeURIComponent(escape(atob(scriptEncoded))))
|
||||
? schema.default(() => decodeBase64(scriptEncoded))
|
||||
: schema
|
||||
),
|
||||
grid: { md: 12 },
|
||||
|
@ -37,7 +37,13 @@ export default [
|
||||
{
|
||||
Header: 'Type',
|
||||
id: 'TYPE',
|
||||
accessor: row => DatastoreModel.getType(row)?.name
|
||||
accessor: row => DatastoreModel.getType(row),
|
||||
Filter: ({ column }) => CategoryFilter({
|
||||
column,
|
||||
multiple: true,
|
||||
title: 'Type'
|
||||
}),
|
||||
filter: 'includesValue'
|
||||
},
|
||||
{
|
||||
Header: 'Clusters IDs',
|
||||
|
@ -44,7 +44,7 @@ const Row = ({ original, value, ...props }) => {
|
||||
</Typography>
|
||||
<span className={classes.labels}>
|
||||
{LOCK && <Lock />}
|
||||
<StatusChip text={TYPE?.name} />
|
||||
<StatusChip text={TYPE} />
|
||||
</span>
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
|
@ -16,12 +16,20 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useMemo } from 'react'
|
||||
// import { useHistory } from 'react-router-dom'
|
||||
import { RefreshDouble } from 'iconoir-react'
|
||||
import {
|
||||
RefreshDouble,
|
||||
CloudDownload
|
||||
} from 'iconoir-react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { useMarketplaceAppApi } from 'client/features/One'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import {
|
||||
ExportForm
|
||||
} from 'client/components/Forms/MarketplaceApp'
|
||||
|
||||
import { createActions } from 'client/components/Tables/Enhanced/Utils'
|
||||
// import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { T, MARKETPLACE_APP_ACTIONS } from 'client/constants'
|
||||
@ -46,7 +54,8 @@ MessageToConfirmAction.displayName = 'MessageToConfirmAction'
|
||||
|
||||
const Actions = () => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { getMarketplaceApps } = useMarketplaceAppApi()
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
const { getMarketplaceApps, exportApp } = useMarketplaceAppApi()
|
||||
|
||||
const marketplaceAppActions = useMemo(() => createActions({
|
||||
filters: getResourceView('MARKETPLACE-APP')?.actions,
|
||||
@ -58,17 +67,25 @@ const Actions = () => {
|
||||
action: async () => {
|
||||
await getMarketplaceApps()
|
||||
}
|
||||
},
|
||||
{
|
||||
accessor: MARKETPLACE_APP_ACTIONS.EXPORT,
|
||||
tooltip: T.ImportIntoDatastore,
|
||||
selected: { max: 1 },
|
||||
icon: CloudDownload,
|
||||
options: [{
|
||||
dialogProps: { title: T.DownloadAppToOpenNebula },
|
||||
form: rows => {
|
||||
const app = rows?.map(({ original }) => original)[0]
|
||||
return ExportForm(app, app)
|
||||
},
|
||||
onSubmit: async (formData, rows) => {
|
||||
const appId = rows?.[0]?.original?.ID
|
||||
const response = await exportApp(appId, formData)
|
||||
enqueueSuccess(response)
|
||||
}
|
||||
/* {
|
||||
accessor: MARKETPLACE_APP_ACTIONS.CREATE_DIALOG,
|
||||
tooltip: T.CreateMarketApp,
|
||||
icon: AddSquare,
|
||||
action: () => {
|
||||
const path = PATH.STORAGE.MARKETPLACE_APPS.CREATE
|
||||
|
||||
history.push(path)
|
||||
}]
|
||||
}
|
||||
} */
|
||||
]
|
||||
}), [view])
|
||||
|
||||
|
@ -19,21 +19,16 @@ import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material'
|
||||
import { NavArrowDown as ExpandMoreIcon } from 'iconoir-react'
|
||||
|
||||
import { TabContext } from 'client/components/Tabs/TabProvider'
|
||||
import { decodeBase64 } from 'client/utils'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const parseTemplateInB64 = template => {
|
||||
try {
|
||||
return decodeURIComponent(escape(atob(template)))
|
||||
} catch (e) { return {} }
|
||||
}
|
||||
|
||||
const AppTemplateTab = () => {
|
||||
const { data: marketplaceApp = {} } = useContext(TabContext)
|
||||
const { TEMPLATE: { APPTEMPLATE64, VMTEMPLATE64 } } = marketplaceApp
|
||||
|
||||
const appTemplate = useMemo(() => parseTemplateInB64(APPTEMPLATE64), [APPTEMPLATE64])
|
||||
const vmTemplate = useMemo(() => parseTemplateInB64(VMTEMPLATE64), [VMTEMPLATE64])
|
||||
const appTemplate = useMemo(() => decodeBase64(APPTEMPLATE64), [APPTEMPLATE64])
|
||||
const vmTemplate = useMemo(() => decodeBase64(VMTEMPLATE64), [VMTEMPLATE64])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -16,21 +16,8 @@
|
||||
import * as STATES from 'client/constants/states'
|
||||
import COLOR from 'client/constants/color'
|
||||
|
||||
/** @type {{name: string, shortName: string}} Datastore type information */
|
||||
export const DATASTORE_TYPES = [
|
||||
{
|
||||
name: 'IMAGE',
|
||||
shortName: 'img'
|
||||
},
|
||||
{
|
||||
name: 'SYSTEM',
|
||||
shortName: 'sys'
|
||||
},
|
||||
{
|
||||
name: 'FILE',
|
||||
shortName: 'fil'
|
||||
}
|
||||
]
|
||||
/** @type {string[]} Datastore type information */
|
||||
export const DATASTORE_TYPES = ['IMAGE', 'SYSTEM', 'FILE']
|
||||
|
||||
/** @type {STATES.StateInfo[]} Datastore states */
|
||||
export const DATASTORE_STATES = [
|
||||
|
@ -16,5 +16,6 @@
|
||||
export const MARKETPLACE_APP_ACTIONS = {
|
||||
REFRESH: 'refresh',
|
||||
CREATE_DIALOG: 'create_dialog',
|
||||
RENAME: 'rename'
|
||||
RENAME: 'rename',
|
||||
EXPORT: 'export'
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ module.exports = {
|
||||
SelectNetwork: 'Select a network',
|
||||
SelectRequest: 'Select request',
|
||||
SelectVmTemplate: 'Select a VM Template',
|
||||
SelectDatastore: 'Select a Datastore to store the resource',
|
||||
Share: 'Share',
|
||||
Show: 'Show',
|
||||
ShowAll: 'Show all',
|
||||
@ -587,6 +588,11 @@ module.exports = {
|
||||
/* Marketplace App - general */
|
||||
Version: 'Version',
|
||||
AppTemplate: 'App Template',
|
||||
ImportIntoDatastore: 'Import into Datastore',
|
||||
DownloadAppToOpenNebula: 'Download App to OpenNebula',
|
||||
ExportAppNameConcept: 'Name that the resource will get for description purposes',
|
||||
ExportTemplateNameConcept: 'The following template will be created in OpenNebula and the previous images will be referenced in the disks',
|
||||
DontAssociateApp: 'Do not import/export associated VM templates/images',
|
||||
|
||||
/* User inputs */
|
||||
UserInputs: 'User Inputs',
|
||||
|
@ -30,3 +30,5 @@ export const getMarketplaceApps = createAction(
|
||||
marketplaceAppService.getMarketplaceApps,
|
||||
response => ({ [RESOURCES.app]: response })
|
||||
)
|
||||
|
||||
export const exportApp = createAction(`${APP}/export`, marketplaceAppService.export)
|
||||
|
@ -35,6 +35,7 @@ export const useMarketplaceAppApi = () => {
|
||||
|
||||
return {
|
||||
getMarketplaceApp: id => unwrapDispatch(actions.getMarketplaceApp({ id })),
|
||||
getMarketplaceApps: () => unwrapDispatch(actions.getMarketplaceApps())
|
||||
getMarketplaceApps: () => unwrapDispatch(actions.getMarketplaceApps()),
|
||||
exportApp: (id, data) => unwrapDispatch(actions.exportApp({ id, ...data }))
|
||||
}
|
||||
}
|
||||
|
@ -59,5 +59,32 @@ export const marketplaceAppService = ({
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
|
||||
return [res?.data?.MARKETPLACEAPP_POOL?.MARKETPLACEAPP ?? []].flat()
|
||||
},
|
||||
|
||||
/**
|
||||
* Exports the marketplace app to the OpenNebula cloud.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - App id
|
||||
* @param {string} params.name - Image name
|
||||
* @param {string|number} params.datastore - Datastore id or name
|
||||
* @param {string|number} params.file - File datastore id or name
|
||||
* @param {string} params.tag - DockerHub image tag (default latest)
|
||||
* @param {string|number} params.template - Associate with VM template
|
||||
* @param {boolean} params.associated - If `true`, Do not import/export associated VM templates/images
|
||||
* @param {string} params.vmname - The name for the new VM Template, if the App contains one
|
||||
* @returns {number} Template id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
export: async ({ id, ...data }) => {
|
||||
const res = await RestClient.request({
|
||||
url: `/api/marketapp/export/${id}`,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data
|
||||
|
||||
return res?.data
|
||||
}
|
||||
})
|
||||
|
@ -56,10 +56,23 @@ export function sanitize (text, ...values) {
|
||||
return DOMPurify.sanitize(dirty)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an string in base 64.
|
||||
*
|
||||
* @param {string} string - Value to decode
|
||||
* @param {any} defaultValue - Default value if it fails
|
||||
* @returns {any} Decoded value from string in base 64
|
||||
*/
|
||||
export const decodeBase64 = (string, defaultValue = {}) => {
|
||||
try {
|
||||
return decodeURIComponent(escape(atob(string)))
|
||||
} catch (e) { return defaultValue }
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a long string of units into a readable format e.g KB, MB, GB, TB, YB.
|
||||
*
|
||||
* @param {number} value - The quantity of units.
|
||||
* @param {number|string} value - The quantity of units.
|
||||
* @param {string} unit - The unit of value.
|
||||
* @param {number} fractionDigits
|
||||
* - Number of digits after the decimal point. Must be in the range 0 - 20, inclusive
|
||||
@ -67,17 +80,18 @@ export function sanitize (text, ...values) {
|
||||
*/
|
||||
export const prettyBytes = (value, unit = 'KB', fractionDigits = 0) => {
|
||||
const UNITS = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
let ensuredValue = +value
|
||||
|
||||
if (Math.abs(value) === 0) return `${value} ${UNITS[0]}`
|
||||
if (Math.abs(ensuredValue) === 0) return `${value} ${UNITS[0]}`
|
||||
|
||||
let idxUnit = UNITS.indexOf(unit)
|
||||
|
||||
while (value > 1024) {
|
||||
value /= 1024
|
||||
while (ensuredValue > 1024) {
|
||||
ensuredValue /= 1024
|
||||
idxUnit += 1
|
||||
}
|
||||
|
||||
return `${value.toFixed(fractionDigits)} ${UNITS[idxUnit]}`
|
||||
return `${ensuredValue.toFixed(fractionDigits)} ${UNITS[idxUnit]}`
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user