mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-02 00:06:58 +00:00
Completes the swap 0033 set up. layers and manifest_references move onto manifest_key and manifests.id is gone, which removes the last node-allocated identifier in the AppView schema. Statement order in 0034 is load-bearing. With foreign keys on, DROP TABLE performs an implicit DELETE FROM, so dropping manifests while layers still holds an ON DELETE CASCADE reference deletes every layer row. Migration 0009 did exactly that; it went unnoticed because the Jetstream backfill rebuilds layers from PDS records, so the damage healed itself. PRAGMA foreign_keys is no help: it is a no-op inside a transaction and migrations run in one. So the new children are built pointing at manifests_new, the old children are dropped first, and only then is the old manifests table dropped, by which point nothing references it. Verified both behaviors before relying on them. manifest_key is declared NOT NULL as well as PRIMARY KEY, because in SQLite a PRIMARY KEY column still accepts NULL unless it is INTEGER PRIMARY KEY. That constraint immediately caught four test helpers inserting manifests without one. Five queries used MAX(id) as "the newest manifest in this repo", which I had previously reported as absent after grepping only for ORDER BY. A derived key has no ordering, so recency now comes from created_at with manifest_key as a deterministic tiebreak. This is a real behavior change, and a fix: the two disagree whenever a manifest is indexed out of order, which the backfill does routinely, and created_at is the push time these queries always wanted. Both directions are tested, including that ties resolve the same way every run. InsertManifest and BatchInsertManifests no longer read anything back. The key is derived from (did, repository, digest), so the writer knows it before the statement runs: the select-back, its per-DID IN list, and the "manifest missing id after batch insert" branch all go away, along with the UNIQUE-conflict fallback that existed only to recover a rowid. 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 {
|
|
Key string // node-independent identity; see ManifestKey
|
|
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 {
|
|
ManifestKey string
|
|
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 {
|
|
ManifestKey string
|
|
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
|
|
}
|