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:
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE nfs_exports
|
||||
ADD COLUMN read_only INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN async_ INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN root_squash INTEGER NOT NULL DEFAULT 1,
|
||||
ADD COLUMN subtree_check INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN fsid INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN advanced TEXT NOT NULL DEFAULT '{}';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_nfs_exports_fsid ON nfs_exports(fsid);
|
||||
+19
-6
@@ -38,12 +38,25 @@ type SambaShare struct {
|
||||
}
|
||||
|
||||
type NFSExport struct {
|
||||
ID int64 `json:"id"`
|
||||
Path string `json:"path"`
|
||||
Clients []string `json:"clients"`
|
||||
Options string `json:"options"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID int64 `json:"id"`
|
||||
Path string `json:"path"`
|
||||
Clients []string `json:"clients"`
|
||||
ReadOnly bool `json:"read_only"`
|
||||
Async bool `json:"async"`
|
||||
RootSquash bool `json:"root_squash"`
|
||||
SubtreeCheck bool `json:"subtree_check"`
|
||||
FSID int64 `json:"fsid"`
|
||||
Advanced string `json:"advanced"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type NFSAdvanced struct {
|
||||
AllSquash bool `json:"all_squash"`
|
||||
Secure bool `json:"secure"`
|
||||
WDelay bool `json:"wdelay"`
|
||||
Hide bool `json:"hide"`
|
||||
Crossmnt bool `json:"crossmnt"`
|
||||
}
|
||||
|
||||
type DirtyModule struct {
|
||||
|
||||
@@ -61,10 +61,30 @@ func (d *DB) ReplaceNFSExports(exports []NFSExport) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
readOnly := 0
|
||||
if exp.ReadOnly {
|
||||
readOnly = 1
|
||||
}
|
||||
async_ := 0
|
||||
if exp.Async {
|
||||
async_ = 1
|
||||
}
|
||||
rootSquash := 0
|
||||
if exp.RootSquash {
|
||||
rootSquash = 1
|
||||
}
|
||||
subtreeCheck := 0
|
||||
if exp.SubtreeCheck {
|
||||
subtreeCheck = 1
|
||||
}
|
||||
advanced := exp.Advanced
|
||||
if advanced == "" {
|
||||
advanced = "{}"
|
||||
}
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO nfs_exports (path, clients, options)
|
||||
VALUES (?, ?, ?)`,
|
||||
exp.Path, clients, exp.Options,
|
||||
INSERT INTO nfs_exports (path, clients, read_only, async_, root_squash, subtree_check, fsid, advanced)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
exp.Path, clients, readOnly, async_, rootSquash, subtreeCheck, exp.FSID, advanced,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("insert nfs export %s: %w", exp.Path, err)
|
||||
|
||||
@@ -10,11 +10,17 @@ func scanNFSExport(row interface {
|
||||
}) (NFSExport, error) {
|
||||
var export NFSExport
|
||||
var clients, createdAt, updatedAt string
|
||||
var readOnly, async_, rootSquash, subtreeCheck, fsid int
|
||||
if err := row.Scan(
|
||||
&export.ID,
|
||||
&export.Path,
|
||||
&clients,
|
||||
&export.Options,
|
||||
&readOnly,
|
||||
&async_,
|
||||
&rootSquash,
|
||||
&subtreeCheck,
|
||||
&fsid,
|
||||
&export.Advanced,
|
||||
&createdAt,
|
||||
&updatedAt,
|
||||
); err != nil {
|
||||
@@ -25,6 +31,11 @@ func scanNFSExport(row interface {
|
||||
if err != nil {
|
||||
return NFSExport{}, err
|
||||
}
|
||||
export.ReadOnly = readOnly != 0
|
||||
export.Async = async_ != 0
|
||||
export.RootSquash = rootSquash != 0
|
||||
export.SubtreeCheck = subtreeCheck != 0
|
||||
export.FSID = int64(fsid)
|
||||
export.CreatedAt = parseTime(createdAt)
|
||||
export.UpdatedAt = parseTime(updatedAt)
|
||||
return export, nil
|
||||
@@ -32,7 +43,7 @@ func scanNFSExport(row interface {
|
||||
|
||||
func (d *DB) ListNFSExports() ([]NFSExport, error) {
|
||||
rows, err := d.conn.Query(`
|
||||
SELECT id, path, clients, options, created_at, updated_at
|
||||
SELECT id, path, clients, read_only, async_, root_squash, subtree_check, fsid, advanced, created_at, updated_at
|
||||
FROM nfs_exports ORDER BY path ASC`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list nfs exports: %w", err)
|
||||
@@ -52,7 +63,7 @@ func (d *DB) ListNFSExports() ([]NFSExport, error) {
|
||||
|
||||
func (d *DB) GetNFSExport(id int64) (NFSExport, error) {
|
||||
row := d.conn.QueryRow(`
|
||||
SELECT id, path, clients, options, created_at, updated_at
|
||||
SELECT id, path, clients, read_only, async_, root_squash, subtree_check, fsid, advanced, created_at, updated_at
|
||||
FROM nfs_exports WHERE id = ?`, id)
|
||||
export, err := scanNFSExport(row)
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -69,10 +80,30 @@ func (d *DB) CreateNFSExport(export NFSExport) (NFSExport, error) {
|
||||
if err != nil {
|
||||
return NFSExport{}, err
|
||||
}
|
||||
readOnly := 0
|
||||
if export.ReadOnly {
|
||||
readOnly = 1
|
||||
}
|
||||
async_ := 0
|
||||
if export.Async {
|
||||
async_ = 1
|
||||
}
|
||||
rootSquash := 0
|
||||
if export.RootSquash {
|
||||
rootSquash = 1
|
||||
}
|
||||
subtreeCheck := 0
|
||||
if export.SubtreeCheck {
|
||||
subtreeCheck = 1
|
||||
}
|
||||
advanced := export.Advanced
|
||||
if advanced == "" {
|
||||
advanced = "{}"
|
||||
}
|
||||
result, err := d.conn.Exec(`
|
||||
INSERT INTO nfs_exports (path, clients, options)
|
||||
VALUES (?, ?, ?)`,
|
||||
export.Path, clients, export.Options,
|
||||
INSERT INTO nfs_exports (path, clients, read_only, async_, root_squash, subtree_check, fsid, advanced)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
export.Path, clients, readOnly, async_, rootSquash, subtreeCheck, export.FSID, advanced,
|
||||
)
|
||||
if err != nil {
|
||||
return NFSExport{}, fmt.Errorf("create nfs export: %w", err)
|
||||
@@ -89,11 +120,31 @@ func (d *DB) UpdateNFSExport(id int64, export NFSExport) (NFSExport, error) {
|
||||
if err != nil {
|
||||
return NFSExport{}, err
|
||||
}
|
||||
readOnly := 0
|
||||
if export.ReadOnly {
|
||||
readOnly = 1
|
||||
}
|
||||
async_ := 0
|
||||
if export.Async {
|
||||
async_ = 1
|
||||
}
|
||||
rootSquash := 0
|
||||
if export.RootSquash {
|
||||
rootSquash = 1
|
||||
}
|
||||
subtreeCheck := 0
|
||||
if export.SubtreeCheck {
|
||||
subtreeCheck = 1
|
||||
}
|
||||
advanced := export.Advanced
|
||||
if advanced == "" {
|
||||
advanced = "{}"
|
||||
}
|
||||
result, err := d.conn.Exec(`
|
||||
UPDATE nfs_exports
|
||||
SET path = ?, clients = ?, options = ?, updated_at = datetime('now')
|
||||
SET path = ?, clients = ?, read_only = ?, async_ = ?, root_squash = ?, subtree_check = ?, fsid = ?, advanced = ?, updated_at = datetime('now')
|
||||
WHERE id = ?`,
|
||||
export.Path, clients, export.Options, id,
|
||||
export.Path, clients, readOnly, async_, rootSquash, subtreeCheck, export.FSID, advanced, id,
|
||||
)
|
||||
if err != nil {
|
||||
return NFSExport{}, fmt.Errorf("update nfs export: %w", err)
|
||||
@@ -122,3 +173,29 @@ func (d *DB) DeleteNFSExport(id int64) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DB) GetAllFSIDs() ([]int64, error) {
|
||||
rows, err := d.conn.Query(`SELECT fsid FROM nfs_exports WHERE fsid != 0`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get all fsids: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var fsids []int64
|
||||
for rows.Next() {
|
||||
var fsid int64
|
||||
if err := rows.Scan(&fsid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fsids = append(fsids, fsid)
|
||||
}
|
||||
return fsids, rows.Err()
|
||||
}
|
||||
|
||||
func (d *DB) FSIDExists(fsid int64) (bool, error) {
|
||||
var count int
|
||||
err := d.conn.QueryRow(`SELECT COUNT(1) FROM nfs_exports WHERE fsid = ?`, fsid).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user