wip: oci image management
This commit is contained in:
162
internal/model/registry.go
Normal file
162
internal/model/registry.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Hash represents a content hash
|
||||
type Hash string
|
||||
|
||||
// NewHash creates a new Hash from a string
|
||||
func NewHash(s string) (Hash, error) {
|
||||
if !strings.HasPrefix(s, "sha256:") {
|
||||
return "", fmt.Errorf("invalid hash format: %s", s)
|
||||
}
|
||||
return Hash(s), nil
|
||||
}
|
||||
|
||||
// String returns the string representation of the hash
|
||||
func (h Hash) String() string {
|
||||
return string(h)
|
||||
}
|
||||
|
||||
// Hex returns the hex part of the hash
|
||||
func (h Hash) Hex() (string, error) {
|
||||
if !strings.HasPrefix(string(h), "sha256:") {
|
||||
return "", fmt.Errorf("invalid hash format: %s", h)
|
||||
}
|
||||
return strings.TrimPrefix(string(h), "sha256:"), nil
|
||||
}
|
||||
|
||||
// SHA256 computes the SHA256 hash of the given reader
|
||||
func SHA256(r io.Reader) (Hash, int64, error) {
|
||||
hasher := sha256.New()
|
||||
n, err := io.Copy(hasher, r)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return Hash("sha256:" + hex.EncodeToString(hasher.Sum(nil))), n, nil
|
||||
}
|
||||
|
||||
// RegistryRepository represents a repository in the registry
|
||||
type RegistryRepository struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"uniqueIndex;not null" json:"name"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
}
|
||||
|
||||
// RegistryManifest represents a manifest in the registry
|
||||
type RegistryManifest struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
RepositoryID uint `gorm:"not null;index;uniqueIndex:idx_repo_digest" json:"repository_id"`
|
||||
Repository RegistryRepository `gorm:"foreignKey:RepositoryID" json:"repository"`
|
||||
Digest string `gorm:"not null;uniqueIndex:idx_repo_digest" json:"digest"`
|
||||
Tag string `gorm:"index" json:"tag"`
|
||||
ContentType string `gorm:"not null" json:"content_type"`
|
||||
Size int64 `gorm:"not null" json:"size"`
|
||||
Blob []byte `gorm:"type:blob" json:"blob"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
}
|
||||
|
||||
// RegistryBlob represents a blob in the registry
|
||||
type RegistryBlob struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Digest string `gorm:"uniqueIndex;not null" json:"digest"`
|
||||
Size int64 `gorm:"not null" json:"size"`
|
||||
Path string `gorm:"not null" json:"path"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
}
|
||||
|
||||
// RegistryTag represents a tag in the registry
|
||||
type RegistryTag struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
RepositoryID uint `gorm:"not null;index" json:"repository_id"`
|
||||
Repository RegistryRepository `gorm:"foreignKey:RepositoryID" json:"repository"`
|
||||
Name string `gorm:"not null;index" json:"name"`
|
||||
ManifestID uint `gorm:"not null;index" json:"manifest_id"`
|
||||
Manifest RegistryManifest `gorm:"foreignKey:ManifestID" json:"manifest"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
}
|
||||
|
||||
// RegistryUploadSession represents an upload session
|
||||
type RegistryUploadSession struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
RepositoryID uint `gorm:"not null;index" json:"repository_id"`
|
||||
Repository RegistryRepository `gorm:"foreignKey:RepositoryID" json:"repository"`
|
||||
SessionID string `gorm:"uniqueIndex;not null" json:"session_id"`
|
||||
Path string `gorm:"not null" json:"path"`
|
||||
Size int64 `gorm:"not null" json:"size"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
}
|
||||
|
||||
// Tag represents a tag list response
|
||||
type Tag struct {
|
||||
Name string `json:"name"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// Catalog represents a catalog response
|
||||
type Catalog struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
// RepoSimpleManifest represents a simple manifest
|
||||
type RepoSimpleManifest struct {
|
||||
Blob []byte `json:"blob"`
|
||||
ContentType string `json:"content_type"`
|
||||
}
|
||||
|
||||
// IndexManifest represents an index manifest
|
||||
type IndexManifest struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
MediaType string `json:"mediaType"`
|
||||
Manifests []IndexManifestEntry `json:"manifests"`
|
||||
}
|
||||
|
||||
// IndexManifestEntry represents an entry in an index manifest
|
||||
type IndexManifestEntry struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
Size int64 `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
Platform *Platform `json:"platform,omitempty"`
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// Platform represents a platform specification
|
||||
type Platform struct {
|
||||
Architecture string `json:"architecture"`
|
||||
OS string `json:"os"`
|
||||
Variant string `json:"variant,omitempty"`
|
||||
}
|
||||
|
||||
// GetBlobPath returns the file path for a blob
|
||||
func GetBlobPath(baseDir, digest string) string {
|
||||
hash := strings.TrimPrefix(digest, "sha256:")
|
||||
if len(hash) < 4 {
|
||||
return filepath.Join(baseDir, "registry", "blobs", "sha256", hash)
|
||||
}
|
||||
return filepath.Join(baseDir, "registry", "blobs", "sha256", hash[:2], hash[2:4], hash)
|
||||
}
|
||||
|
||||
// GetUploadPath returns the file path for an upload session
|
||||
func GetUploadPath(baseDir, sessionID string) string {
|
||||
return filepath.Join(baseDir, "registry", "uploads", sessionID)
|
||||
}
|
||||
Reference in New Issue
Block a user