feat: add storage info to settings page and improve NFS export handling
Backend: - Add source/used_by/available/error fields to diskUsage in system status - collectDiskUsage() now reads /proc/mounts, samba shares and NFS exports from DB - Paths are deduplicated; shared paths list all shares/exports using them - syscall.Statfs errors surface as available=false with user-facing error - collectServiceStatus made a method of Server (receiver consistency) Frontend: - Settings page now shows two cards: mount points and shared resources - Each path shows source badge (Sistema/Mount/SMB/NFS), used_by chips, progress bar - Unavailable paths show amber warning instead of progress bar - DiskUsage interface updated with new fields - NFSExport interface updated with structured fields (fsid, async, etc) - NFS page updated to use new export fields
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
@@ -17,7 +16,13 @@ import (
|
||||
type nfsExportRequest struct {
|
||||
Path string `json:"path"`
|
||||
Clients []string `json:"clients"`
|
||||
Options string `json:"options"`
|
||||
// Main options
|
||||
ReadOnly bool `json:"read_only"`
|
||||
Async bool `json:"async"`
|
||||
RootSquash bool `json:"root_squash"`
|
||||
SubtreeCheck bool `json:"subtree_check"`
|
||||
// Advanced options stored as JSON string
|
||||
Advanced string `json:"advanced"`
|
||||
}
|
||||
|
||||
func (req nfsExportRequest) validate(allowedRoots []string) error {
|
||||
@@ -29,21 +34,18 @@ func (req nfsExportRequest) validate(allowedRoots []string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(req.Options) == "" {
|
||||
return nil
|
||||
}
|
||||
return system.ValidateNFSOptions(req.Options)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req nfsExportRequest) toModel() db.NFSExport {
|
||||
options := strings.TrimSpace(req.Options)
|
||||
if options == "" {
|
||||
options = "rw,sync,no_root_squash"
|
||||
}
|
||||
return db.NFSExport{
|
||||
Path: req.Path,
|
||||
Clients: req.Clients,
|
||||
Options: options,
|
||||
Path: req.Path,
|
||||
Clients: req.Clients,
|
||||
ReadOnly: req.ReadOnly,
|
||||
Async: req.Async,
|
||||
RootSquash: req.RootSquash,
|
||||
SubtreeCheck: req.SubtreeCheck,
|
||||
Advanced: req.Advanced,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +72,22 @@ func (s *Server) handleGetNFSExport(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, export)
|
||||
}
|
||||
|
||||
func generateFSID(ctx context.Context, dbConn *db.DB) (int64, error) {
|
||||
used, err := dbConn.GetAllFSIDs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
usedSet := make(map[uint32]struct{}, len(used))
|
||||
for _, id := range used {
|
||||
usedSet[uint32(id)] = struct{}{}
|
||||
}
|
||||
n, err := nfs.UniqueFSID(usedSet)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(n), nil
|
||||
}
|
||||
|
||||
func (s *Server) handleCreateNFSExport(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := decodeNFSExportRequest(r.Body)
|
||||
if err != nil {
|
||||
@@ -81,7 +99,15 @@ func (s *Server) handleCreateNFSExport(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
export, err := s.DB.CreateNFSExport(req.toModel())
|
||||
model := req.toModel()
|
||||
fsid, err := generateFSID(r.Context(), s.DB)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
model.FSID = fsid
|
||||
|
||||
export, err := s.DB.CreateNFSExport(model)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
@@ -99,6 +125,12 @@ func (s *Server) handleUpdateNFSExport(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
existing, err := s.DB.GetNFSExport(id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
req, err := decodeNFSExportRequest(r.Body)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
@@ -109,7 +141,11 @@ func (s *Server) handleUpdateNFSExport(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
export, err := s.DB.UpdateNFSExport(id, req.toModel())
|
||||
model := req.toModel()
|
||||
model.ID = id
|
||||
model.FSID = existing.FSID
|
||||
|
||||
export, err := s.DB.UpdateNFSExport(id, model)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user