130 lines · 3.5 KB · Go
RawDownload
package pluginstore import (	"context"	"encoding/json"	"fmt"	"net/http"	"net/url"	"strings" 	"github.com/router-for-me/CLIProxyAPI/v7/internal/httpfetch") const userAgent = "CLIProxyAPI" // HTTPDoer abstracts the HTTP client used to execute requests.type HTTPDoer = httpfetch.Doer type Client struct {	HTTPClient  HTTPDoer	RegistryURL string	UserAgent   string} type Release struct {	TagName string         `json:"tag_name"`	Assets  []ReleaseAsset `json:"assets"`} type ReleaseAsset struct {	Name               string `json:"name"`	BrowserDownloadURL string `json:"browser_download_url"`} func (c Client) FetchRegistry(ctx context.Context) (Registry, error) {	registryURL := strings.TrimSpace(c.RegistryURL)	if registryURL == "" {		registryURL = DefaultRegistryURL	}	data, errDownload := c.get(ctx, registryURL, "application/json")	if errDownload != nil {		return Registry{}, errDownload	}	registry, errParse := ParseRegistry(data)	if errParse != nil {		return Registry{}, errParse	}	return registry, nil} func (c Client) FetchRelease(ctx context.Context, plugin Plugin) (Release, error) {	owner, repo, errRepository := GitHubRepositoryParts(plugin.Repository)	if errRepository != nil {		return Release{}, errRepository	}	releaseURL := fmt.Sprintf(		"https://api.github.com/repos/%s/%s/releases/tags/%s",		url.PathEscape(owner),		url.PathEscape(repo),		url.PathEscape("v"+strings.TrimSpace(plugin.Version)),	)	data, errDownload := c.get(ctx, releaseURL, "application/vnd.github+json")	if errDownload != nil {		return Release{}, errDownload	}	var release Release	if errDecode := json.Unmarshal(data, &release); errDecode != nil {		return Release{}, fmt.Errorf("decode release: %w", errDecode)	}	return release, nil} func (c Client) DownloadAsset(ctx context.Context, asset ReleaseAsset) ([]byte, error) {	if strings.TrimSpace(asset.BrowserDownloadURL) == "" {		return nil, fmt.Errorf("asset %q missing browser_download_url", asset.Name)	}	return c.get(ctx, asset.BrowserDownloadURL, "application/octet-stream")} func (c Client) get(ctx context.Context, requestURL string, accept string) ([]byte, error) {	return httpfetch.GetBytes(ctx, c.httpClient(), requestURL, map[string]string{		"Accept":     accept,		"User-Agent": c.userAgent(),	}, 0)} func (c Client) httpClient() HTTPDoer {	if c.HTTPClient != nil {		return c.HTTPClient	}	return http.DefaultClient} func (c Client) userAgent() string {	if strings.TrimSpace(c.UserAgent) != "" {		return strings.TrimSpace(c.UserAgent)	}	return userAgent} func SelectReleaseAssets(release Release, id, version, goos, goarch string) (ReleaseAsset, ReleaseAsset, error) {	archiveName := ArchiveName(id, version, goos, goarch)	var archiveAsset ReleaseAsset	var checksumAsset ReleaseAsset	for _, asset := range release.Assets {		switch strings.TrimSpace(asset.Name) {		case archiveName:			archiveAsset = asset		case "checksums.txt":			checksumAsset = asset		}	}	if strings.TrimSpace(archiveAsset.Name) == "" {		return ReleaseAsset{}, ReleaseAsset{}, fmt.Errorf("release asset %s not found", archiveName)	}	if strings.TrimSpace(checksumAsset.Name) == "" {		return ReleaseAsset{}, ReleaseAsset{}, fmt.Errorf("release asset checksums.txt not found")	}	return archiveAsset, checksumAsset, nil} func ArchiveName(id, version, goos, goarch string) string {	return fmt.Sprintf(		"%s_%s_%s_%s.zip",		strings.TrimSpace(id),		strings.TrimSpace(version),		strings.TrimSpace(goos),		strings.TrimSpace(goarch),	)}