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

- 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:
2026-06-20 12:44:28 +08:00
parent f26802c76e
commit 62d0b22988
15 changed files with 884 additions and 58 deletions
@@ -50,6 +50,30 @@ type desktopClientReleaseUploadInitRequest struct {
ChunkSizeBytes int64 `json:"chunk_size_bytes"`
}
type desktopClientReleaseDirectUploadInitRequest struct {
Platform string `json:"platform" binding:"required"`
Arch string `json:"arch"`
Channel string `json:"channel"`
Version string `json:"version" binding:"required"`
Kind string `json:"kind"`
FileName string `json:"file_name" binding:"required"`
FileSizeBytes int64 `json:"file_size_bytes" binding:"required"`
SHA256 string `json:"sha256" binding:"required"`
ContentMD5 string `json:"content_md5"`
}
type desktopClientReleaseDirectUploadCompleteRequest struct {
Platform string `json:"platform" binding:"required"`
Arch string `json:"arch"`
Channel string `json:"channel"`
Version string `json:"version" binding:"required"`
Kind string `json:"kind"`
OSSObjectKey string `json:"oss_object_key" binding:"required"`
FileName string `json:"file_name" binding:"required"`
FileSizeBytes int64 `json:"file_size_bytes" binding:"required"`
SHA256 string `json:"sha256" binding:"required"`
}
func listDesktopClientReleasesHandler(svc *app.DesktopClientReleaseService) gin.HandlerFunc {
return func(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
@@ -208,6 +232,58 @@ func uploadDesktopClientReleaseHandler(svc *app.DesktopClientReleaseService) gin
}
}
func initiateDesktopClientReleaseDirectUploadHandler(svc *app.DesktopClientReleaseService) gin.HandlerFunc {
return func(c *gin.Context) {
var body desktopClientReleaseDirectUploadInitRequest
if err := c.ShouldBindJSON(&body); err != nil {
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
return
}
result, err := svc.InitiateDirectUpload(c.Request.Context(), app.DesktopClientReleaseDirectUploadInitInput{
Platform: body.Platform,
Arch: body.Arch,
Channel: body.Channel,
Version: body.Version,
Kind: body.Kind,
FileName: body.FileName,
FileSizeBytes: body.FileSizeBytes,
SHA256: body.SHA256,
ContentMD5: body.ContentMD5,
})
if err != nil {
response.Error(c, err)
return
}
response.Success(c, result)
}
}
func completeDesktopClientReleaseDirectUploadHandler(svc *app.DesktopClientReleaseService) gin.HandlerFunc {
return func(c *gin.Context) {
var body desktopClientReleaseDirectUploadCompleteRequest
if err := c.ShouldBindJSON(&body); err != nil {
response.Error(c, response.ErrBadRequest(40000, "invalid_payload", err.Error()))
return
}
result, err := svc.CompleteDirectUpload(c.Request.Context(), app.DesktopClientReleaseDirectUploadCompleteInput{
Platform: body.Platform,
Arch: body.Arch,
Channel: body.Channel,
Version: body.Version,
Kind: body.Kind,
OSSObjectKey: body.OSSObjectKey,
FileName: body.FileName,
FileSizeBytes: body.FileSizeBytes,
SHA256: body.SHA256,
})
if err != nil {
response.Error(c, err)
return
}
response.Success(c, result)
}
}
func initiateDesktopClientReleaseUploadHandler(svc *app.DesktopClientReleaseService) gin.HandlerFunc {
return func(c *gin.Context) {
var body desktopClientReleaseUploadInitRequest