package web import ( "encoding/json" "io" "net/http" "strconv" "github.com/go-chi/chi/v5" "github.com/darroyo/nasctl/internal/db" "github.com/darroyo/nasctl/internal/modules/samba" "github.com/darroyo/nasctl/internal/system" ) func writeJSON(w http.ResponseWriter, status int, payload any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(payload) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) } type sambaShareRequest struct { Name string `json:"name"` Path string `json:"path"` Comment string `json:"comment"` ReadOnly bool `json:"read_only"` GuestOK bool `json:"guest_ok"` ValidUsers []string `json:"valid_users"` ValidGroups []string `json:"valid_groups"` InvalidUsers []string `json:"invalid_users"` } func (req sambaShareRequest) validate(allowedRoots []string) error { if err := system.ValidateShareName(req.Name); err != nil { return err } if err := system.ValidatePathAllowed(req.Path, allowedRoots); err != nil { return err } for _, user := range req.ValidUsers { if err := system.ValidateUsername(user); err != nil { return err } } for _, user := range req.InvalidUsers { if err := system.ValidateUsername(user); err != nil { return err } } return nil } func (req sambaShareRequest) toModel() db.SambaShare { return db.SambaShare{ Name: req.Name, Path: req.Path, Comment: req.Comment, ReadOnly: req.ReadOnly, GuestOK: req.GuestOK, ValidUsers: req.ValidUsers, ValidGroups: req.ValidGroups, InvalidUsers: req.InvalidUsers, } } func (s *Server) handleListSambaShares(w http.ResponseWriter, r *http.Request) { shares, err := s.DB.ListSambaShares() if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, map[string]any{"shares": shares}) } func (s *Server) handleGetSambaShare(w http.ResponseWriter, r *http.Request) { id, err := parseID(chi.URLParam(r, "id")) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } share, err := s.DB.GetSambaShare(id) if err != nil { writeError(w, http.StatusNotFound, err.Error()) return } writeJSON(w, http.StatusOK, share) } func (s *Server) handleCreateSambaShare(w http.ResponseWriter, r *http.Request) { req, err := decodeSambaShareRequest(r.Body) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } if err := req.validate(s.AllowedRoots); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } share, err := s.DB.CreateSambaShare(req.toModel()) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if err := s.DB.MarkDirty(samba.ModuleName); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusCreated, share) } func (s *Server) handleUpdateSambaShare(w http.ResponseWriter, r *http.Request) { id, err := parseID(chi.URLParam(r, "id")) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } req, err := decodeSambaShareRequest(r.Body) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } if err := req.validate(s.AllowedRoots); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } share, err := s.DB.UpdateSambaShare(id, req.toModel()) if err != nil { writeError(w, http.StatusNotFound, err.Error()) return } if err := s.DB.MarkDirty(samba.ModuleName); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, share) } func (s *Server) handleDeleteSambaShare(w http.ResponseWriter, r *http.Request) { id, err := parseID(chi.URLParam(r, "id")) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } if err := s.DB.DeleteSambaShare(id); err != nil { writeError(w, http.StatusNotFound, err.Error()) return } if err := s.DB.MarkDirty(samba.ModuleName); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } w.WriteHeader(http.StatusNoContent) } func decodeSambaShareRequest(body io.ReadCloser) (sambaShareRequest, error) { defer body.Close() var req sambaShareRequest if err := json.NewDecoder(body).Decode(&req); err != nil { return sambaShareRequest{}, err } return req, nil } func parseID(raw string) (int64, error) { id, err := strconv.ParseInt(raw, 10, 64) if err != nil || id <= 0 { return 0, err } return id, nil }