wireplanner/internal/handlers/models.go

213 lines
5.0 KiB
Go

package handlers
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"lostcavewireplanner/internal/models"
)
type ModelListData struct {
Models []models.DeviceModel
Error string
}
type ModelFormData struct {
Model *models.DeviceModel
Error string
IsEdit bool
}
func (h *Handlers) ModelList(w http.ResponseWriter, r *http.Request) {
list, _ := h.Store.ModelGetAll()
if list == nil {
list = []models.DeviceModel{}
}
h.render(w, "model_list.html", ModelListData{Models: list})
}
func (h *Handlers) ModelCreateForm(w http.ResponseWriter, r *http.Request) {
h.render(w, "model_form.html", ModelFormData{IsEdit: false})
}
func (h *Handlers) ModelEditForm(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, _ := strconv.ParseInt(idStr, 10, 64)
model, err := h.Store.ModelGetByID(id)
if err != nil || model == nil {
http.NotFound(w, r)
return
}
h.render(w, "model_form.html", ModelFormData{Model: model, IsEdit: true})
}
func (h *Handlers) ModelCreate(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(10 << 20); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
m := &models.DeviceModel{
Name: r.FormValue("name"),
Manufacturer: r.FormValue("manufacturer"),
IsPatchPanel: r.FormValue("is_patch_panel") == "on",
IsWallSocket: r.FormValue("is_wall_socket") == "on",
IsPowerStrip: r.FormValue("is_power_strip") == "on",
}
isRack := r.FormValue("is_rack_mountable") == "on"
m.IsRackMountable = isRack
if isRack {
if huStr := r.FormValue("height_units"); huStr != "" {
hu, _ := strconv.Atoi(huStr)
m.HeightUnits = &hu
}
}
m.FrontImage = saveUploadedFile(r, "front_image", "uploads")
m.BackImage = saveUploadedFile(r, "back_image", "uploads")
parsePortsFromForm(r, m)
if m.Name == "" {
h.render(w, "model_form.html", ModelFormData{Model: m, IsEdit: false, Error: "Name is required"})
return
}
_, err := h.Store.ModelCreate(m, "uploads")
if err != nil {
h.render(w, "model_form.html", ModelFormData{Model: m, IsEdit: false, Error: err.Error()})
return
}
http.Redirect(w, r, "/models", http.StatusSeeOther)
}
func (h *Handlers) ModelUpdate(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, _ := strconv.ParseInt(idStr, 10, 64)
if err := r.ParseMultipartForm(10 << 20); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
m, err := h.Store.ModelGetByID(id)
if err != nil || m == nil {
http.NotFound(w, r)
return
}
m.Name = r.FormValue("name")
m.Manufacturer = r.FormValue("manufacturer")
m.IsPatchPanel = r.FormValue("is_patch_panel") == "on"
m.IsWallSocket = r.FormValue("is_wall_socket") == "on"
m.IsPowerStrip = r.FormValue("is_power_strip") == "on"
isRack := r.FormValue("is_rack_mountable") == "on"
m.IsRackMountable = isRack
if isRack {
if huStr := r.FormValue("height_units"); huStr != "" {
hu, _ := strconv.Atoi(huStr)
m.HeightUnits = &hu
}
} else {
m.HeightUnits = nil
}
if frontImg := saveUploadedFile(r, "front_image", "uploads"); frontImg != "" {
m.FrontImage = frontImg
}
if backImg := saveUploadedFile(r, "back_image", "uploads"); backImg != "" {
m.BackImage = backImg
}
parsePortsFromForm(r, m)
if m.Name == "" {
h.render(w, "model_form.html", ModelFormData{Model: m, IsEdit: true, Error: "Name is required"})
return
}
if err := h.Store.ModelUpdate(m); err != nil {
h.render(w, "model_form.html", ModelFormData{Model: m, IsEdit: true, Error: err.Error()})
return
}
http.Redirect(w, r, "/models", http.StatusSeeOther)
}
func (h *Handlers) ModelDelete(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, _ := strconv.ParseInt(idStr, 10, 64)
if err := h.Store.ModelDelete(id); err != nil {
h.renderModelListError(w, "Cannot delete model: it is in use by existing devices.")
return
}
http.Redirect(w, r, "/models", http.StatusSeeOther)
}
func (h *Handlers) renderModelListError(w http.ResponseWriter, msg string) {
list, _ := h.Store.ModelGetAll()
if list == nil {
list = []models.DeviceModel{}
}
h.render(w, "model_list.html", ModelListData{Models: list, Error: msg})
}
func saveUploadedFile(r *http.Request, field, dir string) string {
file, header, err := r.FormFile(field)
if err != nil {
return ""
}
defer file.Close()
ext := ""
if idx := strings.LastIndex(header.Filename, "."); idx >= 0 {
ext = header.Filename[idx:]
}
safeName := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
fpath := filepath.Join(dir, safeName)
dst, err := os.Create(fpath)
if err != nil {
return ""
}
defer dst.Close()
if _, err := io.Copy(dst, file); err != nil {
return ""
}
return fpath
}
func parsePortsFromForm(r *http.Request, m *models.DeviceModel) {
r.ParseForm()
portNames := r.Form["port_name"]
portSides := r.Form["port_side"]
m.Ports = nil
for i := range portNames {
if i >= len(portSides) {
break
}
if portNames[i] == "" {
continue
}
m.Ports = append(m.Ports, models.DeviceModelPort{
Name: portNames[i],
Side: portSides[i],
Position: i,
})
}
}