feat: Implement direct upload functionality for desktop client releases
Desktop Client Build / Resolve Build Metadata (push) Successful in 52s
Frontend CI / Frontend (push) Successful in 3m49s
Backend CI / Backend (push) Successful in 16m10s
Desktop Client Build / Build Desktop Client (push) Successful in 23m22s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 31s
Desktop Client Build / Resolve Build Metadata (push) Successful in 52s
Frontend CI / Frontend (push) Successful in 3m49s
Backend CI / Backend (push) Successful in 16m10s
Desktop Client Build / Build Desktop Client (push) Successful in 23m22s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 31s
- Added new endpoints for initiating and completing direct uploads of desktop client packages. - Introduced request and response structures for direct upload operations. - Enhanced the upload process to support SHA256 and Content-MD5 validation. - Updated the frontend to reflect changes in upload button states and progress indicators. - Modified object storage clients to support presigned PUT URLs with content type and MD5 headers. - Added tests for direct upload functionality and ensured existing upload processes remain intact.
This commit is contained in:
@@ -200,7 +200,7 @@
|
||||
:show-upload-list="false"
|
||||
accept=".dmg,.pkg,.exe,.msi,.zip,.AppImage,.appimage,.deb,.rpm,.tar.gz,.tgz"
|
||||
>
|
||||
<a-button :loading="uploading">
|
||||
<a-button :loading="uploadButtonLoading('installer')" :disabled="uploading">
|
||||
<template #icon><UploadOutlined /></template>
|
||||
{{ uploadButtonText('installer') }}
|
||||
</a-button>
|
||||
@@ -266,7 +266,7 @@
|
||||
:show-upload-list="false"
|
||||
accept=".zip,.exe,.AppImage,.appimage"
|
||||
>
|
||||
<a-button :loading="updaterUploading">
|
||||
<a-button :loading="uploadButtonLoading('updater')" :disabled="updaterUploading">
|
||||
<template #icon><UploadOutlined /></template>
|
||||
{{ uploadButtonText('updater') }}
|
||||
</a-button>
|
||||
@@ -352,6 +352,7 @@ import {
|
||||
platformLabel,
|
||||
platformOptions,
|
||||
type DesktopClientRelease,
|
||||
type DesktopClientReleaseUploadProgress,
|
||||
type DesktopReleaseArch,
|
||||
type DesktopReleasePlatform,
|
||||
type DesktopReleaseSource,
|
||||
@@ -400,6 +401,8 @@ const uploading = ref(false)
|
||||
const updaterUploading = ref(false)
|
||||
const uploadProgress = ref<number | null>(null)
|
||||
const updaterUploadProgress = ref<number | null>(null)
|
||||
const uploadStage = ref<DesktopClientReleaseUploadProgress['stage'] | null>(null)
|
||||
const updaterUploadStage = ref<DesktopClientReleaseUploadProgress['stage'] | null>(null)
|
||||
|
||||
const enabledRows = computed(() => rows.value.filter((row) => row.enabled).length)
|
||||
const forceRows = computed(() => rows.value.filter((row) => row.force_update).length)
|
||||
@@ -569,9 +572,11 @@ async function beforeUploadReleasePackage(file: File, kind: 'installer' | 'updat
|
||||
if (kind === 'installer') {
|
||||
uploading.value = true
|
||||
uploadProgress.value = 0
|
||||
uploadStage.value = 'hashing'
|
||||
} else {
|
||||
updaterUploading.value = true
|
||||
updaterUploadProgress.value = 0
|
||||
updaterUploadStage.value = 'hashing'
|
||||
}
|
||||
try {
|
||||
const result = await desktopClientReleaseApi.uploadOSS(file, {
|
||||
@@ -584,8 +589,10 @@ async function beforeUploadReleasePackage(file: File, kind: 'installer' | 'updat
|
||||
if (typeof progress.percent !== 'number') return
|
||||
if (kind === 'installer') {
|
||||
uploadProgress.value = progress.percent
|
||||
uploadStage.value = progress.stage ?? null
|
||||
} else {
|
||||
updaterUploadProgress.value = progress.percent
|
||||
updaterUploadStage.value = progress.stage ?? null
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -611,6 +618,8 @@ async function beforeUploadReleasePackage(file: File, kind: 'installer' | 'updat
|
||||
updaterUploading.value = false
|
||||
uploadProgress.value = null
|
||||
updaterUploadProgress.value = null
|
||||
uploadStage.value = null
|
||||
updaterUploadStage.value = null
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -618,20 +627,33 @@ async function beforeUploadReleasePackage(file: File, kind: 'installer' | 'updat
|
||||
function uploadButtonText(kind: 'installer' | 'updater') {
|
||||
const isUploading = kind === 'installer' ? uploading.value : updaterUploading.value
|
||||
const progress = kind === 'installer' ? uploadProgress.value : updaterUploadProgress.value
|
||||
const stage = kind === 'installer' ? uploadStage.value : updaterUploadStage.value
|
||||
const hasUploaded =
|
||||
kind === 'installer' ? Boolean(form.oss_object_key) : Boolean(form.updater_oss_object_key)
|
||||
if (isUploading && typeof progress === 'number' && progress < 100) {
|
||||
if (isUploading && stage === 'hashing' && typeof progress === 'number') {
|
||||
return `计算 SHA256 ${progress}%`
|
||||
}
|
||||
if (isUploading && stage === 'signing') {
|
||||
return '获取直传地址...'
|
||||
}
|
||||
if (isUploading && stage === 'uploading' && typeof progress === 'number' && progress < 100) {
|
||||
return `上传中 ${progress}%`
|
||||
}
|
||||
if (isUploading && progress === 100) {
|
||||
return '校验并写入 OSS...'
|
||||
if (isUploading && (stage === 'verifying' || progress === 100)) {
|
||||
return '确认上传...'
|
||||
}
|
||||
if (isUploading) {
|
||||
return '写入 OSS...'
|
||||
return '准备上传...'
|
||||
}
|
||||
return hasUploaded ? '重新上传' : '选择文件并上传'
|
||||
}
|
||||
|
||||
function uploadButtonLoading(kind: 'installer' | 'updater') {
|
||||
const isUploading = kind === 'installer' ? uploading.value : updaterUploading.value
|
||||
const progress = kind === 'installer' ? uploadProgress.value : updaterUploadProgress.value
|
||||
return isUploading && progress !== 100
|
||||
}
|
||||
|
||||
function beforeUploadInstallerPackage(file: File) {
|
||||
return beforeUploadReleasePackage(file, 'installer')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user