package client import ( "encoding/json" "errors" "fmt" "io" "net/http" "git.faercol.me/faercol/http-boot-server/bootserver/config" "git.faercol.me/faercol/http-boot-server/bootserver/helpers" "git.faercol.me/faercol/http-boot-server/bootserver/homeassistant" "git.faercol.me/faercol/http-boot-server/bootserver/services" "github.com/google/uuid" "github.com/sirupsen/logrus" ) const SetBootRoute = "/config/boot" type setBootOptionPayload struct { ClientID string `json:"client_id"` OptionID string `json:"option_id"` } type BootController struct { clientService *services.ClientHandlerService appConf *config.AppConfig l *logrus.Logger } func NewBootController(logger *logrus.Logger, service *services.ClientHandlerService, conf *config.AppConfig) *BootController { return &BootController{ clientService: service, l: logger, appConf: conf, } } func (bc *BootController) setBootOption(w http.ResponseWriter, r *http.Request) (int, []byte, error) { dat, err := io.ReadAll(r.Body) if err != nil { return http.StatusInternalServerError, nil, fmt.Errorf("failed to read body: %w", err) } var payload setBootOptionPayload if err := json.Unmarshal(dat, &payload); err != nil { return http.StatusBadRequest, nil, fmt.Errorf("failed to parse body: %w", err) } clientID, err := uuid.Parse(payload.ClientID) if err != nil { return http.StatusBadRequest, []byte("bad client ID"), fmt.Errorf("invalid format for client ID: %w", err) } optionID, err := uuid.Parse(payload.OptionID) if err != nil { return http.StatusBadRequest, []byte("bad option ID"), fmt.Errorf("invalid format for option ID: %w", err) } if err := bc.clientService.SetClientBootOption(clientID, optionID.String()); err != nil { if errors.Is(err, services.ErrUnknownClient) { return http.StatusNotFound, []byte("unknown client"), err } if errors.Is(err, services.ErrUnknownBootOption) { return http.StatusNotFound, []byte("unknown boot option"), err } return http.StatusInternalServerError, nil, fmt.Errorf("failed to set boot option for client: %w", err) } if bc.appConf.HomeAssistantEnabled { bc.l.Debug("Notifying HomeAssistant of change") newConf, err := bc.clientService.GetClientConfig(clientID) if err != nil { bc.l.Errorf("Failed to get new config to send to HA: %s", err.Error()) } else { if err := homeassistant.New(bc.appConf).SendBootOption(r.Context(), newConf.Name, newConf.Options[newConf.SelectedOption].Name); err != nil { bc.l.Errorf("Failed to notify HA: %s", err.Error()) } } } return http.StatusAccepted, nil, nil } func (bc *BootController) deleteClient(w http.ResponseWriter, r *http.Request) (int, []byte, error) { dat, err := io.ReadAll(r.Body) if err != nil { return http.StatusInternalServerError, nil, fmt.Errorf("failed to read body: %w", err) } var payload setBootOptionPayload if err := json.Unmarshal(dat, &payload); err != nil { return http.StatusBadRequest, nil, fmt.Errorf("failed to parse body: %w", err) } clientID, err := uuid.Parse(payload.ClientID) if err != nil { return http.StatusBadRequest, []byte("bad client ID"), fmt.Errorf("invalid format for client ID: %w", err) } if err := bc.clientService.DeleteClient(clientID); err != nil { return http.StatusInternalServerError, []byte("failed to delete client"), fmt.Errorf("failed to delete client: %w", err) } return http.StatusOK, nil, nil } func (bc *BootController) ServeHTTP(w http.ResponseWriter, r *http.Request) { var returncode int var content []byte var err error switch r.Method { case http.MethodPut: returncode, content, err = bc.setBootOption(w, r) if err != nil { bc.l.Errorf("Error setting boot option for client: %s", err.Error()) } case http.MethodDelete: returncode, content, err = bc.deleteClient(w, r) if err != nil { bc.l.Errorf("Error setting boot option for client: %s", err.Error()) } default: helpers.HandleResponse(w, r, http.StatusMethodNotAllowed, nil, bc.l) return } helpers.HandleResponse(w, r, returncode, content, bc.l) }