allow fetch tags from pds

This commit is contained in:
Evan Jarrett
2025-10-12 17:05:32 -05:00
parent 3646cf686a
commit 6edc22cfcc
3 changed files with 137 additions and 6 deletions

View File

@@ -108,9 +108,7 @@ func (r *RoutingRepository) Blobs(ctx context.Context) distribution.BlobStore {
}
// Tags returns the tag service
// Tags will be handled by ATProto as well
// Tags are stored in ATProto as io.atcr.tag records
func (r *RoutingRepository) Tags(ctx context.Context) distribution.TagService {
// For now, delegate to the base repository
// In a full implementation, this would also use ATProto
return r.Repository.Tags(ctx)
return atproto.NewTagStore(r.atprotoClient, r.repositoryName)
}

View File

@@ -135,7 +135,11 @@ func (c *Client) GetRecord(ctx context.Context, collection, rkey string) (*Recor
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.accessToken)
// Only set Authorization header if we have a token
// Empty Bearer tokens will be rejected by PDS
if c.accessToken != "" {
req.Header.Set("Authorization", "Bearer "+c.accessToken)
}
resp, err := c.httpClient.Do(req)
if err != nil {
@@ -217,7 +221,11 @@ func (c *Client) ListRecords(ctx context.Context, collection string, limit int)
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.accessToken)
// Only set Authorization header if we have a token
// Empty Bearer tokens will be rejected by PDS
if c.accessToken != "" {
req.Header.Set("Authorization", "Bearer "+c.accessToken)
}
resp, err := c.httpClient.Do(req)
if err != nil {

125
pkg/atproto/tag_store.go Normal file
View File

@@ -0,0 +1,125 @@
package atproto
import (
"context"
"encoding/json"
"fmt"
"github.com/distribution/distribution/v3"
"github.com/opencontainers/go-digest"
)
// TagStore implements distribution.TagService
// It stores tags in ATProto as records
type TagStore struct {
client *Client
repository string
}
// NewTagStore creates a new ATProto-backed tag store
func NewTagStore(client *Client, repository string) *TagStore {
return &TagStore{
client: client,
repository: repository,
}
}
// Get retrieves the descriptor for a tag
func (s *TagStore) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
// Build record key
rkey := repositoryTagToRKey(s.repository, tag)
// Fetch tag record from ATProto
record, err := s.client.GetRecord(ctx, TagCollection, rkey)
if err != nil {
return distribution.Descriptor{}, distribution.ErrTagUnknown{Tag: tag}
}
var tagRecord TagRecord
if err := json.Unmarshal(record.Value, &tagRecord); err != nil {
return distribution.Descriptor{}, fmt.Errorf("failed to unmarshal tag record: %w", err)
}
// Parse manifest digest
dgst, err := digest.Parse(tagRecord.ManifestDigest)
if err != nil {
return distribution.Descriptor{}, fmt.Errorf("invalid manifest digest in tag record: %w", err)
}
// Return descriptor pointing to the manifest
return distribution.Descriptor{
Digest: dgst,
MediaType: "application/vnd.oci.image.manifest.v1+json",
}, nil
}
// Tag associates a tag with a descriptor (manifest digest)
func (s *TagStore) Tag(ctx context.Context, tag string, desc distribution.Descriptor) error {
// Create tag record
tagRecord := NewTagRecord(s.repository, tag, desc.Digest.String())
// Store in ATProto
rkey := repositoryTagToRKey(s.repository, tag)
_, err := s.client.PutRecord(ctx, TagCollection, rkey, tagRecord)
if err != nil {
return fmt.Errorf("failed to store tag in ATProto: %w", err)
}
return nil
}
// Untag removes a tag
func (s *TagStore) Untag(ctx context.Context, tag string) error {
rkey := repositoryTagToRKey(s.repository, tag)
return s.client.DeleteRecord(ctx, TagCollection, rkey)
}
// All returns all tags for this repository
func (s *TagStore) All(ctx context.Context) ([]string, error) {
// List all records in the tag collection
records, err := s.client.ListRecords(ctx, TagCollection, 100)
if err != nil {
return nil, fmt.Errorf("failed to list tags: %w", err)
}
var tags []string
for _, record := range records {
var tagRecord TagRecord
if err := json.Unmarshal(record.Value, &tagRecord); err != nil {
// Skip invalid records
continue
}
// Only include tags for this repository
if tagRecord.Repository == s.repository {
tags = append(tags, tagRecord.Tag)
}
}
return tags, nil
}
// Lookup returns the set of tags for a given digest
func (s *TagStore) Lookup(ctx context.Context, desc distribution.Descriptor) ([]string, error) {
// List all records in the tag collection
records, err := s.client.ListRecords(ctx, TagCollection, 100)
if err != nil {
return nil, fmt.Errorf("failed to list tags: %w", err)
}
var tags []string
for _, record := range records {
var tagRecord TagRecord
if err := json.Unmarshal(record.Value, &tagRecord); err != nil {
// Skip invalid records
continue
}
// Only include tags for this repository that match the digest
if tagRecord.Repository == s.repository && tagRecord.ManifestDigest == desc.Digest.String() {
tags = append(tags, tagRecord.Tag)
}
}
return tags, nil
}