wip: oci image management
This commit is contained in:
114
internal/database/database.go
Normal file
114
internal/database/database.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// DB 数据库连接
|
||||
var DB *gorm.DB
|
||||
|
||||
// Init 初始化数据库
|
||||
func Init(dataDir string) error {
|
||||
// 确保数据目录存在
|
||||
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create data directory %s: %w", dataDir, err)
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(dataDir, "cluster.db")
|
||||
|
||||
// 检查数据库文件是否存在
|
||||
dbExists := false
|
||||
if _, err := os.Stat(dbPath); err == nil {
|
||||
dbExists = true
|
||||
log.Printf("Database file already exists: %s", dbPath)
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to check database file: %w", err)
|
||||
} else {
|
||||
log.Printf("Creating new database file: %s", dbPath)
|
||||
}
|
||||
|
||||
// 配置 GORM logger
|
||||
gormLogger := logger.Default
|
||||
if os.Getenv("GORM_LOG_LEVEL") == "silent" {
|
||||
gormLogger = gormLogger.LogMode(logger.Silent)
|
||||
}
|
||||
|
||||
// 打开数据库连接
|
||||
var err error
|
||||
DB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
Logger: gormLogger,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
|
||||
// 获取底层 sql.DB 以设置连接池
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get database instance: %w", err)
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
// 启用外键约束
|
||||
if err := DB.Exec("PRAGMA foreign_keys = ON").Error; err != nil {
|
||||
return fmt.Errorf("failed to enable foreign keys: %w", err)
|
||||
}
|
||||
|
||||
// 自动迁移表结构
|
||||
if err := autoMigrate(); err != nil {
|
||||
return fmt.Errorf("failed to migrate tables: %w", err)
|
||||
}
|
||||
|
||||
if !dbExists {
|
||||
log.Printf("Database initialized successfully: %s", dbPath)
|
||||
} else {
|
||||
log.Printf("Database tables verified: %s", dbPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭数据库连接
|
||||
func Close() error {
|
||||
if DB != nil {
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sqlDB.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// autoMigrate 自动迁移表结构
|
||||
func autoMigrate() error {
|
||||
models := []interface{}{
|
||||
&Repository{},
|
||||
&Manifest{},
|
||||
&Blob{},
|
||||
&Tag{},
|
||||
&BlobUpload{},
|
||||
&ManifestBlob{},
|
||||
}
|
||||
|
||||
for _, model := range models {
|
||||
if err := DB.AutoMigrate(model); err != nil {
|
||||
return fmt.Errorf("failed to migrate model %T: %w", model, err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Database migration completed successfully")
|
||||
return nil
|
||||
}
|
||||
116
internal/database/models.go
Normal file
116
internal/database/models.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package database
|
||||
|
||||
import "time"
|
||||
|
||||
// Repository 仓库模型
|
||||
type Repository struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
Name string `gorm:"uniqueIndex;not null" json:"name"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
Manifests []Manifest `gorm:"foreignKey:RepositoryID;constraint:OnDelete:CASCADE" json:"manifests,omitempty"`
|
||||
Tags []Tag `gorm:"foreignKey:RepositoryID;constraint:OnDelete:CASCADE" json:"tags,omitempty"`
|
||||
BlobUploads []BlobUpload `gorm:"foreignKey:RepositoryID;constraint:OnDelete:CASCADE" json:"blob_uploads,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Repository) TableName() string {
|
||||
return "repositories"
|
||||
}
|
||||
|
||||
// Manifest Manifest 模型
|
||||
type Manifest struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
RepositoryID uint `gorm:"index;not null" json:"repository_id"`
|
||||
Digest string `gorm:"index;not null" json:"digest"`
|
||||
MediaType string `gorm:"not null" json:"media_type"`
|
||||
Size int64 `gorm:"not null" json:"size"`
|
||||
DataPath string `gorm:"not null" json:"data_path"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
|
||||
// 唯一约束:同一仓库中 digest 唯一
|
||||
_ struct{} `gorm:"uniqueIndex:idx_repo_digest"`
|
||||
|
||||
// 关联关系
|
||||
Repository Repository `gorm:"foreignKey:RepositoryID" json:"repository,omitempty"`
|
||||
Tags []Tag `gorm:"foreignKey:ManifestID;constraint:OnDelete:CASCADE" json:"tags,omitempty"`
|
||||
ManifestBlobs []ManifestBlob `gorm:"foreignKey:ManifestID;constraint:OnDelete:CASCADE" json:"manifest_blobs,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Manifest) TableName() string {
|
||||
return "manifests"
|
||||
}
|
||||
|
||||
// Blob Blob 模型
|
||||
type Blob struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
Digest string `gorm:"uniqueIndex;not null" json:"digest"`
|
||||
Size int64 `gorm:"not null" json:"size"`
|
||||
DataPath string `gorm:"not null" json:"data_path"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
|
||||
// 关联关系
|
||||
ManifestBlobs []ManifestBlob `gorm:"foreignKey:BlobID;constraint:OnDelete:CASCADE" json:"manifest_blobs,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Blob) TableName() string {
|
||||
return "blobs"
|
||||
}
|
||||
|
||||
// Tag 标签模型
|
||||
type Tag struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
RepositoryID uint `gorm:"uniqueIndex:idx_repo_name;not null" json:"repository_id"`
|
||||
Name string `gorm:"uniqueIndex:idx_repo_name;not null" json:"name"`
|
||||
ManifestID uint `gorm:"index;not null" json:"manifest_id"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
Repository Repository `gorm:"foreignKey:RepositoryID" json:"repository,omitempty"`
|
||||
Manifest Manifest `gorm:"foreignKey:ManifestID" json:"manifest,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Tag) TableName() string {
|
||||
return "tags"
|
||||
}
|
||||
|
||||
// BlobUpload Blob 上传会话模型
|
||||
type BlobUpload struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
UUID string `gorm:"uniqueIndex;not null" json:"uuid"`
|
||||
RepositoryID uint `gorm:"index;not null" json:"repository_id"`
|
||||
Size int64 `gorm:"default:0" json:"size"`
|
||||
DataPath string `gorm:"not null" json:"data_path"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
Repository Repository `gorm:"foreignKey:RepositoryID" json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (BlobUpload) TableName() string {
|
||||
return "blob_uploads"
|
||||
}
|
||||
|
||||
// ManifestBlob Manifest 和 Blob 关联模型
|
||||
type ManifestBlob struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ManifestID uint `gorm:"uniqueIndex:idx_manifest_blob;index;not null" json:"manifest_id"`
|
||||
BlobID uint `gorm:"uniqueIndex:idx_manifest_blob;index;not null" json:"blob_id"`
|
||||
|
||||
// 关联关系
|
||||
Manifest Manifest `gorm:"foreignKey:ManifestID" json:"manifest,omitempty"`
|
||||
Blob Blob `gorm:"foreignKey:BlobID" json:"blob,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (ManifestBlob) TableName() string {
|
||||
return "manifest_blobs"
|
||||
}
|
||||
Reference in New Issue
Block a user