package main import ( "encoding/json" "fmt" "net/http" "powerpod/gotool/pb" ) type tapNotifyAPIRequest struct { Write bool `json:"write"` ClientID uint32 `json:"client_id"` AllClients bool `json:"all_clients"` Single bool `json:"single"` DoubleTap bool `json:"double_tap"` Triple bool `json:"triple"` } type tapNotifyAPIResponse struct { ClientID uint32 `json:"client_id"` Success bool `json:"success"` SlavesUpdated uint32 `json:"slaves_updated"` Single bool `json:"single"` DoubleTap bool `json:"double_tap"` Triple bool `json:"triple"` Error string `json:"error,omitempty"` } type tapSnapshotAPIResponse struct { Events []tapEventView `json:"events"` Error string `json:"error,omitempty"` } type tapEventView struct { ClientID uint32 `json:"client_id"` Kind string `json:"kind"` AgeMs uint32 `json:"age_ms"` } type tapReceiveAPIResponse struct { ClientID uint32 `json:"client_id"` Enabled bool `json:"enabled"` Success bool `json:"success"` Error string `json:"error,omitempty"` } func mountTapAPI(mux *http.ServeMux, link *managedSerial, hub *wsHub, tapCtl *tapNotifyCtl) { mux.HandleFunc("GET /api/clients/{clientID}/tap-notify", func(w http.ResponseWriter, r *http.Request) { clientID, err := parsePathClientID(r) if err != nil { writeJSON(w, http.StatusBadRequest, tapNotifyAPIResponse{Error: err.Error()}) return } serveTapNotifyGet(w, clientID, link) }) mux.HandleFunc("PUT /api/clients/{clientID}/tap-notify", func(w http.ResponseWriter, r *http.Request) { clientID, err := parsePathClientID(r) if err != nil { writeJSON(w, http.StatusBadRequest, tapNotifyAPIResponse{Error: err.Error()}) return } serveClientTapNotifyPut(w, r, clientID, link, hub, tapCtl) }) mux.HandleFunc("PUT /api/clients/{clientID}/tap-receive", func(w http.ResponseWriter, r *http.Request) { clientID, err := parsePathClientID(r) if err != nil { writeJSON(w, http.StatusBadRequest, tapReceiveAPIResponse{Error: err.Error()}) return } serveClientTapReceivePut(w, r, clientID, hub) }) mux.HandleFunc("/api/tap-notify", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: serveTapNotifyGetQuery(w, r, link) case http.MethodPost: serveTapNotifyPost(w, r, link, hub, tapCtl) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }) mux.HandleFunc("/api/tap-snapshot", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } serveTapSnapshotGet(w, r, link) }) } func applyTapNotifyClient(link *managedSerial, hub *wsHub, tapCtl *tapNotifyCtl, clientID uint32, single, doubleTap, triple bool) tapNotifyAPIResponse { return applyTapNotifyClientWS(link, hub, tapCtl, clientID, single, doubleTap, triple) } func serveTapNotifyGet(w http.ResponseWriter, clientID uint32, link *managedSerial) { resp, err := link.TapNotifyPoll(&pb.TapNotifyRequest{ Write: false, ClientId: clientID, }) if err != nil { writeJSON(w, http.StatusServiceUnavailable, tapNotifyAPIResponse{ ClientID: clientID, Error: err.Error(), }) return } writeJSON(w, http.StatusOK, tapNotifyAPIResponse{ ClientID: resp.GetClientId(), Success: resp.GetSuccess(), Single: resp.GetSingle(), DoubleTap: resp.GetDoubleTap(), Triple: resp.GetTriple(), }) } func serveTapNotifyGetQuery(w http.ResponseWriter, r *http.Request, link *managedSerial) { clientID, err := parseUintQuery(r, "client_id", 0) if err != nil || clientID == 0 { writeJSON(w, http.StatusBadRequest, tapNotifyAPIResponse{Error: "client_id required"}) return } serveTapNotifyGet(w, clientID, link) } type clientTapNotifyBody struct { Single bool `json:"single"` DoubleTap bool `json:"double_tap"` Triple bool `json:"triple"` } func serveClientTapNotifyPut(w http.ResponseWriter, r *http.Request, clientID uint32, link *managedSerial, hub *wsHub, tapCtl *tapNotifyCtl) { var body clientTapNotifyBody if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, tapNotifyAPIResponse{Error: "invalid JSON"}) return } out := applyTapNotifyClient(link, hub, tapCtl, clientID, body.Single, body.DoubleTap, body.Triple) status := http.StatusOK if out.Error != "" || !out.Success { status = http.StatusServiceUnavailable } writeJSON(w, status, out) } func serveTapNotifyPost(w http.ResponseWriter, r *http.Request, link *managedSerial, hub *wsHub, tapCtl *tapNotifyCtl) { var body tapNotifyAPIRequest if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, tapNotifyAPIResponse{Error: "invalid JSON"}) return } if body.AllClients { updated, err := applyTapNotifyAll(link, hub, tapCtl, body.Single, body.DoubleTap, body.Triple) if err != nil { writeJSON(w, http.StatusServiceUnavailable, tapNotifyAPIResponse{Error: err.Error()}) return } writeJSON(w, http.StatusOK, tapNotifyAPIResponse{ Success: updated > 0, SlavesUpdated: updated, Single: body.Single, DoubleTap: body.DoubleTap, Triple: body.Triple, }) return } if body.ClientID == 0 { writeJSON(w, http.StatusBadRequest, tapNotifyAPIResponse{Error: "client_id required"}) return } out := applyTapNotifyClient(link, hub, tapCtl, body.ClientID, body.Single, body.DoubleTap, body.Triple) status := http.StatusOK if out.Error != "" || !out.Success { status = http.StatusServiceUnavailable } writeJSON(w, status, out) } func serveClientTapReceivePut(w http.ResponseWriter, r *http.Request, clientID uint32, hub *wsHub) { var body struct { Enable bool `json:"enable"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, tapReceiveAPIResponse{Error: "invalid JSON"}) return } if hub != nil { hub.patchClientTapReceive(clientID, body.Enable) } writeJSON(w, http.StatusOK, tapReceiveAPIResponse{ ClientID: clientID, Enabled: body.Enable, Success: true, }) } func applyTapNotifyAll(link *managedSerial, hub *wsHub, tapCtl *tapNotifyCtl, single, doubleTap, triple bool) (uint32, error) { resp, err := link.TapNotify(&pb.TapNotifyRequest{ Write: true, AllClients: true, Single: single, DoubleTap: doubleTap, Triple: triple, }) if err != nil { return 0, err } if !resp.GetSuccess() { return 0, fmt.Errorf("tap notify not applied to any slave") } if hub != nil || tapCtl != nil { clients, _ := link.listClientsPoll() for _, c := range clients { if tapCtl != nil { tapCtl.Set(c.GetId(), single, doubleTap, triple) } if hub != nil { hub.patchClientTapNotify(c.GetId(), single, doubleTap, triple) } } } return resp.GetSlavesUpdated(), nil } func serveTapSnapshotGet(w http.ResponseWriter, r *http.Request, link *managedSerial) { clientID, err := parseUintQuery(r, "client_id", 0) if err != nil { writeJSON(w, http.StatusBadRequest, tapSnapshotAPIResponse{Error: err.Error()}) return } resp, err := link.readTapSnapshotPoll(clientID) if err != nil { writeJSON(w, http.StatusServiceUnavailable, tapSnapshotAPIResponse{Error: err.Error()}) return } out := tapSnapshotAPIResponse{Events: make([]tapEventView, 0, len(resp.GetEvents()))} for _, e := range resp.GetEvents() { if !e.GetValid() { continue } out.Events = append(out.Events, tapEventView{ ClientID: e.GetClientId(), Kind: tapKindLabel(e.GetKind()), AgeMs: e.GetAgeMs(), }) } writeJSON(w, http.StatusOK, out) }