feat: add downloadable release management for desktop client
- Extend DesktopClientReleaseCheckResponse with platform, arch, and channel fields. - Introduce DesktopClientDownloadableRelease and DesktopClientDownloadableReleaseListResponse interfaces for managing downloadable releases. - Implement ListDownloadable method in DesktopClientReleaseService to retrieve downloadable releases based on the specified channel. - Update DesktopClientReleaseCheckResult to include platform, arch, and channel information. - Add resolveInstallerDownloadURL method to handle download URL resolution for releases. - Create new endpoint in DesktopReleaseHandler for listing downloadable releases. - Update router to include the new downloadable releases route. - Enhance tests to cover the new functionality for resolving download URLs and listing downloadable releases.
This commit is contained in:
@@ -100,6 +100,29 @@ type DesktopClientReleaseListResult struct {
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
type DesktopClientDownloadableRelease struct {
|
||||
ID int64 `json:"id"`
|
||||
Platform string `json:"platform"`
|
||||
Arch string `json:"arch"`
|
||||
Channel string `json:"channel"`
|
||||
Version string `json:"version"`
|
||||
DownloadURL *string `json:"download_url"`
|
||||
FileName *string `json:"file_name"`
|
||||
FileSizeBytes *int64 `json:"file_size_bytes"`
|
||||
SHA256 *string `json:"sha256"`
|
||||
ReleaseNotes *string `json:"release_notes"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ForceUpdate bool `json:"force_update"`
|
||||
MinSupportedVersion *string `json:"min_supported_version"`
|
||||
DownloadSource string `json:"download_source"`
|
||||
OSSObjectKey *string `json:"oss_object_key"`
|
||||
}
|
||||
|
||||
type DesktopClientDownloadableReleaseListResult struct {
|
||||
Items []DesktopClientDownloadableRelease `json:"items"`
|
||||
}
|
||||
|
||||
type DesktopClientReleaseInput struct {
|
||||
Platform string
|
||||
Arch string
|
||||
@@ -151,6 +174,9 @@ type DesktopClientReleaseCheckResult struct {
|
||||
UpdateAvailable bool `json:"update_available"`
|
||||
LatestVersion string `json:"latest_version"`
|
||||
CurrentVersion string `json:"current_version"`
|
||||
Platform string `json:"platform"`
|
||||
Arch string `json:"arch"`
|
||||
Channel string `json:"channel"`
|
||||
MinSupportedVersion *string `json:"min_supported_version"`
|
||||
ForceUpdate bool `json:"force_update"`
|
||||
DownloadSource string `json:"download_source"`
|
||||
@@ -241,6 +267,55 @@ func (s *DesktopClientReleaseService) List(ctx context.Context, in DesktopClient
|
||||
return &DesktopClientReleaseListResult{Items: views, Total: total, Page: page, Size: size}, nil
|
||||
}
|
||||
|
||||
func (s *DesktopClientReleaseService) ListDownloadable(ctx context.Context, channel string) (*DesktopClientDownloadableReleaseListResult, error) {
|
||||
enabled := true
|
||||
normalizedChannel := normalizeDesktopReleaseEnum(channel)
|
||||
if normalizedChannel == "" {
|
||||
normalizedChannel = "stable"
|
||||
}
|
||||
if !isValidDesktopReleaseToken(normalizedChannel) {
|
||||
return nil, response.ErrBadRequest(40062, "invalid_desktop_release_channel", "发布渠道格式无效")
|
||||
}
|
||||
|
||||
items, _, err := s.releases.List(ctx, repository.DesktopClientReleaseFilter{
|
||||
Channel: normalizedChannel,
|
||||
Enabled: &enabled,
|
||||
Limit: 200,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
views := make([]DesktopClientDownloadableRelease, 0, len(items))
|
||||
for i := range items {
|
||||
var downloadURL *string
|
||||
if items[i].DownloadSource == DesktopClientDownloadSourceCustom {
|
||||
if value := strings.TrimSpace(stringPointerValue(items[i].CustomDownloadURL)); value != "" {
|
||||
downloadURL = &value
|
||||
}
|
||||
}
|
||||
views = append(views, DesktopClientDownloadableRelease{
|
||||
ID: items[i].ID,
|
||||
Platform: items[i].Platform,
|
||||
Arch: items[i].Arch,
|
||||
Channel: items[i].Channel,
|
||||
Version: items[i].Version,
|
||||
DownloadURL: downloadURL,
|
||||
FileName: items[i].FileName,
|
||||
FileSizeBytes: items[i].FileSizeBytes,
|
||||
SHA256: items[i].SHA256,
|
||||
ReleaseNotes: items[i].ReleaseNotes,
|
||||
PublishedAt: items[i].PublishedAt,
|
||||
UpdatedAt: items[i].UpdatedAt,
|
||||
ForceUpdate: items[i].ForceUpdate,
|
||||
MinSupportedVersion: items[i].MinSupportedVersion,
|
||||
DownloadSource: items[i].DownloadSource,
|
||||
OSSObjectKey: items[i].OSSObjectKey,
|
||||
})
|
||||
}
|
||||
return &DesktopClientDownloadableReleaseListResult{Items: views}, nil
|
||||
}
|
||||
|
||||
func (s *DesktopClientReleaseService) Create(ctx context.Context, actor *Actor, in DesktopClientReleaseInput) (*DesktopClientReleaseView, error) {
|
||||
normalized, err := normalizeDesktopClientReleaseInput(in)
|
||||
if err != nil {
|
||||
@@ -378,6 +453,9 @@ func (s *DesktopClientReleaseService) CheckLatest(ctx context.Context, in Deskto
|
||||
UpdateAvailable: compareVersionStrings(item.Version, currentVersion) > 0,
|
||||
LatestVersion: item.Version,
|
||||
CurrentVersion: currentVersion,
|
||||
Platform: item.Platform,
|
||||
Arch: item.Arch,
|
||||
Channel: item.Channel,
|
||||
MinSupportedVersion: item.MinSupportedVersion,
|
||||
ForceUpdate: item.ForceUpdate || minSupportedVersionRequiresUpdate(item.MinSupportedVersion, currentVersion),
|
||||
DownloadSource: item.DownloadSource,
|
||||
@@ -519,13 +597,38 @@ func itemUpdaterAsset(item *domain.DesktopClientRelease) desktopClientReleaseAss
|
||||
}
|
||||
|
||||
func (s *DesktopClientReleaseService) resolveDownloadURLResult(item *domain.DesktopClientRelease) (*DesktopClientReleaseDownloadURLResult, error) {
|
||||
downloadURL, err := s.resolveAssetDownloadURL(itemInstallerAsset(item))
|
||||
downloadURL, err := s.resolveInstallerDownloadURL(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DesktopClientReleaseDownloadURLResult{DownloadURL: downloadURL}, nil
|
||||
}
|
||||
|
||||
func (s *DesktopClientReleaseService) resolveInstallerDownloadURL(item *domain.DesktopClientRelease) (string, error) {
|
||||
asset := itemInstallerAsset(item)
|
||||
if !asset.HasTarget() {
|
||||
return "", nil
|
||||
}
|
||||
if stringPointerValue(asset.DownloadSource) == DesktopClientDownloadSourceCustom {
|
||||
return strings.TrimSpace(stringPointerValue(asset.CustomDownloadURL)), nil
|
||||
}
|
||||
|
||||
objectKey := strings.TrimSpace(stringPointerValue(asset.OSSObjectKey))
|
||||
if objectKey == "" {
|
||||
return "", response.ErrInternal(50090, "desktop_client_release_missing_object_key", "OSS object key is missing")
|
||||
}
|
||||
if s.storage == nil {
|
||||
return "", response.ErrInternal(50091, "object_storage_unavailable", "object storage is unavailable")
|
||||
}
|
||||
downloadURL, err := s.storage.DownloadURL(objectKey)
|
||||
if err != nil {
|
||||
appErr := response.ErrInternal(50091, "object_storage_url_failed", err.Error())
|
||||
appErr.Cause = err
|
||||
return "", appErr
|
||||
}
|
||||
return downloadURL, nil
|
||||
}
|
||||
|
||||
func (s *DesktopClientReleaseService) resolveAssetDownloadURL(asset desktopClientReleaseAsset) (string, error) {
|
||||
if !asset.HasTarget() {
|
||||
return "", nil
|
||||
|
||||
Reference in New Issue
Block a user