package server import ( "encoding/json" "log/slog" "net/http" "time" "prism/service/notification" "prism/service/util" "github.com/go-chi/chi/v5" ) func (s *Server) handleGetMappings(w http.ResponseWriter, r *http.Request) { mappings, err := s.store.GetAllMappings() if err != nil { s.logger.Error("Failed to get mappings", "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(mappings); err != nil { slog.Error("Failed to encode response", "error", err) } } type createMappingRequest struct { App string `json:"app"` Channel notification.Channel `json:"channel"` PushEndpoint *string `json:"pushEndpoint,omitempty"` } func (s *Server) handleCreateMapping(w http.ResponseWriter, r *http.Request) { var req createMappingRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.App == "" { http.Error(w, "Missing required fields", http.StatusBadRequest) return } if req.Channel == "" { req.Channel = notification.ChannelWebPush } if req.Channel == notification.ChannelWebPush && req.PushEndpoint == nil { http.Error(w, "pushEndpoint required for webpush channel", http.StatusBadRequest) return } var webPush *notification.WebPushSubscription if req.Channel == notification.ChannelWebPush && req.PushEndpoint != nil { webPush = ¬ification.WebPushSubscription{ Endpoint: *req.PushEndpoint, } } if err := s.store.Register(req.App, &req.Channel, nil, webPush); err != nil { s.logger.Error("Failed to register mapping", "error", err) http.Error(w, "Failed to create mapping", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) if err := json.NewEncoder(w).Encode(map[string]string{"status": "created"}); err != nil { s.logger.Error("Failed to encode response", "error", err) } } func (s *Server) handleDeleteMapping(w http.ResponseWriter, r *http.Request) { appName := chi.URLParam(r, "appName") if appName == "" { http.Error(w, "Missing appName parameter", http.StatusBadRequest) return } if err := s.store.RemoveApp(appName); err != nil { s.logger.Error("Failed to delete mapping", "error", err) http.Error(w, "Failed to delete mapping", http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } type updateChannelRequest struct { Channel notification.Channel `json:"channel"` UpEndpoint *string `json:"upEndpoint,omitempty"` } func (s *Server) handleUpdateChannel(w http.ResponseWriter, r *http.Request) { appName := chi.URLParam(r, "appName") if appName == "" { http.Error(w, "Missing appName parameter", http.StatusBadRequest) return } var req updateChannelRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.Channel == notification.ChannelWebPush && req.UpEndpoint == nil { http.Error(w, "upEndpoint required for webpush channel", http.StatusBadRequest) return } if err := s.store.UpdateChannel(appName, req.Channel); err != nil { s.logger.Error("Failed to update channel", "error", err) http.Error(w, "Failed to update channel", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"status": "updated"}); err != nil { s.logger.Error("Failed to encode response", "error", err) } } func (s *Server) handleGetStats(w http.ResponseWriter, r *http.Request) { mappings, err := s.store.GetAllMappings() if err != nil { s.logger.Error("Failed to get mappings", "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } uptime := time.Since(s.startTime) stats := map[string]interface{}{ "uptime": util.FormatUptime(uptime), "uptimeSeconds": int(uptime.Seconds()), "mappingsCount": len(mappings), "signalCount": countByChannel(mappings, notification.ChannelSignal), "webpushCount": countByChannel(mappings, notification.ChannelWebPush), "protonEnabled": s.cfg.IsProtonEnabled(), } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(stats); err != nil { s.logger.Error("Failed to encode response", "error", err) } } func countByChannel(mappings []notification.Mapping, channel notification.Channel) int { count := 0 for _, m := range mappings { if m.Channel == channel { count++ } } return count }