mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-20 01:04:15 +00:00
Nothing joined on it. It was selected into a struct field no caller read, and used only by DeleteTagsNotInList, which fetched surrogate ids, filtered them in Go with a nested loop over the keep list, and issued one DELETE per row. The natural key was already enforced by UNIQUE(did, repository, tag), so that becomes the primary key and the column goes. An AUTOINCREMENT rowid is allocated by whichever node performs the insert. That is fine while every write funnels through one writer and stops being a stable identity the moment they do not, so removing an identifier nobody used is the cheapest way to shrink that surface before local-write replicas. DeleteTagsNotInList now diffs against a set and deletes in batches. It still reads the current tags first rather than issuing one NOT IN over the keep list: that would need two placeholders per kept tag and would break past the driver's parameter ceiling for a user with enough tags, and it cannot be chunked, because each chunk would delete the tags every other chunk meant to keep. An explicit delete list chunks safely. idx_tags_did_repo is dropped rather than recreated: the new primary key indexes (did, repository) as a prefix. It existed only because the primary key used to be the surrogate id. The rebuild names its columns explicitly. Column order is not guaranteed to match between a fresh install and a migrated one, so INSERT ... SELECT * here could write values into the wrong columns. TestMigration0032PreservesTagRows runs the migration body against a table in the old shape and checks the contents survive, which the schema drift test cannot: it compares shape, not data. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
202 lines
6.2 KiB
Go
202 lines
6.2 KiB
Go
package db
|
|
|
|
import "time"
|
|
|
|
// User represents a user in the system
|
|
type User struct {
|
|
DID string
|
|
Handle string
|
|
PDSEndpoint string
|
|
Avatar string
|
|
DefaultHoldDID string
|
|
OciClient string
|
|
RegistryDomain string
|
|
LastSeen time.Time
|
|
}
|
|
|
|
// Manifest represents an OCI manifest stored in the cache
|
|
type Manifest struct {
|
|
ID int64
|
|
DID string
|
|
Repository string
|
|
Digest string
|
|
HoldEndpoint string
|
|
SchemaVersion int
|
|
MediaType string
|
|
ConfigDigest string
|
|
ConfigSize int64
|
|
ArtifactType string // container-image, helm-chart, unknown
|
|
SubjectDigest string // digest of the parent manifest (for attestations/referrers)
|
|
CreatedAt time.Time
|
|
// Annotations removed - now stored in repository_annotations table
|
|
}
|
|
|
|
// Layer represents a layer in a manifest
|
|
type Layer struct {
|
|
ManifestID int64
|
|
Digest string
|
|
Size int64
|
|
MediaType string
|
|
LayerIndex int
|
|
Annotations map[string]string // JSON-encoded layer annotations (e.g. in-toto predicate type)
|
|
}
|
|
|
|
// ManifestReference represents a reference to a manifest in a manifest list/index
|
|
type ManifestReference struct {
|
|
ManifestID int64
|
|
Digest string
|
|
Size int64
|
|
MediaType string
|
|
PlatformArchitecture string
|
|
PlatformOS string
|
|
PlatformVariant string
|
|
PlatformOSVersion string
|
|
IsAttestation bool // true if vnd.docker.reference.type = "attestation-manifest"
|
|
ReferenceIndex int
|
|
}
|
|
|
|
// Tag represents a tag pointing to a manifest
|
|
type Tag struct {
|
|
DID string
|
|
Repository string
|
|
Tag string
|
|
Digest string
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// Repository represents an aggregated view of a user's repository
|
|
type Repository struct {
|
|
Name string
|
|
TagCount int
|
|
ManifestCount int
|
|
LastPush time.Time
|
|
Tags []Tag
|
|
Manifests []Manifest
|
|
Title string
|
|
Description string
|
|
SourceURL string
|
|
DocumentationURL string
|
|
Licenses string
|
|
IconURL string
|
|
ReadmeURL string
|
|
Version string
|
|
}
|
|
|
|
// RepositoryStats represents statistics for a repository
|
|
type RepositoryStats struct {
|
|
DID string `json:"did"`
|
|
Repository string `json:"repository"`
|
|
StarCount int `json:"star_count"` // Calculated from stars table, not stored
|
|
PullCount int `json:"pull_count"`
|
|
LastPull *time.Time `json:"last_pull,omitempty"`
|
|
PushCount int `json:"push_count"`
|
|
LastPush *time.Time `json:"last_push,omitempty"`
|
|
}
|
|
|
|
// DailyStats represents daily pull/push statistics for a repository
|
|
type DailyStats struct {
|
|
DID string `json:"did"`
|
|
Repository string `json:"repository"`
|
|
Date string `json:"date"`
|
|
PullCount int `json:"pull_count"`
|
|
PushCount int `json:"push_count"`
|
|
}
|
|
|
|
// RepositoryWithStats combines repository data with statistics
|
|
type RepositoryWithStats struct {
|
|
Repository
|
|
Stats RepositoryStats
|
|
}
|
|
|
|
// RepoCardData contains all data needed to render a repository card
|
|
type RepoCardData struct {
|
|
OwnerHandle string
|
|
OwnerAvatarURL string // Owner's profile avatar URL (fallback when no repo icon)
|
|
Repository string
|
|
Title string
|
|
Description string
|
|
IconURL string
|
|
StarCount int
|
|
PullCount int
|
|
IsStarred bool // Whether the current user has starred this repository
|
|
ArtifactType string // container-image, helm-chart, unknown
|
|
Tag string // Latest tag name (e.g., "latest", "v1.0.0")
|
|
Digest string // Latest manifest digest (sha256:...)
|
|
LastUpdated time.Time // When the repository was last pushed to
|
|
RegistryURL string // Registry URL for docker commands (e.g., "atcr.io" or "127.0.0.1:5000")
|
|
OciClient string // Preferred OCI client for pull commands (e.g., "docker", "podman")
|
|
}
|
|
|
|
// SetRegistryURL sets the RegistryURL field on all cards in the slice
|
|
func SetRegistryURL(cards []RepoCardData, registryURL string) {
|
|
for i := range cards {
|
|
cards[i].RegistryURL = registryURL
|
|
}
|
|
}
|
|
|
|
// SetOciClient sets the OciClient field on all cards in the slice
|
|
func SetOciClient(cards []RepoCardData, ociClient string) {
|
|
for i := range cards {
|
|
cards[i].OciClient = ociClient
|
|
}
|
|
}
|
|
|
|
// PlatformInfo represents platform information (OS/Architecture)
|
|
type PlatformInfo struct {
|
|
OS string
|
|
Architecture string
|
|
Variant string
|
|
OSVersion string
|
|
Digest string // child platform manifest digest (for manifest lists)
|
|
HoldEndpoint string // hold endpoint for this platform manifest
|
|
CompressedSize int64 // sum of layer sizes (compressed)
|
|
}
|
|
|
|
// TagWithPlatforms extends Tag with platform information
|
|
type TagWithPlatforms struct {
|
|
Tag
|
|
HoldEndpoint string // hold endpoint from the tag's own manifest
|
|
Platforms []PlatformInfo
|
|
IsMultiArch bool
|
|
HasAttestations bool // true if manifest list contains attestation references
|
|
ArtifactType string // container-image, helm-chart, unknown
|
|
CompressedSize int64 // sum of layer sizes for single-arch tags
|
|
}
|
|
|
|
// ManifestWithMetadata extends Manifest with tags and platform information
|
|
type ManifestWithMetadata struct {
|
|
Manifest
|
|
Tags []string
|
|
Platforms []PlatformInfo
|
|
PlatformCount int
|
|
IsManifestList bool
|
|
HasAttestations bool // true if manifest list contains attestation references
|
|
Reachable bool // Whether the hold endpoint is reachable
|
|
Pending bool // Whether health check is still in progress
|
|
// Note: ArtifactType is available via embedded Manifest struct
|
|
}
|
|
|
|
// ManifestEntry is a unified view model for the tags tab.
|
|
// Every entry is a manifest — labeled by tag name or digest.
|
|
type ManifestEntry struct {
|
|
Label string // tag name, or digest if untagged
|
|
Digest string // manifest digest
|
|
IsTagged bool
|
|
CreatedAt time.Time
|
|
HoldEndpoint string
|
|
Platforms []PlatformInfo
|
|
IsMultiArch bool
|
|
HasAttestations bool
|
|
ArtifactType string
|
|
CompressedSize int64 // for single-arch
|
|
}
|
|
|
|
// AttestationDetail represents an attestation manifest and its layers
|
|
type AttestationDetail struct {
|
|
Digest string
|
|
MediaType string // attestation manifest media type
|
|
Size int64
|
|
HoldEndpoint string // hold DID/URL where blobs are stored
|
|
Layers []Layer
|
|
}
|