Files

302 lines
9.0 KiB
Go

package versions_handler
import (
"errors"
"gorm.io/gorm"
"log"
"net/http"
"strconv"
"autolibrary.top/webserver/backend/database/versions_database"
"autolibrary.top/webserver/backend/models/versions_model"
"autolibrary.top/webserver/utils"
"github.com/gin-gonic/gin"
)
/*
These handlers are used to manage download versions.
*/
// GetLatestDownload returns the newest published stable release, or null.
func GetLatestDownload(c *gin.Context) {
var version versions_model.DownloadVersion
err := versions_database.DB.Where("type = ? AND release_date <= ?", "stable", utils.NowInShanghai()).Order("release_date DESC, id DESC").First(&version).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "No stable release available", "data": nil})
return
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "msg": "Unable to load latest release", "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "Latest stable release", "data": version})
}
// GET /download/versions?page={number}&per_page={number<=50}
func GetDownloadVersions(c *gin.Context) {
// Parse page and perPage parameters
pageStr := c.Query("page")
perPageStr := c.Query("per_page")
// Default values for page and perPage
if pageStr == "" {
pageStr = "1"
}
if perPageStr == "" {
perPageStr = "9"
}
page, err := strconv.Atoi(pageStr)
if err != nil {
log.Printf("[E] GET > try to get downloads, but got invalid parameter: 'page=%s'", pageStr)
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "GET > try to get downloads, but got invalid parameter: 'page'",
"data": nil,
})
return
}
perPage, err := strconv.Atoi(perPageStr)
if err != nil {
log.Printf("[E] GET > try to get downloads (page=%d), but got invalid parameter: 'per_page=%s'", page, perPageStr)
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "GET > try to get downloads, but got invalid parameter: 'per_page'",
"data": nil,
})
return
}
// constrain page and perPage parameters
if page <= 0 {
page = 1
}
if perPage <= 0 || perPage > 50 {
perPage = 9
}
// Calculate offset
offset := (page - 1) * perPage
var downloads []versions_model.DownloadVersion
result := versions_database.DB.Order("id DESC").Limit(perPage).Offset(offset).Find(&downloads)
if result.Error != nil {
log.Printf("[E] GET > occur an error when get downloads (page=%d, per_page=%d): %v", page, perPage, result.Error)
c.JSON(http.StatusInternalServerError, gin.H{
"code": http.StatusInternalServerError,
"msg": "GET > occur an error when get downloads",
"data": nil,
})
return
}
var total int64
result = versions_database.DB.Model(&versions_model.DownloadVersion{}).Count(&total)
if result.Error != nil {
log.Printf("[E] GET > occur an error when get downloads (page=%d, per_page=%d): %v", page, perPage, result.Error)
c.JSON(http.StatusInternalServerError, gin.H{
"code": http.StatusInternalServerError,
"msg": "GET > occur an error when get downloads",
"data": nil,
})
return
}
totalPages := int(total) / perPage
if int(total)%perPage != 0 {
totalPages++
}
pageMeta := gin.H{
"page": page,
"per_page": perPage,
"total": total,
"total_pages": totalPages,
"has_next": page < totalPages,
}
log.Printf("[I] GET > get downloads (page=%d, per_page=%d) successful", page, perPage)
c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"msg": "GET > get downloads successful",
"data": gin.H{
"downloads": downloads,
"meta": pageMeta,
},
})
}
// GET /download/versions/:id
func GetDownloadVersionByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
log.Printf("[E] GET > try to get download, but got invalid id: '%s'", c.Param("id"))
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "GET > try to get download, but got invalid id",
"data": nil,
})
return
}
var download versions_model.DownloadVersion
result := versions_database.DB.First(&download, id)
if result.Error != nil {
log.Printf("[E] GET > download (id=%d) can not found: %v", id, result.Error)
c.JSON(http.StatusNotFound, gin.H{
"code": http.StatusNotFound,
"msg": "GET > download can not found",
"data": nil,
})
return
}
log.Printf("[I] GET > get download (id=%d) successful", id)
c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"msg": "GET > get download successful",
"data": download,
})
}
// POST /download/versions
func PostDownloadVersion(c *gin.Context) {
var download versions_model.DownloadVersion
if err := c.ShouldBindJSON(&download); err != nil {
log.Printf("[E] POST > try to create download, but got invalid request data: %v", err)
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "POST > try to create download, but got invalid request data",
"data": nil,
})
return
}
download.CreatedAt = utils.NowInShanghai()
result := versions_database.DB.Create(&download)
if result.Error != nil {
log.Printf("[E] POST > occur an error when create download: %v", result.Error)
c.JSON(http.StatusInternalServerError, gin.H{
"code": http.StatusInternalServerError,
"msg": "POST > occur an error when create download",
"data": nil,
})
return
}
id := strconv.FormatUint(uint64(download.ID), 10)
log.Printf("[I] POST > create download (id=%s) successful", id)
c.JSON(http.StatusCreated, gin.H{
"code": http.StatusCreated,
"msg": "POST > create download successful",
"data": download,
})
}
// PUT /download/versions/:id
func PutDownloadVersionByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
log.Printf("[E] PUT > try to update download, but got invalid id: '%s'", c.Param("id"))
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "PUT > try to update download, but got invalid id",
"data": nil,
})
return
}
var updateDownload versions_model.DownloadVersion
if err = c.ShouldBindJSON(&updateDownload); err != nil {
log.Printf("[E] PUT > try to update download (id=%d), but got invalid request data: %v", id, err)
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "PUT > try to update download, but got invalid request data",
"data": nil,
})
return
}
var existingDownload versions_model.DownloadVersion
result := versions_database.DB.First(&existingDownload, id)
if result.Error != nil {
log.Printf("[E] PUT > download (id=%d) can not found: %v", id, result.Error)
c.JSON(http.StatusNotFound, gin.H{
"code": http.StatusNotFound,
"msg": "PUT > download can not found",
"data": nil,
})
return
}
result = versions_database.DB.Model(&versions_model.DownloadVersion{}).Where("id = ?", id).Updates(updateDownload)
if result.Error != nil {
log.Printf("[E] PUT > occur an error when update download (id=%d): %v", id, result.Error)
c.JSON(http.StatusInternalServerError, gin.H{
"code": http.StatusInternalServerError,
"msg": "PUT > occur an error when update download",
"data": nil,
})
return
}
// post check after update
var updatedDownload versions_model.DownloadVersion
if result = versions_database.DB.First(&updatedDownload, id); result.Error == nil {
log.Printf("[I] PUT > update download (id=%d) successful", id)
c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"msg": "PUT > update download successful",
"data": updatedDownload,
})
} else {
log.Printf("[W] PUT > update successful but failed to retrieve updated download (id=%d): %v", id, result.Error)
c.JSON(http.StatusAccepted, gin.H{
"code": http.StatusAccepted,
"msg": "PUT > update successful but failed to retrieve updated download",
"data": nil,
})
}
}
// DELETE /download/versions/:id
func DeleteDownloadVersionByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
log.Printf("[E] DELETE > try to delete download, but got invalid id: %s", c.Param("id"))
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": "DELETE > try to delete download, but got invalid id",
"data": nil,
})
return
}
result := versions_database.DB.Delete(&versions_model.DownloadVersion{}, id)
if result.Error != nil {
log.Printf("[E] DELETE > occur an error when delete download (id=%d): %v", id, result.Error)
c.JSON(http.StatusInternalServerError, gin.H{
"code": http.StatusInternalServerError,
"msg": "DELETE > occur an error when delete download",
"data": nil,
})
return
}
if result.RowsAffected == 0 {
log.Printf("[E] DELETE > try to delete download (id=%d), but RowsAffected is 0", id)
c.JSON(http.StatusNotFound, gin.H{
"code": http.StatusNotFound,
"msg": "DELETE > delete download has no effect",
"data": nil,
})
return
}
log.Printf("[I] DELETE > delete download (id=%d) successful", id)
c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"msg": "DELETE > delete download successful",
"data": nil,
})
}