Skip to content

Commit 700f4e0

Browse files
committed
Fix local upload from browser
1 parent a7c2a05 commit 700f4e0

4 files changed

Lines changed: 195 additions & 54 deletions

File tree

ui/public/locales/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,8 @@
16171617
"label.offeringid": "Offering ID",
16181618
"label.offeringtype": "Compute Offering type",
16191619
"label.ok": "OK",
1620+
"label.ssvm.open.cert.page": "Open Certificate Page",
1621+
"label.retry.upload": "Retry Upload",
16201622
"label.only.end.date.and.time": "Only end date and time",
16211623
"label.only.start.date.and.time": "Only start date and time",
16221624
"label.open.documentation": "Open documentation",
@@ -3667,6 +3669,9 @@
36673669
"message.upload.iso.failed.description": "Failed to upload ISO.",
36683670
"message.upload.template.failed.description": "Failed to upload Template",
36693671
"message.upload.volume.failed": "Volume upload failed",
3672+
"message.ssvm.cert.untrusted": "The upload server certificate is not trusted by your browser.",
3673+
"message.ssvm.cert.trust.instructions": "Click 'Open Certificate Page' to open the upload server in a new browser tab. Accept the certificate warning shown by your browser, then come back here and click 'Retry Upload'.",
3674+
"message.ssvm.cert.still.untrusted": "The certificate still appears untrusted. Please accept it in the opened tab and try again.",
36703675
"message.user.not.permitted.api": "User is not permitted to use the API",
36713676
"message.validate.equalto": "Please enter the same value again.",
36723677
"message.validate.max": "Please enter a value less than or equal to {0}.",

ui/src/views/image/RegisterOrUploadIso.vue

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,27 @@
1919
<div
2020
class="form-layout"
2121
@keyup.ctrl.enter="handleSubmit">
22-
<span v-if="uploadPercentage > 0">
22+
<span v-if="uploading">
2323
<loading-outlined />
2424
{{ $t('message.upload.file.processing') }}
2525
<a-progress :percent="uploadPercentage" />
2626
</span>
27+
<div v-else-if="ssvmCertUntrusted" class="ssvm-cert-warning">
28+
<a-alert
29+
type="warning"
30+
show-icon
31+
:message="$t('message.ssvm.cert.untrusted')"
32+
:description="$t('message.ssvm.cert.trust.instructions')" />
33+
<div class="action-button" style="margin-top: 16px">
34+
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
35+
<a :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
36+
<a-button>{{ $t('label.ssvm.open.cert.page') }}</a-button>
37+
</a>
38+
<a-button type="primary" :loading="loading" @click="retryUpload">
39+
{{ $t('label.retry.upload') }}
40+
</a-button>
41+
</div>
42+
</div>
2743
<a-spin :spinning="loading" v-else>
2844
<a-form
2945
:ref="formRef"
@@ -343,9 +359,12 @@ export default {
343359
userdatapolicy: null,
344360
userdatapolicylist: {},
345361
loading: false,
362+
uploading: false,
346363
allowed: false,
347364
uploadParams: null,
348365
uploadPercentage: 0,
366+
ssvmCertUntrusted: false,
367+
ssvmOrigin: '',
349368
currentForm: ['plus-outlined', 'PlusOutlined'].includes(this.action.currentAction.icon) ? 'Create' : 'Upload',
350369
domains: [],
351370
accounts: [],
@@ -489,6 +508,25 @@ export default {
489508
this.form.file = file
490509
return false
491510
},
511+
async probeSsvmCert (origin) {
512+
try {
513+
await fetch(origin, { method: 'HEAD', mode: 'no-cors' })
514+
return true
515+
} catch (e) {
516+
return false
517+
}
518+
},
519+
async retryUpload () {
520+
this.loading = true
521+
const trusted = await this.probeSsvmCert(this.ssvmOrigin)
522+
this.loading = false
523+
if (!trusted) {
524+
this.$message.warning(this.$t('message.ssvm.cert.still.untrusted'))
525+
return
526+
}
527+
this.ssvmCertUntrusted = false
528+
this.handleUpload()
529+
},
492530
handleUpload () {
493531
const { fileList } = this
494532
if (this.fileList.length > 1) {
@@ -502,6 +540,7 @@ export default {
502540
fileList.forEach(file => {
503541
formData.append('files[]', file)
504542
})
543+
this.uploading = true
505544
this.uploadPercentage = 0
506545
axios.post(this.uploadParams.postURL,
507546
formData,
@@ -529,6 +568,8 @@ export default {
529568
description: `${this.$t('message.upload.iso.failed.description')} - ${e}`,
530569
duration: 0
531570
})
571+
}).finally(() => {
572+
this.uploading = false
532573
})
533574
},
534575
handleSubmit (e) {
@@ -583,18 +624,18 @@ export default {
583624
}
584625
params.format = 'ISO'
585626
this.loading = true
586-
api('getUploadParamsForIso', params).then(json => {
627+
api('getUploadParamsForIso', params).then(async json => {
587628
this.uploadParams = (json.postuploadisoresponse && json.postuploadisoresponse.getuploadparams) ? json.postuploadisoresponse.getuploadparams : ''
588-
const response = this.handleUpload()
589629
if (this.userdataid !== null) {
590630
this.linkUserdataToTemplate(this.userdataid, json.postuploadisoresponse.iso[0].id)
591631
}
592-
if (response === 'upload successful') {
593-
this.$notification.success({
594-
message: this.$t('message.success.upload'),
595-
description: this.$t('message.success.upload.iso.description')
596-
})
632+
this.ssvmOrigin = new URL(this.uploadParams.postURL).origin
633+
const trusted = await this.probeSsvmCert(this.ssvmOrigin)
634+
if (!trusted) {
635+
this.ssvmCertUntrusted = true
636+
return
597637
}
638+
this.handleUpload()
598639
}).catch(error => {
599640
this.$notifyError(error)
600641
}).finally(() => {

ui/src/views/image/RegisterOrUploadTemplate.vue

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,27 @@
1919
<div
2020
:class="'form-layout'"
2121
@keyup.ctrl.enter="handleSubmit">
22-
<span v-if="uploadPercentage > 0">
22+
<span v-if="uploading">
2323
<loading-outlined />
2424
{{ $t('message.upload.file.processing') }}
2525
<a-progress :percent="uploadPercentage" />
2626
</span>
27+
<div v-else-if="ssvmCertUntrusted" class="ssvm-cert-warning">
28+
<a-alert
29+
type="warning"
30+
show-icon
31+
:message="$t('message.ssvm.cert.untrusted')"
32+
:description="$t('message.ssvm.cert.trust.instructions')" />
33+
<div class="action-button" style="margin-top: 16px">
34+
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
35+
<a :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
36+
<a-button>{{ $t('label.ssvm.open.cert.page') }}</a-button>
37+
</a>
38+
<a-button type="primary" :loading="loading" @click="retryUpload">
39+
{{ $t('label.retry.upload') }}
40+
</a-button>
41+
</div>
42+
</div>
2743
<a-spin :spinning="loading" v-else>
2844
<a-form
2945
:ref="formRef"
@@ -497,6 +513,8 @@ export default {
497513
uploadPercentage: 0,
498514
uploading: false,
499515
fileList: [],
516+
ssvmCertUntrusted: false,
517+
ssvmOrigin: '',
500518
zones: {},
501519
defaultZone: '',
502520
hyperVisor: {},
@@ -610,12 +628,32 @@ export default {
610628
this.form.file = file
611629
return false
612630
},
631+
async probeSsvmCert (origin) {
632+
try {
633+
await fetch(origin, { method: 'HEAD', mode: 'no-cors' })
634+
return true
635+
} catch (e) {
636+
return false
637+
}
638+
},
639+
async retryUpload () {
640+
this.loading = true
641+
const trusted = await this.probeSsvmCert(this.ssvmOrigin)
642+
this.loading = false
643+
if (!trusted) {
644+
this.$message.warning(this.$t('message.ssvm.cert.still.untrusted'))
645+
return
646+
}
647+
this.ssvmCertUntrusted = false
648+
this.handleUpload()
649+
},
613650
handleUpload () {
614651
const { fileList } = this
615652
const formData = new FormData()
616653
fileList.forEach(file => {
617654
formData.append('files[]', file)
618655
})
656+
this.uploading = true
619657
this.uploadPercentage = 0
620658
axios.post(this.uploadParams.postURL,
621659
formData,
@@ -639,6 +677,8 @@ export default {
639677
this.closeAction()
640678
}).catch(e => {
641679
this.$notifyError(e)
680+
}).finally(() => {
681+
this.uploading = false
642682
})
643683
},
644684
fetchCustomHypervisorName () {
@@ -1124,12 +1164,18 @@ export default {
11241164
duration: 0
11251165
})
11261166
}
1127-
api('getUploadParamsForTemplate', params).then(json => {
1167+
api('getUploadParamsForTemplate', params).then(async json => {
11281168
this.uploadParams = (json.postuploadtemplateresponse && json.postuploadtemplateresponse.getuploadparams) ? json.postuploadtemplateresponse.getuploadparams : ''
1129-
this.handleUpload()
11301169
if (this.userdataid !== null) {
11311170
this.linkUserdataToTemplate(this.userdataid, json.postuploadtemplateresponse.template[0].id)
11321171
}
1172+
this.ssvmOrigin = new URL(this.uploadParams.postURL).origin
1173+
const trusted = await this.probeSsvmCert(this.ssvmOrigin)
1174+
if (!trusted) {
1175+
this.ssvmCertUntrusted = true
1176+
return
1177+
}
1178+
this.handleUpload()
11331179
}).catch(error => {
11341180
this.$notifyError(error)
11351181
}).finally(() => {

ui/src/views/storage/UploadLocalVolume.vue

Lines changed: 92 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,27 @@
1717

1818
<template>
1919
<div class="form-layout" v-ctrl-enter="handleSubmit">
20-
<span v-if="uploadPercentage > 0">
20+
<span v-if="uploading">
2121
<loading-outlined />
2222
{{ $t('message.upload.file.processing') }}
2323
<a-progress :percent="uploadPercentage" />
2424
</span>
25+
<div v-else-if="ssvmCertUntrusted" class="ssvm-cert-warning">
26+
<a-alert
27+
type="warning"
28+
show-icon
29+
:message="$t('message.ssvm.cert.untrusted')"
30+
:description="$t('message.ssvm.cert.trust.instructions')" />
31+
<div class="action-button" style="margin-top: 16px">
32+
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
33+
<a :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
34+
<a-button>{{ $t('label.ssvm.open.cert.page') }}</a-button>
35+
</a>
36+
<a-button type="primary" :loading="loading" @click="retryUpload">
37+
{{ $t('label.retry.upload') }}
38+
</a-button>
39+
</div>
40+
</div>
2541
<a-spin :spinning="loading" v-else>
2642
<a-form
2743
:ref="formRef"
@@ -178,7 +194,10 @@ export default {
178194
customDiskOffering: false,
179195
isCustomizedDiskIOps: false,
180196
loading: false,
181-
uploadPercentage: 0
197+
uploading: false,
198+
uploadPercentage: 0,
199+
ssvmCertUntrusted: false,
200+
ssvmOrigin: ''
182201
}
183202
},
184203
beforeCreate () {
@@ -267,6 +286,70 @@ export default {
267286
this.form.account = accountName
268287
this.account = accountName
269288
},
289+
async probeSsvmCert (origin) {
290+
try {
291+
await fetch(origin, { method: 'HEAD', mode: 'no-cors' })
292+
return true
293+
} catch (e) {
294+
return false
295+
}
296+
},
297+
async retryUpload () {
298+
this.loading = true
299+
const trusted = await this.probeSsvmCert(this.ssvmOrigin)
300+
this.loading = false
301+
if (!trusted) {
302+
this.$message.warning(this.$t('message.ssvm.cert.still.untrusted'))
303+
return
304+
}
305+
this.ssvmCertUntrusted = false
306+
this.handleUpload()
307+
},
308+
handleUpload () {
309+
const { fileList } = this
310+
if (this.fileList.length > 1) {
311+
this.$notification.error({
312+
message: this.$t('message.upload.volume.failed'),
313+
description: this.$t('message.upload.file.limit'),
314+
duration: 0
315+
})
316+
}
317+
const formData = new FormData()
318+
fileList.forEach(file => {
319+
formData.append('files[]', file)
320+
})
321+
this.uploading = true
322+
this.uploadPercentage = 0
323+
axios.post(this.uploadParams.postURL,
324+
formData,
325+
{
326+
headers: {
327+
'content-type': 'multipart/form-data',
328+
'x-signature': this.uploadParams.signature,
329+
'x-expires': this.uploadParams.expires,
330+
'x-metadata': this.uploadParams.metadata
331+
},
332+
onUploadProgress: (progressEvent) => {
333+
this.uploadPercentage = Number(parseFloat(100 * progressEvent.loaded / progressEvent.total).toFixed(1))
334+
},
335+
timeout: 86400000
336+
}).then((json) => {
337+
this.$notification.success({
338+
message: this.$t('message.success.upload'),
339+
description: this.$t('message.success.upload.volume.description')
340+
})
341+
this.closeAction()
342+
}).catch(e => {
343+
this.$notification.error({
344+
message: this.$t('message.upload.failed'),
345+
description: `${this.$t('message.upload.volume.failed')} - ${e}`,
346+
duration: 0
347+
})
348+
}).finally(() => {
349+
this.uploading = false
350+
this.loading = false
351+
})
352+
},
270353
handleSubmit (e) {
271354
e.preventDefault()
272355
if (this.loading) return
@@ -286,49 +369,15 @@ export default {
286369
}
287370
params.domainId = this.domainId
288371
this.loading = true
289-
api('getUploadParamsForVolume', params).then(json => {
372+
api('getUploadParamsForVolume', params).then(async json => {
290373
this.uploadParams = json.postuploadvolumeresponse?.getuploadparams || ''
291-
const { fileList } = this
292-
if (this.fileList.length > 1) {
293-
this.$notification.error({
294-
message: this.$t('message.upload.volume.failed'),
295-
description: this.$t('message.upload.file.limit'),
296-
duration: 0
297-
})
374+
this.ssvmOrigin = new URL(this.uploadParams.postURL).origin
375+
const trusted = await this.probeSsvmCert(this.ssvmOrigin)
376+
if (!trusted) {
377+
this.ssvmCertUntrusted = true
378+
return
298379
}
299-
const formData = new FormData()
300-
fileList.forEach(file => {
301-
formData.append('files[]', file)
302-
})
303-
this.uploadPercentage = 0
304-
axios.post(this.uploadParams.postURL,
305-
formData,
306-
{
307-
headers: {
308-
'content-type': 'multipart/form-data',
309-
'x-signature': this.uploadParams.signature,
310-
'x-expires': this.uploadParams.expires,
311-
'x-metadata': this.uploadParams.metadata
312-
},
313-
onUploadProgress: (progressEvent) => {
314-
this.uploadPercentage = Number(parseFloat(100 * progressEvent.loaded / progressEvent.total).toFixed(1))
315-
},
316-
timeout: 86400000
317-
}).then((json) => {
318-
this.$notification.success({
319-
message: this.$t('message.success.upload'),
320-
description: this.$t('message.success.upload.volume.description')
321-
})
322-
this.closeAction()
323-
}).catch(e => {
324-
this.$notification.error({
325-
message: this.$t('message.upload.failed'),
326-
description: `${this.$t('message.upload.volume.failed')} - ${e}`,
327-
duration: 0
328-
})
329-
}).finally(() => {
330-
this.loading = false
331-
})
380+
this.handleUpload()
332381
}).catch(e => {
333382
this.$notification.error({
334383
message: this.$t('message.upload.failed'),

0 commit comments

Comments
 (0)