// Package server provides the HTTP endpoint that receives clipboard payloads // from remote clip-sync peers. package server import ( "encoding/json" "log" "net/http" "git.dracodev.net/Projets/clip-sync/internal/clipboard" "git.dracodev.net/Projets/clip-sync/internal/dedup" ) // Server receives clips from peers and writes them to the local clipboard. type Server struct { httpServer *http.Server clipboard clipboard.Clipboard filter *dedup.Filter } // New creates a Server that listens on addr. func New(addr string, cb clipboard.Clipboard, filter *dedup.Filter) *Server { s := &Server{ clipboard: cb, filter: filter, } mux := http.NewServeMux() mux.HandleFunc("/clip", s.handleClip) s.httpServer = &http.Server{ Addr: addr, Handler: mux, } return s } // ListenAndServe starts the HTTP server. It blocks until the server is stopped. func (s *Server) ListenAndServe() error { return s.httpServer.ListenAndServe() } // Close gracefully shuts down the HTTP server. func (s *Server) Close() error { return s.httpServer.Close() } func (s *Server) handleClip(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } var p dedup.Payload if err := json.NewDecoder(r.Body).Decode(&p); err != nil { http.Error(w, "bad request: invalid JSON", http.StatusBadRequest) return } if p.Text == "" { http.Error(w, "bad request: empty text", http.StatusBadRequest) return } if s.filter.ShouldIgnore(p) { w.WriteHeader(http.StatusNoContent) return } if err := s.clipboard.Write(p.Text); err != nil { log.Printf("server: clipboard write: %v", err) http.Error(w, "internal server error", http.StatusInternalServerError) return } s.filter.MarkWritten(p.Text) w.WriteHeader(http.StatusNoContent) }