package api import ( "encoding/json" "log" "net/http" "strconv" "git.rouggy.com/rouggy/ShackMaster/internal/config" "github.com/gorilla/websocket" ) type Server struct { deviceManager *DeviceManager hub *Hub config *config.Config upgrader websocket.Upgrader } func NewServer(dm *DeviceManager, hub *Hub, cfg *config.Config) *Server { return &Server{ deviceManager: dm, hub: hub, config: cfg, upgrader: websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true // Allow all origins for now }, }, } } func (s *Server) SetupRoutes() *http.ServeMux { mux := http.NewServeMux() // WebSocket endpoint mux.HandleFunc("/ws", s.handleWebSocket) // REST API endpoints mux.HandleFunc("/api/status", s.handleGetStatus) mux.HandleFunc("/api/config", s.handleGetConfig) // WebSwitch endpoints mux.HandleFunc("/api/webswitch/relay/on", s.handleWebSwitchRelayOn) mux.HandleFunc("/api/webswitch/relay/off", s.handleWebSwitchRelayOff) mux.HandleFunc("/api/webswitch/all/on", s.handleWebSwitchAllOn) mux.HandleFunc("/api/webswitch/all/off", s.handleWebSwitchAllOff) // Rotator endpoints <<<<<<< HEAD mux.HandleFunc("/api/rotator/heading", s.handleRotatorHeading) ======= mux.HandleFunc("/api/rotator/move", s.handleRotatorMove) >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 mux.HandleFunc("/api/rotator/cw", s.handleRotatorCW) mux.HandleFunc("/api/rotator/ccw", s.handleRotatorCCW) mux.HandleFunc("/api/rotator/stop", s.handleRotatorStop) // Tuner endpoints mux.HandleFunc("/api/tuner/operate", s.handleTunerOperate) <<<<<<< HEAD mux.HandleFunc("/api/tuner/bypass", s.handleTunerBypass) mux.HandleFunc("/api/tuner/autotune", s.handleTunerAutoTune) // Antenna Genius endpoints mux.HandleFunc("/api/antenna/select", s.handleAntennaSelect) mux.HandleFunc("/api/antenna/reboot", s.handleAntennaReboot) // Power Genius endpoints mux.HandleFunc("/api/power/fanmode", s.handlePowerFanMode) mux.HandleFunc("/api/power/operate", s.handlePowerOperate) ======= mux.HandleFunc("/api/tuner/tune", s.handleTunerAutoTune) mux.HandleFunc("/api/tuner/antenna", s.handleTunerAntenna) // Antenna Genius endpoints mux.HandleFunc("/api/antenna/set", s.handleAntennaSet) // Power Genius endpoints mux.HandleFunc("/api/power/fanmode", s.handlePowerFanMode) >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 // Static files (will be frontend) mux.Handle("/", http.FileServer(http.Dir("./web/dist"))) return mux } func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) { conn, err := s.upgrader.Upgrade(w, r, nil) if err != nil { log.Printf("WebSocket upgrade error: %v", err) return } ServeWs(s.hub, conn) } func (s *Server) handleGetStatus(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } status := s.deviceManager.GetStatus() s.sendJSON(w, status) } func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Only send public config info (not API keys) configInfo := map[string]interface{}{ "callsign": s.config.Location.Callsign, "location": map[string]float64{ "latitude": s.config.Location.Latitude, "longitude": s.config.Location.Longitude, }, } s.sendJSON(w, configInfo) } // WebSwitch handlers func (s *Server) handleWebSwitchRelayOn(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } relay, err := strconv.Atoi(r.URL.Query().Get("relay")) if err != nil || relay < 1 || relay > 5 { http.Error(w, "Invalid relay number", http.StatusBadRequest) return } if err := s.deviceManager.WebSwitch().TurnOn(relay); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleWebSwitchRelayOff(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } relay, err := strconv.Atoi(r.URL.Query().Get("relay")) if err != nil || relay < 1 || relay > 5 { http.Error(w, "Invalid relay number", http.StatusBadRequest) return } if err := s.deviceManager.WebSwitch().TurnOff(relay); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleWebSwitchAllOn(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } if err := s.deviceManager.WebSwitch().AllOn(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleWebSwitchAllOff(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } if err := s.deviceManager.WebSwitch().AllOff(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } // Rotator handlers <<<<<<< HEAD func (s *Server) handleRotatorHeading(w http.ResponseWriter, r *http.Request) { ======= func (s *Server) handleRotatorMove(w http.ResponseWriter, r *http.Request) { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { <<<<<<< HEAD Heading int `json:"heading"` ======= Rotator int `json:"rotator"` Azimuth int `json:"azimuth"` >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } <<<<<<< HEAD if err := s.deviceManager.RotatorGenius().SetHeading(req.Heading); err != nil { ======= if err := s.deviceManager.RotatorGenius().MoveToAzimuth(req.Rotator, req.Azimuth); err != nil { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleRotatorCW(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } <<<<<<< HEAD if err := s.deviceManager.RotatorGenius().RotateCW(); err != nil { ======= rotator, err := strconv.Atoi(r.URL.Query().Get("rotator")) if err != nil || rotator < 1 || rotator > 2 { http.Error(w, "Invalid rotator number", http.StatusBadRequest) return } if err := s.deviceManager.RotatorGenius().RotateCW(rotator); err != nil { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleRotatorCCW(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } <<<<<<< HEAD if err := s.deviceManager.RotatorGenius().RotateCCW(); err != nil { ======= rotator, err := strconv.Atoi(r.URL.Query().Get("rotator")) if err != nil || rotator < 1 || rotator > 2 { http.Error(w, "Invalid rotator number", http.StatusBadRequest) return } if err := s.deviceManager.RotatorGenius().RotateCCW(rotator); err != nil { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleRotatorStop(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } if err := s.deviceManager.RotatorGenius().Stop(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } // Tuner handlers func (s *Server) handleTunerOperate(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { <<<<<<< HEAD Value int `json:"value"` ======= Operate bool `json:"operate"` >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } <<<<<<< HEAD if err := s.deviceManager.TunerGenius().SetOperate(req.Value); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleTunerBypass(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { Value int `json:"value"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if err := s.deviceManager.TunerGenius().SetBypass(req.Value); err != nil { ======= if err := s.deviceManager.TunerGenius().SetOperate(req.Operate); err != nil { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleTunerAutoTune(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } if err := s.deviceManager.TunerGenius().AutoTune(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } <<<<<<< HEAD // Antenna Genius handlers func (s *Server) handleAntennaSelect(w http.ResponseWriter, r *http.Request) { ======= func (s *Server) handleTunerAntenna(w http.ResponseWriter, r *http.Request) { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { <<<<<<< HEAD Port int `json:"port"` ======= >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 Antenna int `json:"antenna"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } <<<<<<< HEAD if err := s.deviceManager.AntennaGenius().SetAntenna(req.Port, req.Antenna); err != nil { ======= if err := s.deviceManager.TunerGenius().ActivateAntenna(req.Antenna); err != nil { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } <<<<<<< HEAD func (s *Server) handleAntennaReboot(w http.ResponseWriter, r *http.Request) { ======= // Antenna Genius handlers func (s *Server) handleAntennaSet(w http.ResponseWriter, r *http.Request) { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } <<<<<<< HEAD if err := s.deviceManager.AntennaGenius().Reboot(); err != nil { ======= var req struct { Radio int `json:"radio"` Antenna int `json:"antenna"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if err := s.deviceManager.AntennaGenius().SetRadioAntenna(req.Radio, req.Antenna); err != nil { >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } // Power Genius handlers func (s *Server) handlePowerFanMode(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { Mode string `json:"mode"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if err := s.deviceManager.PowerGenius().SetFanMode(req.Mode); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } <<<<<<< HEAD func (s *Server) handlePowerOperate(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req struct { Value int `json:"value"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if err := s.deviceManager.PowerGenius().SetOperate(req.Value); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s.sendJSON(w, map[string]string{"status": "ok"}) } ======= >>>>>>> 4ab192418e21065c68d59777493ea03b76c061e7 func (s *Server) sendJSON(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) }