124 lines
3.6 KiB
Go
124 lines
3.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"path"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/minio/minio-go/v7"
|
|
"gorm.io/gorm"
|
|
|
|
"smoop-api/internal/dto"
|
|
"smoop-api/internal/models"
|
|
)
|
|
|
|
type RecordsHandler struct {
|
|
db *gorm.DB
|
|
minio *minio.Client
|
|
recordsBucket string
|
|
presignTTL time.Duration
|
|
}
|
|
|
|
func NewRecordsHandler(db *gorm.DB, mc *minio.Client, bucket string, ttl time.Duration) *RecordsHandler {
|
|
return &RecordsHandler{db: db, minio: mc, recordsBucket: bucket, presignTTL: ttl}
|
|
}
|
|
|
|
func (h *RecordsHandler) Upload(c *gin.Context) {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "file required"})
|
|
return
|
|
}
|
|
guid := c.PostForm("guid")
|
|
if guid == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "guid required"})
|
|
return
|
|
}
|
|
startedAt, _ := strconv.ParseInt(c.PostForm("startedAt"), 10, 64)
|
|
stoppedAt, _ := strconv.ParseInt(c.PostForm("stoppedAt"), 10, 64)
|
|
|
|
var dev models.Device
|
|
if err := h.db.Where("guid = ?", guid).First(&dev).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "device not found"})
|
|
return
|
|
}
|
|
|
|
objKey := fmt.Sprintf("%s/%d_%s", guid, time.Now().UnixNano(), path.Base(file.Filename))
|
|
if err := h.putFile(c, file, h.recordsBucket, objKey); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "upload failed"})
|
|
return
|
|
}
|
|
|
|
rec := models.Record{
|
|
DeviceGUID: dev.GUID,
|
|
StartedAt: startedAt,
|
|
StoppedAt: stoppedAt,
|
|
ObjectKey: objKey,
|
|
}
|
|
if err := h.db.Create(&rec).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "db save failed"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, dto.RecordDto{ID: rec.ID, StartedAt: rec.StartedAt, StoppedAt: rec.StoppedAt})
|
|
}
|
|
|
|
func (h *RecordsHandler) putFile(c *gin.Context, fh *multipart.FileHeader, bucket, object string) error {
|
|
src, err := fh.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer src.Close()
|
|
_, err = h.minio.PutObject(c, bucket, object, src, fh.Size, minio.PutObjectOptions{
|
|
ContentType: fh.Header.Get("Content-Type"),
|
|
ContentDisposition: "attachment; filename=" + fh.Filename,
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (h *RecordsHandler) List(c *gin.Context) {
|
|
guid := c.Query("guid")
|
|
if guid == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "guid is required"})
|
|
return
|
|
}
|
|
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
if limit <= 0 || limit > 200 {
|
|
limit = 50
|
|
}
|
|
|
|
var total int64
|
|
h.db.Model(&models.Record{}).Where("device_guid = ?", guid).Count(&total)
|
|
|
|
var recs []models.Record
|
|
if err := h.db.Where("device_guid = ?", guid).Order("id desc").Offset(offset).Limit(limit).Find(&recs).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
|
|
return
|
|
}
|
|
out := make([]dto.RecordDto, 0, len(recs))
|
|
for _, r := range recs {
|
|
out = append(out, dto.RecordDto{ID: r.ID, StartedAt: r.StartedAt, StoppedAt: r.StoppedAt})
|
|
}
|
|
c.JSON(http.StatusOK, dto.RecordListDto{Records: out, Offset: offset, Limit: limit, Total: total})
|
|
}
|
|
|
|
func (h *RecordsHandler) File(c *gin.Context) {
|
|
id, _ := strconv.Atoi(c.Param("id"))
|
|
var rec models.Record
|
|
if err := h.db.First(&rec, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
|
return
|
|
}
|
|
u, err := h.minio.PresignedGetObject(context.Background(), h.recordsBucket, rec.ObjectKey, h.presignTTL, nil)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "presign failed"})
|
|
return
|
|
}
|
|
c.Redirect(http.StatusFound, u.String())
|
|
}
|