163 lines
5.6 KiB
Go
163 lines
5.6 KiB
Go
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)
|
|
}
|