2024-04-24 21:57:13 +07:00
|
|
|
package routes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2024-05-08 19:32:14 +07:00
|
|
|
"strconv"
|
2024-04-29 21:45:18 +07:00
|
|
|
"time"
|
2024-04-24 21:57:13 +07:00
|
|
|
|
|
|
|
"github.com/aarondl/opt/omit"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"github.com/tigorlazuardi/redmage/models"
|
|
|
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
|
|
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
2024-05-08 19:32:14 +07:00
|
|
|
"github.com/tigorlazuardi/redmage/views/components"
|
2024-04-24 21:57:13 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
type deviceUpdate struct {
|
|
|
|
models.Device
|
|
|
|
|
|
|
|
MinX *int32 `json:"min_x"`
|
|
|
|
MinY *int32 `json:"min_y"`
|
|
|
|
MaxX *int32 `json:"max_x"`
|
|
|
|
MaxY *int32 `json:"max_y"`
|
|
|
|
NSFW *int32 `json:"nsfw"`
|
|
|
|
WindowsWallpaperMode *int32 `json:"windows_wallpaper_mode"`
|
|
|
|
AspectRatioTolerance *float64 `json:"aspect_ratio_tolerance"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (routes *Routes) APIDeviceUpdate(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
var err error
|
|
|
|
ctx, span := tracer.Start(r.Context(), "*Routes.APIDeviceUpdate")
|
|
|
|
defer span.End()
|
|
|
|
|
2024-04-30 11:34:30 +07:00
|
|
|
var (
|
|
|
|
enc = json.NewEncoder(rw)
|
|
|
|
dec = json.NewDecoder(r.Body)
|
|
|
|
)
|
|
|
|
|
2024-04-30 14:12:33 +07:00
|
|
|
slug := chi.URLParam(r, "slug")
|
|
|
|
if slug == "" {
|
2024-04-24 21:57:13 +07:00
|
|
|
rw.WriteHeader(http.StatusBadRequest)
|
2024-04-30 14:12:33 +07:00
|
|
|
_ = json.NewEncoder(rw).Encode(map[string]string{"error": "missing name"})
|
2024-04-24 21:57:13 +07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var body deviceUpdate
|
|
|
|
|
2024-04-30 11:34:30 +07:00
|
|
|
if err = dec.Decode(&body); err != nil {
|
2024-04-24 21:57:13 +07:00
|
|
|
log.New(ctx).Err(err).Error("failed to decode json body")
|
|
|
|
rw.WriteHeader(http.StatusBadRequest)
|
|
|
|
_ = json.NewEncoder(rw).Encode(map[string]string{"error": fmt.Sprintf("cannot decode json body: %s", err)})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-30 14:12:33 +07:00
|
|
|
device, err := routes.API.DevicesUpdate(ctx, slug, &models.DeviceSetter{
|
2024-04-24 21:57:13 +07:00
|
|
|
Name: omit.FromCond(body.Name, body.Name != ""),
|
2024-05-08 19:32:14 +07:00
|
|
|
Enable: omit.FromCond(body.Enable, body.Enable == 1 || body.Enable == 0),
|
2024-04-24 21:57:13 +07:00
|
|
|
ResolutionX: omit.FromCond(body.ResolutionX, body.ResolutionX != 0),
|
|
|
|
ResolutionY: omit.FromCond(body.ResolutionY, body.ResolutionY != 0),
|
|
|
|
AspectRatioTolerance: omit.FromPtr(body.AspectRatioTolerance),
|
|
|
|
MinX: omit.FromPtr(body.MinX),
|
|
|
|
MinY: omit.FromPtr(body.MinY),
|
|
|
|
MaxX: omit.FromPtr(body.MaxX),
|
|
|
|
MaxY: omit.FromPtr(body.MaxY),
|
|
|
|
NSFW: omit.FromPtr(body.NSFW),
|
2024-04-29 21:45:18 +07:00
|
|
|
UpdatedAt: omit.From(time.Now().Unix()),
|
2024-04-24 21:57:13 +07:00
|
|
|
})
|
|
|
|
if err != nil {
|
2024-04-30 11:34:30 +07:00
|
|
|
log.New(ctx).Err(err).Error("failed to update device")
|
2024-04-24 22:26:04 +07:00
|
|
|
code, message := errs.HTTPMessage(err)
|
|
|
|
rw.WriteHeader(code)
|
2024-04-30 11:34:30 +07:00
|
|
|
_ = enc.Encode(map[string]string{"error": message})
|
2024-04-24 21:57:13 +07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-30 11:34:30 +07:00
|
|
|
_ = enc.Encode(device)
|
2024-04-24 21:57:13 +07:00
|
|
|
}
|
2024-05-08 19:32:14 +07:00
|
|
|
|
|
|
|
func (routes *Routes) DevicesUpdateHTMX(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
ctx, span := tracer.Start(req.Context(), "*Routes.DevicesUpdateHTMX")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
slug := chi.URLParam(req, "slug")
|
|
|
|
exist, err := routes.API.DevicesExist(ctx, slug)
|
|
|
|
if err != nil {
|
|
|
|
log.New(ctx).Err(err).Error("failed to check device slug existence")
|
|
|
|
code, message := errs.HTTPMessage(err)
|
|
|
|
rw.WriteHeader(code)
|
|
|
|
if err := components.ErrorToast(message).Render(ctx, rw); err != nil {
|
|
|
|
log.New(ctx).Err(err).Error("failed to render error notification")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exist {
|
|
|
|
rw.WriteHeader(http.StatusNotFound)
|
|
|
|
if err := components.ErrorToast("Device with slug identifier '%s' does not exist", slug).Render(ctx, rw); err != nil {
|
|
|
|
log.New(ctx).Err(err).Error("failed to render error notification")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
device, err := routes.API.DevicesUpdate(ctx, slug, deviceSetterFromRequest(req))
|
|
|
|
if err != nil {
|
|
|
|
log.New(ctx).Err(err).Error("failed to update device")
|
|
|
|
code, message := errs.HTTPMessage(err)
|
|
|
|
rw.WriteHeader(code)
|
|
|
|
if err := components.ErrorToast(message).Render(ctx, rw); err != nil {
|
|
|
|
log.New(ctx).Err(err).Error("failed to render error notification")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
rw.Header().Set("HX-Redirect", fmt.Sprintf("/devices/details/%s", slug))
|
|
|
|
if err := components.SuccessToast("Device %q has been updated", device.Name).Render(ctx, rw); err != nil {
|
|
|
|
log.New(ctx).Err(err).Error("failed to render success notification")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func deviceSetterFromRequest(req *http.Request) *models.DeviceSetter {
|
|
|
|
setter := &models.DeviceSetter{
|
|
|
|
UpdatedAt: omit.From(time.Now().Unix()),
|
|
|
|
}
|
|
|
|
|
|
|
|
name := req.FormValue("name")
|
|
|
|
setter.Name = omit.FromCond(name, name != "")
|
|
|
|
|
|
|
|
enable, err := strconv.Atoi(req.FormValue("enable"))
|
|
|
|
if err == nil {
|
|
|
|
if enable > 1 {
|
|
|
|
enable = 1
|
|
|
|
} else if enable < 0 {
|
|
|
|
enable = 0
|
|
|
|
}
|
|
|
|
setter.Enable = omit.From(int32(enable))
|
|
|
|
}
|
|
|
|
|
|
|
|
resx, _ := strconv.Atoi(req.FormValue("resolution_x"))
|
|
|
|
setter.ResolutionX = omit.FromCond(float64(resx), resx != 0)
|
|
|
|
|
|
|
|
resy, _ := strconv.Atoi(req.FormValue("resolution_y"))
|
|
|
|
setter.ResolutionY = omit.FromCond(float64(resy), resy != 0)
|
|
|
|
|
|
|
|
art, err := strconv.ParseFloat(req.FormValue("aspect_ratio_tolerance"), 64)
|
|
|
|
if err == nil {
|
|
|
|
setter.AspectRatioTolerance = omit.FromCond(art, art >= 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
minX, err := strconv.Atoi(req.FormValue("min_x"))
|
|
|
|
if err == nil {
|
|
|
|
setter.MinX = omit.FromCond(int32(minX), minX >= 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
minY, err := strconv.Atoi(req.FormValue("min_y"))
|
|
|
|
if err == nil {
|
|
|
|
setter.MinY = omit.FromCond(int32(minY), minY >= 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
maxX, err := strconv.Atoi(req.FormValue("max_x"))
|
|
|
|
if err == nil {
|
|
|
|
setter.MaxX = omit.FromCond(int32(maxX), maxX >= 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
maxY, err := strconv.Atoi(req.FormValue("max_y"))
|
|
|
|
if err == nil {
|
|
|
|
setter.MaxY = omit.FromCond(int32(maxY), maxY >= 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
nsfw, err := strconv.Atoi(req.FormValue("nsfw"))
|
|
|
|
if err == nil {
|
|
|
|
if nsfw > 1 {
|
|
|
|
nsfw = 1
|
|
|
|
} else if nsfw < 0 {
|
|
|
|
nsfw = 0
|
|
|
|
}
|
|
|
|
setter.NSFW = omit.From(int32(nsfw))
|
|
|
|
}
|
|
|
|
|
|
|
|
return setter
|
|
|
|
}
|