feat: auto-discover container images from registries
- Add ListImages() to registry interface, implement for Gitea - Add owner field to registry config (needed for Gitea packages API) - GET /api/registries/:id/images endpoint - "Browse Images" button on Projects and Quick Deploy pages - Image dropdown with registry grouping and search - i18n support (EN/RU) for all new UI strings
This commit is contained in:
@@ -41,6 +41,80 @@ func NewGiteaClient(baseURL, token string) *GiteaClient {
|
||||
}
|
||||
}
|
||||
|
||||
// ListImages returns all container images (packages) for the given owner.
|
||||
// It queries GET /api/v1/packages/{owner}?type=container and paginates
|
||||
// through all results, returning a RegistryImage for each unique package.
|
||||
func (c *GiteaClient) ListImages(ctx context.Context, owner string) ([]RegistryImage, error) {
|
||||
if owner == "" {
|
||||
return nil, fmt.Errorf("owner is required for listing images")
|
||||
}
|
||||
|
||||
// Extract the registry host from baseURL to build full references.
|
||||
host := c.baseURL
|
||||
for _, prefix := range []string{"https://", "http://"} {
|
||||
host = strings.TrimPrefix(host, prefix)
|
||||
}
|
||||
host = strings.TrimRight(host, "/")
|
||||
|
||||
var images []RegistryImage
|
||||
seen := make(map[string]bool)
|
||||
page := 1
|
||||
limit := 50
|
||||
|
||||
for {
|
||||
url := fmt.Sprintf("%s/api/v1/packages/%s?type=container&page=%d&limit=%d",
|
||||
c.baseURL, owner, page, limit)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
|
||||
if c.token != "" {
|
||||
req.Header.Set("Authorization", "token "+c.token)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute request: %w", err)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var packages []giteaPackageListEntry
|
||||
if err := json.Unmarshal(body, &packages); err != nil {
|
||||
return nil, fmt.Errorf("decode package list: %w", err)
|
||||
}
|
||||
|
||||
for _, p := range packages {
|
||||
if !seen[p.Name] {
|
||||
seen[p.Name] = true
|
||||
images = append(images, RegistryImage{
|
||||
Name: p.Name,
|
||||
Owner: owner,
|
||||
FullRef: fmt.Sprintf("%s/%s/%s", host, owner, p.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(packages) < limit {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// ListTags returns all available tags for the given container image.
|
||||
// The image should be in the format "owner/package-name" or
|
||||
// "registry-host/owner/package-name" (the registry host prefix is stripped).
|
||||
|
||||
Reference in New Issue
Block a user