package update import ( domain "RenewCMS/internal/domain/update" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) const ( releasesPath = "/api/v1/repos/fsylvain/RenewCMS/releases" feedRequestTimeout = 10 * time.Second maxFeedBodyBytes = 4 << 20 ) type GiteaReleaseFeed struct { baseURL string client *http.Client } var _ domain.ReleaseFeed = &GiteaReleaseFeed{} type giteaRelease struct { TagName string `json:"tag_name"` HTMLURL string `json:"html_url"` Body string `json:"body"` Draft bool `json:"draft"` PublishedAt time.Time `json:"published_at"` Assets []giteaAsset `json:"assets"` } type giteaAsset struct { Name string `json:"name"` Size int64 `json:"size"` DownloadURL string `json:"browser_download_url"` } func NewGiteaReleaseFeed(baseURL string) *GiteaReleaseFeed { return &GiteaReleaseFeed{ baseURL: strings.TrimRight(baseURL, "/"), client: &http.Client{Timeout: feedRequestTimeout}, } } func (g *GiteaReleaseFeed) Latest() (domain.Release, error) { releases, err := g.fetch() if err != nil { return domain.Release{}, err } return domain.Newest(releases) } func (g *GiteaReleaseFeed) fetch() ([]domain.Release, error) { response, err := g.client.Get(g.baseURL + releasesPath) if err != nil { return nil, fmt.Errorf("%w: %v", domain.ErrFeedUnavailable, err) } defer response.Body.Close() if response.StatusCode != http.StatusOK { return nil, fmt.Errorf("%w: status %d", domain.ErrFeedUnavailable, response.StatusCode) } var raw []giteaRelease if err := json.NewDecoder(io.LimitReader(response.Body, maxFeedBodyBytes)).Decode(&raw); err != nil { return nil, fmt.Errorf("%w: %v", domain.ErrFeedUnavailable, err) } return toReleases(raw), nil } func toReleases(raw []giteaRelease) []domain.Release { releases := make([]domain.Release, 0, len(raw)) for _, item := range raw { if item.Draft { continue } releases = append(releases, domain.Release{ Tag: item.TagName, URL: item.HTMLURL, PublishedAt: item.PublishedAt, Notes: domain.BuildNotes(item.TagName, item.PublishedAt, item.Body), Assets: toAssets(item.Assets), }) } return releases } func toAssets(raw []giteaAsset) []domain.Asset { assets := make([]domain.Asset, 0, len(raw)) for _, item := range raw { assets = append(assets, domain.Asset{Name: item.Name, URL: item.DownloadURL, Size: item.Size}) } return assets }