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
@@ -149,6 +149,60 @@ func TestIsComplianceInvalidArticleVersionError(t *testing.T) {
}
}
func TestBuildListPublishTasksByStatusesQueryHidesDeletedPublishRecords(t *testing.T) {
t.Parallel()
query, args := buildListPublishTasksByStatusesQuery(
publishTaskListOwner{TenantID: 7, WorkspaceID: 11, UserID: 23},
[]string{"succeeded", "failed"},
"合肥",
10,
20,
"t.updated_at DESC",
)
normalized := normalizeSQLForDesktopTaskTest(query)
for _, fragment := range []string{
"NOT (t.payload ? 'publish_record_id') OR EXISTS",
"FROM publish_records pr",
"pr.tenant_id = t.tenant_id",
"t.payload->>'publish_record_id')::bigint",
"pr.deleted_at IS NULL",
"LIMIT $6 OFFSET $7",
} {
if !strings.Contains(normalized, fragment) {
t.Fatalf("list publish tasks query missing %q: %s", fragment, normalized)
}
}
if len(args) != 7 {
t.Fatalf("args len = %d, want 7", len(args))
}
}
func TestBuildCountPublishTasksByStatusesQueryHidesDeletedPublishRecords(t *testing.T) {
t.Parallel()
query, args := buildCountPublishTasksByStatusesQuery(
publishTaskListOwner{TenantID: 7, WorkspaceID: 11, UserID: 23},
[]string{"succeeded", "failed"},
"",
)
normalized := normalizeSQLForDesktopTaskTest(query)
for _, fragment := range []string{
"NOT (t.payload ? 'publish_record_id') OR EXISTS",
"FROM publish_records pr",
"pr.deleted_at IS NULL",
} {
if !strings.Contains(normalized, fragment) {
t.Fatalf("count publish tasks query missing %q: %s", fragment, normalized)
}
}
if len(args) != 5 {
t.Fatalf("args len = %d, want 5", len(args))
}
}
func recoverDesktopTaskSelectColumns(query string) []string {
re := regexp.MustCompile(`(?is)select\s+(.*?)\s+from\s+desktop_tasks`)
match := re.FindStringSubmatch(query)
@@ -165,3 +219,7 @@ func recoverDesktopTaskSelectColumns(query string) []string {
}
return columns
}
func normalizeSQLForDesktopTaskTest(sql string) string {
return strings.Join(strings.Fields(sql), " ")
}