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", } 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" 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, }) } }