package handlers import ( "net/http" "strconv" "strings" "lostcavewireplanner/internal/models" ) type RackViewData struct { Rack models.Rack FrontSlots []RackSlot BackSlots []RackSlot RackedDevices []models.Device UnrackedDevices []models.Device Connections []models.Connection ConnectionTypes []models.ConnectionType AllDevices []models.Device WallSocketModels []models.DeviceModel Error string } type RackSlot struct { Unit int Device *models.Device Height int IsStart bool } func (h *Handlers) RackView(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { http.NotFound(w, r) return } rack, err := h.Store.RackGetByID(id) if err != nil || rack == nil { http.NotFound(w, r) return } rackedDevices, _ := h.Store.DeviceGetByRackID(id) unrackedDevices, _ := h.Store.DeviceGetUnrackedByRackID(id) connections, _ := h.Store.ConnectionGetAllForRack(id) connTypes, _ := h.Store.ConnectionTypeGetAll() allDevices, _ := h.Store.DeviceGetAllUnracked() if rackedDevices == nil { rackedDevices = []models.Device{} } if unrackedDevices == nil { unrackedDevices = []models.Device{} } if connections == nil { connections = []models.Connection{} } if connTypes == nil { connTypes = []models.ConnectionType{} } if allDevices == nil { allDevices = []models.Device{} } frontSlots, backSlots := buildRackSlots(rack.HeightUnits, rackedDevices) h.render(w, "rack.html", RackViewData{ Rack: *rack, FrontSlots: frontSlots, BackSlots: backSlots, RackedDevices: rackedDevices, UnrackedDevices: unrackedDevices, Connections: connections, ConnectionTypes: connTypes, AllDevices: allDevices, }) } func buildRackSlots(heightUnits int, devices []models.Device) (front, back []RackSlot) { front = make([]RackSlot, heightUnits) back = make([]RackSlot, heightUnits) for i := range front { front[i].Unit = i + 1 back[i].Unit = i + 1 } for _, d := range devices { if d.RackUnitStart == nil || d.RackSide == nil { continue } height := 1 if d.Model != nil && d.Model.HeightUnits != nil { height = *d.Model.HeightUnits } start := *d.RackUnitStart - 1 if start < 0 || start >= heightUnits { continue } end := start + height if end > heightUnits { end = heightUnits } slots := &front if *d.RackSide == "back" { slots = &back } for u := start; u < end; u++ { (*slots)[u].Device = &d (*slots)[u].Height = height (*slots)[u].IsStart = (u == start) } } return } func (h *Handlers) RackDelete(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, _ := strconv.ParseInt(idStr, 10, 64) h.Store.RackDelete(id) h.redirect(w, r, "/") } func (h *Handlers) RackEdit(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, _ := strconv.ParseInt(idStr, 10, 64) r.ParseForm() rack, err := h.Store.RackGetByID(id) if err != nil || rack == nil { http.NotFound(w, r) return } rack.Name = r.FormValue("name") rack.RackType = r.FormValue("rack_type") rack.Depth = r.FormValue("depth") if hu := r.FormValue("height_units"); hu != "" { if n, err := strconv.Atoi(hu); err == nil { rack.HeightUnits = n } } rack.Comment = r.FormValue("comment") if err := h.Store.RackUpdate(rack); err != nil { h.renderRackError(w, *rack, "Failed to update: "+err.Error()) return } h.redirect(w, r, "/racks/"+idStr) } func (h *Handlers) RackAddRackedDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") rackID, _ := strconv.ParseInt(idStr, 10, 64) r.ParseForm() deviceIDStr := r.FormValue("device_id") deviceID, _ := strconv.ParseInt(deviceIDStr, 10, 64) unitStartStr := r.FormValue("rack_unit_start") unitStart, _ := strconv.Atoi(unitStartStr) rackSide := r.FormValue("rack_side") device, err := h.Store.DeviceGetByID(deviceID) if err != nil || device == nil { h.redirect(w, r, "/racks/"+idStr) return } device.RackID = &rackID device.RackUnitStart = &unitStart device.RackSide = &rackSide if err := h.Store.DeviceUpdate(device); err != nil { h.redirect(w, r, "/racks/"+idStr) return } h.redirect(w, r, "/racks/"+idStr) } func (h *Handlers) RackAddUnrackedDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") rackID, _ := strconv.ParseInt(idStr, 10, 64) r.ParseForm() deviceIDStr := r.FormValue("device_id") deviceID, _ := strconv.ParseInt(deviceIDStr, 10, 64) device, err := h.Store.DeviceGetByID(deviceID) if err != nil || device == nil { h.redirect(w, r, "/racks/"+idStr) return } device.RackID = &rackID device.RackUnitStart = nil device.RackSide = nil if err := h.Store.DeviceUpdate(device); err != nil { h.redirect(w, r, "/racks/"+idStr) return } h.redirect(w, r, "/racks/"+idStr) } func (h *Handlers) RackRemoveDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") devIDStr := r.PathValue("devId") rackID, _ := strconv.ParseInt(idStr, 10, 64) devID, _ := strconv.ParseInt(devIDStr, 10, 64) device, err := h.Store.DeviceGetByID(devID) if err != nil || device == nil { h.redirect(w, r, "/racks/"+strconv.FormatInt(rackID, 10)) return } device.RackID = nil device.RackUnitStart = nil device.RackSide = nil h.Store.DeviceUpdate(device) h.redirect(w, r, "/racks/"+idStr) } func (h *Handlers) renderRackError(w http.ResponseWriter, rack models.Rack, errMsg string) { rackedDevices, _ := h.Store.DeviceGetByRackID(rack.ID) unrackedDevices, _ := h.Store.DeviceGetUnrackedByRackID(rack.ID) connections, _ := h.Store.ConnectionGetAllForRack(rack.ID) connTypes, _ := h.Store.ConnectionTypeGetAll() allDevices, _ := h.Store.DeviceGetAllUnracked() if rackedDevices == nil { rackedDevices = []models.Device{} } if unrackedDevices == nil { unrackedDevices = []models.Device{} } if connections == nil { connections = []models.Connection{} } if connTypes == nil { connTypes = []models.ConnectionType{} } frontSlots, backSlots := buildRackSlots(rack.HeightUnits, rackedDevices) h.render(w, "rack.html", RackViewData{ Rack: rack, FrontSlots: frontSlots, BackSlots: backSlots, RackedDevices: rackedDevices, UnrackedDevices: unrackedDevices, Connections: connections, ConnectionTypes: connTypes, AllDevices: allDevices, Error: errMsg, }) } func formatPortName(name string) string { parts := strings.Split(name, "-") if len(parts) > 0 { return parts[len(parts)-1] } return name }