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
@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
@@ -290,6 +291,51 @@ func (c *minioClient) DownloadURL(objectKey string) (string, error) {
return u.String(), nil
}
func (c *minioClient) PresignedPutURL(
ctx context.Context,
objectKey string,
contentType string,
contentMD5Base64 string,
ttl time.Duration,
) (*PresignedPutResult, error) {
if err := c.Validate(); err != nil {
return nil, err
}
objectKey = normalizeObjectKeyPath(objectKey)
if objectKey == "" {
return nil, fmt.Errorf("object key is required")
}
if ttl <= 0 {
ttl = c.cfg.SignedURLTTL
}
if ttl <= 0 {
ttl = 30 * time.Minute
}
headers := http.Header{}
if contentType = strings.TrimSpace(contentType); contentType != "" {
headers.Set("Content-Type", contentType)
}
if contentMD5Base64 = strings.TrimSpace(contentMD5Base64); contentMD5Base64 != "" {
headers.Set("Content-MD5", contentMD5Base64)
}
var u *url.URL
var err error
if len(headers) > 0 {
u, err = c.client.PresignHeader(ctx, http.MethodPut, c.bucket, objectKey, ttl, nil, headers)
} else {
u, err = c.client.PresignedPutObject(ctx, c.bucket, objectKey, ttl)
}
if err != nil {
return nil, fmt.Errorf("presign minio put url: %w", err)
}
return &PresignedPutResult{
URL: u.String(),
Headers: headers,
ExpiresAt: time.Now().UTC().Add(ttl),
}, nil
}
func (c *minioClient) List(ctx context.Context, in ListInput) (*ListResult, error) {
if err := c.Validate(); err != nil {
return nil, err