package artefact import ( "crypto/sha256" "errors" "fmt" "io" "net/http" "os" "path/filepath" "strconv" "github.com/gin-gonic/gin" "gorm.io/gorm" "onixbyte.com/pipely/internal/database" "onixbyte.com/pipely/internal/model" ) type Handler struct { db *gorm.DB artefactDir string } func NewHandler(db *gorm.DB, artefactDir string) *Handler { return &Handler{db: db, artefactDir: artefactDir} } func (h *Handler) Upload(c *gin.Context) { version := c.PostForm("version") if version == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "version is required"}) return } deltaRef := c.PostForm("deltaRef") metadata := c.PostForm("metadata") if metadata == "" { metadata = "{}" } file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "file is required: " + err.Error()}) return } defer file.Close() destDir := filepath.Join(h.artefactDir, version) if err := os.MkdirAll(destDir, 0755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create directory"}) return } destPath := filepath.Join(destDir, header.Filename) out, err := os.Create(destPath) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create file"}) return } defer out.Close() hasher := sha256.New() writer := io.MultiWriter(out, hasher) size, err := io.Copy(writer, file) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to write file"}) return } sha256Hash := fmt.Sprintf("%x", hasher.Sum(nil)) artefact := &model.Artefact{ Version: version, FileName: header.Filename, FilePath: destPath, FileSize: size, SHA256Hash: sha256Hash, DeltaRef: deltaRef, IsDelta: deltaRef != "", Metadata: metadata, } if err := database.CreateArtefact(h.db, artefact); err != nil { os.Remove(destPath) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save artefact: " + err.Error()}) return } c.JSON(http.StatusCreated, artefact) } func (h *Handler) List(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } artefacts, total, err := database.ListArtefacts(h.db, page, pageSize) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, model.PaginatedResponse{ Data: artefacts, Total: total, Page: page, PageSize: pageSize, }) } func (h *Handler) Get(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } artefact, err := database.GetArtefactByID(h.db, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "Artefact not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, artefact) }