refactor: reorganize models to pkg/model and add authentication module
- Move ORM models from internal/model to pkg/model organized by module (auth/k8s/registry) - Add authentication module with login, user management handlers - Update all import paths to use new model locations - Add frontend auth pages (Login, UserManagement) and authStore - Remove deprecated internal/model/model.go
This commit is contained in:
41
internal/module/auth/auth.go
Normal file
41
internal/module/auth/auth.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
auth "gitea.loveuer.com/loveuer/cluster/pkg/model/auth"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/tool"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Init(ctx context.Context, db *gorm.DB) error {
|
||||
if err := db.AutoMigrate(&auth.User{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&auth.User{}).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
defaultAdmin := &auth.User{
|
||||
Username: "admin",
|
||||
Password: tool.NewPassword("cluster"),
|
||||
Email: "admin@cluster.local",
|
||||
Nickname: "Administrator",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
Permissions: "registry_read,registry_write,cluster_read,cluster_write",
|
||||
}
|
||||
|
||||
if err := db.Create(defaultAdmin).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("[Auth] Default admin user created: username=admin, password=cluster")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
42
internal/module/auth/handler.current.go
Normal file
42
internal/module/auth/handler.current.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func GetCurrentUser(ctx context.Context) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
authHeader := c.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
return resp.R401(c, "MISSING_TOKEN", nil, "authorization token is required")
|
||||
}
|
||||
|
||||
tokenString := authHeader
|
||||
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
||||
tokenString = authHeader[7:]
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(JWTSecret), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
return resp.R401(c, "INVALID_TOKEN", nil, "invalid or expired token")
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok {
|
||||
return resp.R401(c, "INVALID_CLAIMS", nil, "invalid token claims")
|
||||
}
|
||||
|
||||
return resp.R200(c, map[string]interface{}{
|
||||
"user_id": claims.UserID,
|
||||
"username": claims.Username,
|
||||
"role": claims.Role,
|
||||
})
|
||||
}
|
||||
}
|
||||
97
internal/module/auth/handler.login.go
Normal file
97
internal/module/auth/handler.login.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
authModel "gitea.loveuer.com/loveuer/cluster/pkg/model/auth"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/tool"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
JWTSecret = "cluster-secret-key-change-in-production"
|
||||
TokenDuration = 7 * 24 * time.Hour
|
||||
)
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func Login(ctx context.Context, db *gorm.DB) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
var req LoginRequest
|
||||
|
||||
body := c.Body()
|
||||
if len(body) == 0 {
|
||||
return resp.R400(c, "EMPTY_BODY", nil, "request body is empty")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
return resp.R400(c, "INVALID_REQUEST", nil, "invalid request body")
|
||||
}
|
||||
|
||||
if req.Username == "" || req.Password == "" {
|
||||
return resp.R400(c, "MISSING_CREDENTIALS", nil, "username and password are required")
|
||||
}
|
||||
|
||||
var user authModel.User
|
||||
if err := db.Where("username = ?", req.Username).First(&user).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R401(c, "INVALID_CREDENTIALS", nil, "invalid username or password")
|
||||
}
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
if user.Status != "active" {
|
||||
return resp.R403(c, "USER_INACTIVE", nil, "user account is inactive")
|
||||
}
|
||||
|
||||
if !tool.ComparePassword(req.Password, user.Password) {
|
||||
return resp.R401(c, "INVALID_CREDENTIALS", nil, "invalid username or password")
|
||||
}
|
||||
|
||||
claims := &Claims{
|
||||
UserID: user.ID,
|
||||
Username: user.Username,
|
||||
Role: user.Role,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenDuration)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(JWTSecret))
|
||||
if err != nil {
|
||||
return resp.R500(c, "", nil, "failed to generate token")
|
||||
}
|
||||
|
||||
return resp.R200(c, LoginResponse{
|
||||
Token: tokenString,
|
||||
Username: user.Username,
|
||||
Nickname: user.Nickname,
|
||||
Role: user.Role,
|
||||
})
|
||||
}
|
||||
}
|
||||
207
internal/module/auth/handler.user.go
Normal file
207
internal/module/auth/handler.user.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
auth "gitea.loveuer.com/loveuer/cluster/pkg/model/auth"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/tool"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func UserList(ctx context.Context, db *gorm.DB) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
var users []auth.User
|
||||
if err := db.Find(&users).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
return resp.R200(c, map[string]interface{}{
|
||||
"users": users,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func UserCreate(ctx context.Context, db *gorm.DB) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
Permissions string `json:"permissions"`
|
||||
}
|
||||
|
||||
body := c.Body()
|
||||
if len(body) == 0 {
|
||||
return resp.R400(c, "EMPTY_BODY", nil, "request body is empty")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
return resp.R400(c, "INVALID_REQUEST", nil, "invalid request body")
|
||||
}
|
||||
|
||||
if req.Username == "" || req.Password == "" {
|
||||
return resp.R400(c, "MISSING_FIELDS", nil, "username and password are required")
|
||||
}
|
||||
|
||||
if err := tool.CheckPassword(req.Password); err != nil {
|
||||
return resp.R400(c, "WEAK_PASSWORD", nil, err.Error())
|
||||
}
|
||||
|
||||
var existing auth.User
|
||||
if err := db.Unscoped().Where("username = ?", req.Username).First(&existing).Error; err == nil {
|
||||
return resp.R400(c, "USER_EXISTS", nil, "username already exists")
|
||||
}
|
||||
|
||||
user := &auth.User{
|
||||
Username: req.Username,
|
||||
Password: tool.NewPassword(req.Password),
|
||||
Email: req.Email,
|
||||
Nickname: req.Nickname,
|
||||
Role: req.Role,
|
||||
Status: req.Status,
|
||||
Permissions: req.Permissions,
|
||||
}
|
||||
|
||||
if user.Role == "" {
|
||||
user.Role = "user"
|
||||
}
|
||||
if user.Status == "" {
|
||||
user.Status = "active"
|
||||
}
|
||||
|
||||
if err := db.Create(user).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
return resp.R200(c, map[string]interface{}{
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func UserUpdate(ctx context.Context, db *gorm.DB) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
userID := c.Params("id")
|
||||
if userID == "" {
|
||||
return resp.R400(c, "MISSING_ID", nil, "user id is required")
|
||||
}
|
||||
|
||||
authHeader := c.Get("Authorization")
|
||||
var currentUserID uint
|
||||
if authHeader != "" {
|
||||
tokenString := authHeader
|
||||
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
||||
tokenString = authHeader[7:]
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(JWTSecret), nil
|
||||
})
|
||||
|
||||
if err == nil && token.Valid {
|
||||
if claims, ok := token.Claims.(*Claims); ok {
|
||||
currentUserID = claims.UserID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetUserID, _ := strconv.ParseUint(userID, 10, 32)
|
||||
isSelf := currentUserID == uint(targetUserID)
|
||||
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
Permissions string `json:"permissions"`
|
||||
}
|
||||
|
||||
body := c.Body()
|
||||
if len(body) == 0 {
|
||||
return resp.R400(c, "EMPTY_BODY", nil, "request body is empty")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
return resp.R400(c, "INVALID_REQUEST", nil, "invalid request body")
|
||||
}
|
||||
|
||||
var user auth.User
|
||||
if err := db.First(&user, userID).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "USER_NOT_FOUND", nil, "user not found")
|
||||
}
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
user.Email = req.Email
|
||||
user.Nickname = req.Nickname
|
||||
|
||||
if req.Password != "" {
|
||||
if err := tool.CheckPassword(req.Password); err != nil {
|
||||
return resp.R400(c, "WEAK_PASSWORD", nil, err.Error())
|
||||
}
|
||||
user.Password = tool.NewPassword(req.Password)
|
||||
}
|
||||
|
||||
if !isSelf {
|
||||
user.Role = req.Role
|
||||
user.Status = req.Status
|
||||
user.Permissions = req.Permissions
|
||||
}
|
||||
|
||||
if err := db.Save(&user).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
return resp.R200(c, map[string]interface{}{
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func UserDelete(ctx context.Context, db *gorm.DB) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
userID := c.Params("id")
|
||||
if userID == "" {
|
||||
return resp.R400(c, "MISSING_ID", nil, "user id is required")
|
||||
}
|
||||
|
||||
var user auth.User
|
||||
if err := db.First(&user, userID).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "USER_NOT_FOUND", nil, "user not found")
|
||||
}
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
if user.Username == "admin" {
|
||||
return resp.R403(c, "CANNOT_DELETE_ADMIN", nil, "cannot delete admin user")
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&auth.User{}).Count(&count).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
if count <= 1 {
|
||||
return resp.R403(c, "LAST_USER", nil, "cannot delete the last user")
|
||||
}
|
||||
|
||||
if err := db.Delete(&user).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
return resp.R200(c, map[string]interface{}{
|
||||
"message": "user deleted successfully",
|
||||
})
|
||||
}
|
||||
}
|
||||
108
internal/module/auth/wallpaper.go
Normal file
108
internal/module/auth/wallpaper.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
func Wallpaper(ctx context.Context) fiber.Handler {
|
||||
type Result struct {
|
||||
Images []struct {
|
||||
Startdate string `json:"startdate"`
|
||||
Fullstartdate string `json:"fullstartdate"`
|
||||
Enddate string `json:"enddate"`
|
||||
URL string `json:"url"`
|
||||
Urlbase string `json:"urlbase"`
|
||||
Copyright string `json:"copyright"`
|
||||
Copyrightlink string `json:"copyrightlink"`
|
||||
Title string `json:"title"`
|
||||
Quiz string `json:"quiz"`
|
||||
Wp bool `json:"wp"`
|
||||
Hsh string `json:"hsh"`
|
||||
Drk int `json:"drk"`
|
||||
Top int `json:"top"`
|
||||
Bot int `json:"bot"`
|
||||
} `json:"images"`
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
sync.Mutex
|
||||
Date string `json:"date"`
|
||||
Body []byte `json:"body"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
|
||||
client := resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
apiUrl := "https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1"
|
||||
imgUrlPrefix := "https://cn.bing.com"
|
||||
store := &Store{}
|
||||
|
||||
get := func() ([]byte, map[string]string, error) {
|
||||
var (
|
||||
err error
|
||||
rr *resty.Response
|
||||
result = new(Result)
|
||||
headers = make(map[string]string)
|
||||
date = time.Now().Format("2006-01-02")
|
||||
)
|
||||
|
||||
if store.Date == date {
|
||||
return store.Body, store.Headers, nil
|
||||
}
|
||||
|
||||
if _, err = client.R().
|
||||
SetResult(result).
|
||||
Get(apiUrl); err != nil {
|
||||
return nil, nil, fmt.Errorf("[BingWallpaper] get %s err: %w", apiUrl, err)
|
||||
}
|
||||
|
||||
if len(result.Images) == 0 {
|
||||
err = errors.New("[BingWallpaper]: image length = 0")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
address := fmt.Sprintf("%s%s", imgUrlPrefix, result.Images[0].URL)
|
||||
|
||||
if rr, err = client.R().
|
||||
Get(address); err != nil {
|
||||
err = fmt.Errorf("[BingWallpaper] get image body: %s err: %w", address, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for key := range rr.Header() {
|
||||
headers[key] = strings.Join(rr.Header()[key], ", ")
|
||||
}
|
||||
|
||||
store.Lock()
|
||||
store.Date = date
|
||||
store.Body = rr.Body()
|
||||
store.Headers = headers
|
||||
store.Unlock()
|
||||
|
||||
return rr.Body(), headers, nil
|
||||
}
|
||||
|
||||
return func(c fiber.Ctx) error {
|
||||
bs, headers, err := get()
|
||||
if err != nil {
|
||||
return resp.R500(c, "", nil, err.Error())
|
||||
}
|
||||
|
||||
for key := range headers {
|
||||
c.Set(key, headers[key])
|
||||
}
|
||||
|
||||
_, err = c.Write(bs)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
k8s "gitea.loveuer.com/loveuer/cluster/pkg/model/k8s"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
func ClusterConfigGet(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
var config model.ClusterConfig
|
||||
var config k8s.ClusterConfig
|
||||
|
||||
if err := db.Where("key = ?", "kubeconfig").First(&config).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
@@ -40,7 +40,7 @@ func ClusterConfigSet(ctx context.Context, db *gorm.DB, store store.Store) fiber
|
||||
return resp.R400(c, "", nil, err)
|
||||
}
|
||||
|
||||
config := model.ClusterConfig{
|
||||
config := k8s.ClusterConfig{
|
||||
Key: "kubeconfig",
|
||||
Value: req.Kubeconfig,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
k8sModel "gitea.loveuer.com/loveuer/cluster/pkg/model/k8s"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
)
|
||||
|
||||
func getK8sClient(db *gorm.DB) (*kubernetes.Clientset, error) {
|
||||
var config model.ClusterConfig
|
||||
var config k8sModel.ClusterConfig
|
||||
|
||||
if err := db.Where("key = ?", "kubeconfig").First(&config).Error; err != nil {
|
||||
return nil, fmt.Errorf("kubeconfig not found: %w", err)
|
||||
@@ -52,7 +52,7 @@ func getK8sClient(db *gorm.DB) (*kubernetes.Clientset, error) {
|
||||
}
|
||||
|
||||
func getK8sConfig(db *gorm.DB) (*rest.Config, error) {
|
||||
var config model.ClusterConfig
|
||||
var config k8sModel.ClusterConfig
|
||||
|
||||
if err := db.Where("key = ?", "kubeconfig").First(&config).Error; err != nil {
|
||||
return nil, fmt.Errorf("kubeconfig not found: %w", err)
|
||||
@@ -428,7 +428,7 @@ func K8sResourceApply(ctx context.Context, db *gorm.DB, store store.Store) fiber
|
||||
return resp.R400(c, "", nil, fmt.Errorf("yaml content is empty"))
|
||||
}
|
||||
|
||||
var config model.ClusterConfig
|
||||
var config k8sModel.ClusterConfig
|
||||
if err := db.Where("key = ?", "kubeconfig").First(&config).Error; err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("kubeconfig not found: %w", err))
|
||||
}
|
||||
@@ -636,7 +636,7 @@ func K8sResourceUpdate(ctx context.Context, db *gorm.DB, store store.Store) fibe
|
||||
return resp.R400(c, "", nil, fmt.Errorf("yaml content is empty"))
|
||||
}
|
||||
|
||||
var config model.ClusterConfig
|
||||
var config k8sModel.ClusterConfig
|
||||
if err := db.Where("key = ?", "kubeconfig").First(&config).Error; err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("kubeconfig not found: %w", err))
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
k8s "gitea.loveuer.com/loveuer/cluster/pkg/model/k8s"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Init(ctx context.Context, db *gorm.DB, store store.Store) error {
|
||||
if err := db.AutoMigrate(
|
||||
&model.ClusterConfig{},
|
||||
&k8s.ClusterConfig{},
|
||||
); err != nil {
|
||||
log.Fatalf("failed to migrate k8s database: %v", err)
|
||||
return err
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -51,7 +51,7 @@ func HandleBlobs(c fiber.Ctx, db *gorm.DB, store store.Store) error {
|
||||
repo := strings.Join(parts[:blobsIndex], "/")
|
||||
|
||||
// Strip registry_address prefix from repo if present
|
||||
var registryConfig model.RegistryConfig
|
||||
var registryConfig registry.RegistryConfig
|
||||
registryAddress := ""
|
||||
if err := db.Where("key = ?", "registry_address").First(®istryConfig).Error; err == nil {
|
||||
registryAddress = registryConfig.Value
|
||||
@@ -120,7 +120,7 @@ func handleBlobUploadStart(c fiber.Ctx, db *gorm.DB, store store.Store, repo str
|
||||
uuid := hex.EncodeToString(uuidBytes)
|
||||
|
||||
// ??????
|
||||
upload := &model.BlobUpload{
|
||||
upload := ®istry.BlobUpload{
|
||||
UUID: uuid,
|
||||
Repository: repo,
|
||||
Path: uuid, // ?? UUID ??????
|
||||
@@ -150,7 +150,7 @@ func handleBlobUploadStart(c fiber.Ctx, db *gorm.DB, store store.Store, repo str
|
||||
// handleBlobUploadChunk ?? blob ???
|
||||
func handleBlobUploadChunk(c fiber.Ctx, db *gorm.DB, store store.Store, repo string, uuid string) error {
|
||||
// ??????
|
||||
var upload model.BlobUpload
|
||||
var upload registry.BlobUpload
|
||||
if err := db.Where("uuid = ? AND repository = ?", uuid, repo).First(&upload).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "UPLOAD_NOT_FOUND", nil, "upload session not found")
|
||||
@@ -187,7 +187,7 @@ func handleBlobUploadChunk(c fiber.Ctx, db *gorm.DB, store store.Store, repo str
|
||||
// handleBlobUploadComplete ?? blob ??
|
||||
func handleBlobUploadComplete(c fiber.Ctx, db *gorm.DB, store store.Store, repo string, uuid string, digest string) error {
|
||||
// ??????
|
||||
var upload model.BlobUpload
|
||||
var upload registry.BlobUpload
|
||||
if err := db.Where("uuid = ? AND repository = ?", uuid, repo).First(&upload).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "UPLOAD_NOT_FOUND", nil, "upload session not found")
|
||||
@@ -215,10 +215,10 @@ func handleBlobUploadComplete(c fiber.Ctx, db *gorm.DB, store store.Store, repo
|
||||
}
|
||||
|
||||
// ????? blob ??
|
||||
var blob model.Blob
|
||||
var blob registry.Blob
|
||||
if err := db.Where("digest = ?", digest).First(&blob).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
blob = model.Blob{
|
||||
blob = registry.Blob{
|
||||
Digest: digest,
|
||||
Size: size,
|
||||
Repository: repo,
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -31,7 +31,7 @@ func HandleCatalog(c fiber.Ctx, db *gorm.DB, store store.Store) error {
|
||||
last := c.Query("last")
|
||||
|
||||
// ????
|
||||
var repos []model.Repository
|
||||
var repos []registry.Repository
|
||||
query := db.Order("name ASC").Limit(n + 1)
|
||||
|
||||
if last != "" {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// RegistryConfigGet returns the registry configuration
|
||||
func RegistryConfigGet(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
var configs []model.RegistryConfig
|
||||
var configs []registry.RegistryConfig
|
||||
if err := db.Find(&configs).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
@@ -53,11 +53,11 @@ func RegistryConfigSet(ctx context.Context, db *gorm.DB, store store.Store) fibe
|
||||
}
|
||||
|
||||
// Find or create config
|
||||
var config model.RegistryConfig
|
||||
var config registry.RegistryConfig
|
||||
err := db.Where("key = ?", req.Key).First(&config).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// Create new config
|
||||
config = model.RegistryConfig{
|
||||
config = registry.RegistryConfig{
|
||||
Key: req.Key,
|
||||
Value: req.Value,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -39,7 +39,7 @@ func RegistryImageDownload(ctx context.Context, db *gorm.DB, store store.Store)
|
||||
log.Printf("[Download] Start downloading: %s", fullImageName)
|
||||
|
||||
// Get current registry_address to strip it from the request
|
||||
var registryConfig model.RegistryConfig
|
||||
var registryConfig registry.RegistryConfig
|
||||
registryAddress := ""
|
||||
if err := db.Where("key = ?", "registry_address").First(®istryConfig).Error; err == nil {
|
||||
registryAddress = registryConfig.Value
|
||||
@@ -71,7 +71,7 @@ func RegistryImageDownload(ctx context.Context, db *gorm.DB, store store.Store)
|
||||
|
||||
// Find the repository
|
||||
t1 := time.Now()
|
||||
var repositoryModel model.Repository
|
||||
var repositoryModel registry.Repository
|
||||
if err := db.Where("name = ?", repository).First(&repositoryModel).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "IMAGE_NOT_FOUND", nil, fmt.Sprintf("image %s not found", repository))
|
||||
@@ -82,7 +82,7 @@ func RegistryImageDownload(ctx context.Context, db *gorm.DB, store store.Store)
|
||||
|
||||
// Find the tag record
|
||||
t2 := time.Now()
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND tag = ?", repository, tag).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// Try to get the first available tag
|
||||
@@ -102,7 +102,7 @@ func RegistryImageDownload(ctx context.Context, db *gorm.DB, store store.Store)
|
||||
|
||||
// Get the manifest
|
||||
t3 := time.Now()
|
||||
var manifest model.Manifest
|
||||
var manifest registry.Manifest
|
||||
if err := db.Where("digest = ?", tagRecord.Digest).First(&manifest).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found")
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -161,7 +161,7 @@ func pullImage(ctx context.Context, db *gorm.DB, store store.Store, repo string,
|
||||
log.Printf("[PullImage] Got manifest with %d layers", len(manifest.Layers))
|
||||
|
||||
// Create repository
|
||||
if err := db.FirstOrCreate(&model.Repository{}, model.Repository{Name: repo}).Error; err != nil {
|
||||
if err := db.FirstOrCreate(®istry.Repository{}, registry.Repository{Name: repo}).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to create repository: %w", err)
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func pullImage(ctx context.Context, db *gorm.DB, store store.Store, repo string,
|
||||
return nil, fmt.Errorf("failed to write config blob: %w", err)
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: digest,
|
||||
Size: manifest.Config.Size,
|
||||
MediaType: "application/vnd.docker.container.image.v1+json",
|
||||
@@ -229,7 +229,7 @@ func pullImage(ctx context.Context, db *gorm.DB, store store.Store, repo string,
|
||||
return nil, fmt.Errorf("failed to write layer blob %d: %w", idx, err)
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: layerDigest,
|
||||
Size: layerDesc.Size,
|
||||
MediaType: string(layerDesc.MediaType),
|
||||
@@ -275,7 +275,7 @@ func pullImage(ctx context.Context, db *gorm.DB, store store.Store, repo string,
|
||||
return nil, fmt.Errorf("failed to write manifest: %w", err)
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Manifest{
|
||||
if err := db.Create(®istry.Manifest{
|
||||
Repository: repo,
|
||||
Tag: tag,
|
||||
Digest: manifestDigest,
|
||||
@@ -286,7 +286,7 @@ func pullImage(ctx context.Context, db *gorm.DB, store store.Store, repo string,
|
||||
return nil, fmt.Errorf("failed to save manifest: %w", err)
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Tag{
|
||||
if err := db.Create(®istry.Tag{
|
||||
Repository: repo,
|
||||
Tag: tag,
|
||||
Digest: manifestDigest,
|
||||
|
||||
@@ -3,7 +3,7 @@ package registry
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -17,7 +17,7 @@ func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fibe
|
||||
filter := c.Query("filter", "")
|
||||
|
||||
// Get current registry_address setting
|
||||
var registryConfig model.RegistryConfig
|
||||
var registryConfig registry.RegistryConfig
|
||||
registryAddress := ""
|
||||
if err := db.Where("key = ?", "registry_address").First(®istryConfig).Error; err == nil {
|
||||
registryAddress = registryConfig.Value
|
||||
@@ -26,7 +26,7 @@ func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fibe
|
||||
registryAddress = "localhost:9119"
|
||||
}
|
||||
|
||||
var repositories []model.Repository
|
||||
var repositories []registry.Repository
|
||||
|
||||
// Query all repositories from the database
|
||||
query := db
|
||||
@@ -41,7 +41,7 @@ func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fibe
|
||||
var result []map[string]interface{}
|
||||
for _, repo := range repositories {
|
||||
// Get all tags for this repository
|
||||
var tags []model.Tag
|
||||
var tags []registry.Tag
|
||||
if err := db.Where("repository = ?", repo.Name).Find(&tags).Error; err != nil {
|
||||
continue // Skip this repository if we can't get tags
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fibe
|
||||
var sizeResult struct {
|
||||
Total int64
|
||||
}
|
||||
err := db.Model(&model.Blob{}).
|
||||
err := db.Model(®istry.Blob{}).
|
||||
Where("repository = ?", repo.Name).
|
||||
Select("COALESCE(SUM(size), 0) as total").
|
||||
Scan(&sizeResult).Error
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -142,7 +142,7 @@ func RegistryImageUpload(ctx context.Context, db *gorm.DB, store store.Store) fi
|
||||
// This allows registry_address to be changed without breaking existing images
|
||||
repoName := originalRepo
|
||||
|
||||
if err := db.FirstOrCreate(&model.Repository{}, model.Repository{Name: repoName}).Error; err != nil {
|
||||
if err := db.FirstOrCreate(®istry.Repository{}, registry.Repository{Name: repoName}).Error; err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to create repository: %w", err))
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ func RegistryImageUpload(ctx context.Context, db *gorm.DB, store store.Store) fi
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write config blob: %w", err))
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: configDigest,
|
||||
Size: int64(len(configContent)),
|
||||
MediaType: "application/vnd.docker.container.image.v1+json",
|
||||
@@ -179,7 +179,7 @@ func RegistryImageUpload(ctx context.Context, db *gorm.DB, store store.Store) fi
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write layer blob: %w", err))
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: digest,
|
||||
Size: int64(len(content)),
|
||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
@@ -218,7 +218,7 @@ func RegistryImageUpload(ctx context.Context, db *gorm.DB, store store.Store) fi
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write manifest: %w", err))
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Manifest{
|
||||
if err := db.Create(®istry.Manifest{
|
||||
Repository: repoName,
|
||||
Tag: tag,
|
||||
Digest: manifestDigest,
|
||||
@@ -229,7 +229,7 @@ func RegistryImageUpload(ctx context.Context, db *gorm.DB, store store.Store) fi
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to save manifest: %w", err))
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Tag{
|
||||
if err := db.Create(®istry.Tag{
|
||||
Repository: repoName,
|
||||
Tag: tag,
|
||||
Digest: manifestDigest,
|
||||
@@ -277,7 +277,7 @@ func handleOCIFormat(c fiber.Ctx, db *gorm.DB, store store.Store, ociIndex *OCII
|
||||
}
|
||||
|
||||
// Create repository
|
||||
if err := db.FirstOrCreate(&model.Repository{}, model.Repository{Name: repoName}).Error; err != nil {
|
||||
if err := db.FirstOrCreate(®istry.Repository{}, registry.Repository{Name: repoName}).Error; err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to create repository: %w", err))
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ func handleOCIFormat(c fiber.Ctx, db *gorm.DB, store store.Store, ociIndex *OCII
|
||||
if err := store.WriteBlob(c.Context(), indexDigest, strings.NewReader(string(indexJSON))); err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write index blob: %w", err))
|
||||
}
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: indexDigest,
|
||||
Size: int64(len(indexJSON)),
|
||||
MediaType: "application/vnd.oci.image.index.v1+json",
|
||||
@@ -323,7 +323,7 @@ func handleOCIFormat(c fiber.Ctx, db *gorm.DB, store store.Store, ociIndex *OCII
|
||||
if err := store.WriteBlob(c.Context(), ociManifest.Config.Digest, strings.NewReader(string(configContent))); err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write config blob: %w", err))
|
||||
}
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: ociManifest.Config.Digest,
|
||||
Size: ociManifest.Config.Size,
|
||||
MediaType: ociManifest.Config.MediaType,
|
||||
@@ -343,7 +343,7 @@ func handleOCIFormat(c fiber.Ctx, db *gorm.DB, store store.Store, ociIndex *OCII
|
||||
if err := store.WriteBlob(c.Context(), layer.Digest, strings.NewReader(string(layerContent))); err != nil {
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write layer blob: %w", err))
|
||||
}
|
||||
if err := db.Create(&model.Blob{
|
||||
if err := db.Create(®istry.Blob{
|
||||
Digest: layer.Digest,
|
||||
Size: layer.Size,
|
||||
MediaType: layer.MediaType,
|
||||
@@ -387,7 +387,7 @@ func handleOCIFormat(c fiber.Ctx, db *gorm.DB, store store.Store, ociIndex *OCII
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to write manifest: %w", err))
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Manifest{
|
||||
if err := db.Create(®istry.Manifest{
|
||||
Repository: repoName,
|
||||
Tag: tag,
|
||||
Digest: manifestDigest,
|
||||
@@ -398,7 +398,7 @@ func handleOCIFormat(c fiber.Ctx, db *gorm.DB, store store.Store, ociIndex *OCII
|
||||
return resp.R500(c, "", nil, fmt.Errorf("failed to save manifest: %w", err))
|
||||
}
|
||||
|
||||
if err := db.Create(&model.Tag{
|
||||
if err := db.Create(®istry.Tag{
|
||||
Repository: repoName,
|
||||
Tag: tag,
|
||||
Digest: manifestDigest,
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -20,28 +20,28 @@ func isDigestFormat(s string) bool {
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
algo := parts[0]
|
||||
hash := parts[1]
|
||||
|
||||
|
||||
// Check algorithm
|
||||
if algo != "sha256" {
|
||||
// Could be extended to support other algorithms like sha512
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Check that hash is a valid hex string of expected length (64 for sha256)
|
||||
if len(hash) != 64 {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Verify it's all hex characters
|
||||
for _, r := range hash {
|
||||
if !((r >= '0' && r <= '9') || (r >= 'a' && r <= 'f')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -75,9 +75,9 @@ func HandleManifest(c fiber.Ctx, db *gorm.DB, store store.Store) error {
|
||||
|
||||
// ???? manifests ???????
|
||||
repo := strings.Join(parts[:manifestsIndex], "/")
|
||||
|
||||
|
||||
// Strip registry_address prefix from repo if present
|
||||
var registryConfig model.RegistryConfig
|
||||
var registryConfig registry.RegistryConfig
|
||||
registryAddress := ""
|
||||
if err := db.Where("key = ?", "registry_address").First(®istryConfig).Error; err == nil {
|
||||
registryAddress = registryConfig.Value
|
||||
@@ -85,7 +85,7 @@ func HandleManifest(c fiber.Ctx, db *gorm.DB, store store.Store) error {
|
||||
if registryAddress != "" && strings.HasPrefix(repo, registryAddress+"/") {
|
||||
repo = strings.TrimPrefix(repo, registryAddress+"/")
|
||||
}
|
||||
|
||||
|
||||
// tag ? manifests ?????
|
||||
tag := parts[manifestsIndex+1]
|
||||
|
||||
@@ -140,10 +140,10 @@ func handleManifestPut(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
}
|
||||
|
||||
// ??????
|
||||
var repository model.Repository
|
||||
var repository registry.Repository
|
||||
if err := db.Where("name = ?", repo).First(&repository).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
repository = model.Repository{Name: repo}
|
||||
repository = registry.Repository{Name: repo}
|
||||
if err := db.Create(&repository).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
@@ -158,11 +158,11 @@ func handleManifestPut(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
}
|
||||
|
||||
// ?? manifest ?????
|
||||
var manifest model.Manifest
|
||||
var manifest registry.Manifest
|
||||
if err := db.Where("digest = ?", digest).First(&manifest).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// ???? manifest ??
|
||||
manifest = model.Manifest{
|
||||
manifest = registry.Manifest{
|
||||
Repository: repo,
|
||||
Tag: tag,
|
||||
Digest: digest,
|
||||
@@ -186,10 +186,10 @@ func handleManifestPut(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
}
|
||||
|
||||
// ????? tag ??
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND tag = ?", repo, tag).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
tagRecord = model.Tag{
|
||||
tagRecord = registry.Tag{
|
||||
Repository: repo,
|
||||
Tag: tag,
|
||||
Digest: digest,
|
||||
@@ -217,13 +217,13 @@ func handleManifestPut(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
|
||||
// handleManifestGet ?? manifest
|
||||
func handleManifestGet(c fiber.Ctx, db *gorm.DB, store store.Store, repo string, tag string) error {
|
||||
var manifest model.Manifest
|
||||
var manifest registry.Manifest
|
||||
|
||||
// ?? tag ??????????????????????
|
||||
if isDigestFormat(tag) {
|
||||
// ?? digest ???????????? repository
|
||||
digest := tag
|
||||
|
||||
|
||||
// ?? manifest ???
|
||||
if err := db.Where("digest = ?", digest).First(&manifest).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
@@ -231,9 +231,9 @@ func handleManifestGet(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
}
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
|
||||
// ???? manifest ?????????? repository ??
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND digest = ?", repo, digest).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found in this repository")
|
||||
@@ -242,7 +242,7 @@ func handleManifestGet(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
}
|
||||
} else {
|
||||
// ?? tag ???? tag ?????????
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND tag = ?", repo, tag).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found")
|
||||
@@ -302,13 +302,13 @@ func handleManifestGet(c fiber.Ctx, db *gorm.DB, store store.Store, repo string,
|
||||
|
||||
// handleManifestHead ?? manifest ????
|
||||
func handleManifestHead(c fiber.Ctx, db *gorm.DB, store store.Store, repo string, tag string) error {
|
||||
var manifest model.Manifest
|
||||
var manifest registry.Manifest
|
||||
|
||||
// ?? tag ??????????????????????
|
||||
if isDigestFormat(tag) {
|
||||
// ?? digest ???????????? repository
|
||||
digest := tag
|
||||
|
||||
|
||||
// ?? manifest ???
|
||||
if err := db.Where("digest = ?", digest).First(&manifest).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
@@ -316,9 +316,9 @@ func handleManifestHead(c fiber.Ctx, db *gorm.DB, store store.Store, repo string
|
||||
}
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
|
||||
// ???? manifest ?????????? repository ??
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND digest = ?", repo, digest).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found in this repository")
|
||||
@@ -327,7 +327,7 @@ func handleManifestHead(c fiber.Ctx, db *gorm.DB, store store.Store, repo string
|
||||
}
|
||||
} else {
|
||||
// ?? tag ???? tag ?????????
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND tag = ?", repo, tag).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found")
|
||||
@@ -358,32 +358,32 @@ func handleManifestDelete(c fiber.Ctx, db *gorm.DB, store store.Store, repo stri
|
||||
if isDigestFormat(tag) {
|
||||
// ?? digest ???????????? repository
|
||||
digest = tag
|
||||
|
||||
|
||||
// ???? manifest ?????????? repository ??
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND digest = ?", repo, digest).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found in this repository")
|
||||
}
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
|
||||
// ???????? tag ??? manifest
|
||||
var count int64
|
||||
if err := db.Model(&model.Tag{}).Where("digest = ?", digest).Count(&count).Error; err != nil {
|
||||
if err := db.Model(®istry.Tag{}).Where("digest = ?", digest).Count(&count).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
// ??? tag ??????? manifest ??
|
||||
if count == 0 {
|
||||
var manifest model.Manifest
|
||||
var manifest registry.Manifest
|
||||
if err := db.Where("digest = ?", digest).First(&manifest).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found")
|
||||
}
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
|
||||
if err := db.Delete(&manifest).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
@@ -394,7 +394,7 @@ func handleManifestDelete(c fiber.Ctx, db *gorm.DB, store store.Store, repo stri
|
||||
}
|
||||
} else {
|
||||
// ?? tag ???? tag ?????????
|
||||
var tagRecord model.Tag
|
||||
var tagRecord registry.Tag
|
||||
if err := db.Where("repository = ? AND tag = ?", repo, tag).First(&tagRecord).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return resp.R404(c, "MANIFEST_NOT_FOUND", nil, "manifest not found")
|
||||
@@ -411,13 +411,13 @@ func handleManifestDelete(c fiber.Ctx, db *gorm.DB, store store.Store, repo stri
|
||||
|
||||
// ???????? tag ??? manifest
|
||||
var count int64
|
||||
if err := db.Model(&model.Tag{}).Where("digest = ?", digest).Count(&count).Error; err != nil {
|
||||
if err := db.Model(®istry.Tag{}).Where("digest = ?", digest).Count(&count).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
// ?????? tag ????? manifest ??
|
||||
if count == 0 {
|
||||
var manifest model.Manifest
|
||||
var manifest registry.Manifest
|
||||
if err := db.Where("digest = ?", digest).First(&manifest).Error; err == nil {
|
||||
if err := db.Delete(&manifest).Error; err != nil {
|
||||
return resp.R500(c, "", nil, err)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
registry "gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -14,12 +14,12 @@ import (
|
||||
|
||||
func Registry(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handler {
|
||||
if err := db.AutoMigrate(
|
||||
&model.Repository{},
|
||||
&model.Blob{},
|
||||
&model.Manifest{},
|
||||
&model.Tag{},
|
||||
&model.BlobUpload{},
|
||||
&model.RegistryConfig{},
|
||||
®istry.Repository{},
|
||||
®istry.Blob{},
|
||||
®istry.Manifest{},
|
||||
®istry.Tag{},
|
||||
®istry.BlobUpload{},
|
||||
®istry.RegistryConfig{},
|
||||
); err != nil {
|
||||
log.Fatalf("failed to migrate database: %v", err)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.loveuer.com/loveuer/cluster/internal/model"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/model/registry"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
||||
"gitea.loveuer.com/loveuer/cluster/pkg/store"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -40,7 +40,7 @@ func HandleTags(c fiber.Ctx, db *gorm.DB, store store.Store) error {
|
||||
repo := strings.Join(parts[:tagsIndex], "/")
|
||||
|
||||
// Strip registry_address prefix from repo if present
|
||||
var registryConfig model.RegistryConfig
|
||||
var registryConfig registry.RegistryConfig
|
||||
registryAddress := ""
|
||||
if err := db.Where("key = ?", "registry_address").First(®istryConfig).Error; err == nil {
|
||||
registryAddress = registryConfig.Value
|
||||
@@ -58,7 +58,7 @@ func HandleTags(c fiber.Ctx, db *gorm.DB, store store.Store) error {
|
||||
last := c.Query("last")
|
||||
|
||||
// ?? tags
|
||||
var tags []model.Tag
|
||||
var tags []registry.Tag
|
||||
query := db.Where("repository = ?", repo).Order("tag ASC").Limit(n + 1)
|
||||
|
||||
if last != "" {
|
||||
|
||||
Reference in New Issue
Block a user