package api import ( "encoding/json" "net/http" "github.com/google/uuid" "github.com/thepeterstone/claudomator/internal/storage" ) // pushSubscriptionStore is the minimal interface needed by push handlers. type pushSubscriptionStore interface { SavePushSubscription(sub storage.PushSubscription) error DeletePushSubscription(endpoint string) error ListPushSubscriptions() ([]storage.PushSubscription, error) } // SetVAPIDConfig configures VAPID keys and email for web push notifications. func (s *Server) SetVAPIDConfig(pub, priv, email string) { s.vapidPublicKey = pub s.vapidPrivateKey = priv s.vapidEmail = email } // SetPushStore configures the push subscription store. func (s *Server) SetPushStore(store pushSubscriptionStore) { s.pushStore = store } // SetDropsDir configures the file drop directory. func (s *Server) SetDropsDir(dir string) { s.dropsDir = dir } // handleGetVAPIDKey returns the VAPID public key for client-side push subscription. func (s *Server) handleGetVAPIDKey(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"public_key": s.vapidPublicKey}) } // handlePushSubscribe saves a new push subscription. func (s *Server) handlePushSubscribe(w http.ResponseWriter, r *http.Request) { var input struct { Endpoint string `json:"endpoint"` Keys struct { P256DH string `json:"p256dh"` Auth string `json:"auth"` } `json:"keys"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()}) return } if input.Endpoint == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "endpoint is required"}) return } if input.Keys.P256DH == "" || input.Keys.Auth == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "keys.p256dh and keys.auth are required"}) return } sub := storage.PushSubscription{ ID: uuid.New().String(), Endpoint: input.Endpoint, P256DHKey: input.Keys.P256DH, AuthKey: input.Keys.Auth, } store := s.pushStore if store == nil { store = s.store } if err := store.SavePushSubscription(sub); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusCreated, map[string]string{"id": sub.ID}) } // handlePushUnsubscribe deletes a push subscription. func (s *Server) handlePushUnsubscribe(w http.ResponseWriter, r *http.Request) { var input struct { Endpoint string `json:"endpoint"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()}) return } if input.Endpoint == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "endpoint is required"}) return } store := s.pushStore if store == nil { store = s.store } if err := store.DeletePushSubscription(input.Endpoint); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } w.WriteHeader(http.StatusNoContent) }