package server import ( "net/http" "strconv" "github.com/go-chi/chi/v5" "git.unkin.net/unkin/tomswallapi/internal/model" ) // mountNAT wires the NAT-tier resources (snat/masquerade, netmap, 1:1 nat). func (s *Server) mountNAT(r chi.Router) { r.Route("/snat", func(r chi.Router) { r.Get("/", s.listSNAT) r.Post("/", s.createSNAT) r.Get("/{id}", s.getSNAT) r.Delete("/{id}", s.deleteSNAT) }) r.Route("/netmap", func(r chi.Router) { r.Get("/", s.listNetmap) r.Post("/", s.createNetmap) r.Get("/{id}", s.getNetmap) r.Delete("/{id}", s.deleteNetmap) }) r.Route("/nat", func(r chi.Router) { r.Get("/", s.listNAT) r.Post("/", s.createNAT) r.Get("/{id}", s.getNAT) r.Delete("/{id}", s.deleteNAT) }) } func idParam(w http.ResponseWriter, r *http.Request) (int64, bool) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "id must be an integer") return 0, false } return id, true } // ---- SNAT ------------------------------------------------------------------ func (s *Server) listSNAT(w http.ResponseWriter, r *http.Request) { list, err := s.store.ListSNAT(r.Context()) respondList(w, list, err) } func (s *Server) createSNAT(w http.ResponseWriter, r *http.Request) { var v model.SNATRule if !decode(w, r, &v) { return } if v.Action != "masquerade" && v.Action != "snat" { writeError(w, http.StatusBadRequest, "action must be masquerade or snat") return } id, err := s.store.CreateSNAT(r.Context(), v) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } v.ID = id writeJSON(w, http.StatusCreated, v) } func (s *Server) getSNAT(w http.ResponseWriter, r *http.Request) { id, ok := idParam(w, r) if !ok { return } v, err := s.store.GetSNAT(r.Context(), id) respondOne(w, v, err) } func (s *Server) deleteSNAT(w http.ResponseWriter, r *http.Request) { id, ok := idParam(w, r) if !ok { return } respondDelete(w, s.store.DeleteSNAT(r.Context(), id)) } // ---- Netmap ---------------------------------------------------------------- func (s *Server) listNetmap(w http.ResponseWriter, r *http.Request) { list, err := s.store.ListNetmap(r.Context()) respondList(w, list, err) } func (s *Server) createNetmap(w http.ResponseWriter, r *http.Request) { var v model.NetmapRule if !decode(w, r, &v) { return } if v.Type != "dnat" && v.Type != "snat" { writeError(w, http.StatusBadRequest, "type must be dnat or snat") return } id, err := s.store.CreateNetmap(r.Context(), v) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } v.ID = id writeJSON(w, http.StatusCreated, v) } func (s *Server) getNetmap(w http.ResponseWriter, r *http.Request) { id, ok := idParam(w, r) if !ok { return } v, err := s.store.GetNetmap(r.Context(), id) respondOne(w, v, err) } func (s *Server) deleteNetmap(w http.ResponseWriter, r *http.Request) { id, ok := idParam(w, r) if !ok { return } respondDelete(w, s.store.DeleteNetmap(r.Context(), id)) } // ---- 1:1 NAT --------------------------------------------------------------- func (s *Server) listNAT(w http.ResponseWriter, r *http.Request) { list, err := s.store.ListNAT(r.Context()) respondList(w, list, err) } func (s *Server) createNAT(w http.ResponseWriter, r *http.Request) { var v model.NATRule if !decode(w, r, &v) { return } if v.Device == "" || v.External == "" || v.Internal == "" { writeError(w, http.StatusBadRequest, "device, external, and internal are required") return } id, err := s.store.CreateNAT(r.Context(), v) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } v.ID = id writeJSON(w, http.StatusCreated, v) } func (s *Server) getNAT(w http.ResponseWriter, r *http.Request) { id, ok := idParam(w, r) if !ok { return } v, err := s.store.GetNAT(r.Context(), id) respondOne(w, v, err) } func (s *Server) deleteNAT(w http.ResponseWriter, r *http.Request) { id, ok := idParam(w, r) if !ok { return } respondDelete(w, s.store.DeleteNAT(r.Context(), id)) }