428056e1be
GET /api/storage/disk-usage returns source path stats (total/used/free bytes, %).
Polls every 30s while Storage page is open.
Backend:
- migration 0009: adds mover_warning_threshold (1-99, default 80) to storage_config
- StatPath moved to internal/storage package for testability
- handleStorageDiskUsage returns {source: PathStat}
Frontend:
- DiskUsageBar component in Mergerfs Mover card: usage bar with color
green < threshold, amber >= threshold, red >= 95%
- Warning message when threshold reached or exceeded
- New input in config form: Umbral de aviso (%)
- storageDiskUsage() API method
Tests: StatPath unit tests (empty/nonexistent/valid paths)
Version: 0.7.3
68 lines
1.7 KiB
Go
68 lines
1.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"os/exec"
|
|
"syscall"
|
|
|
|
"github.com/darroyo/nasctl/internal/db"
|
|
"github.com/darroyo/nasctl/internal/system"
|
|
)
|
|
|
|
type Capabilities struct {
|
|
Rsync bool `json:"rsync"`
|
|
MergerfsBin bool `json:"mergerfs"`
|
|
MergerfsMounted bool `json:"mergerfs_mounted"`
|
|
SnapraidBin bool `json:"snapraid"`
|
|
}
|
|
|
|
func Probe(ctx context.Context) (Capabilities, error) {
|
|
_, rsyncErr := exec.LookPath("rsync")
|
|
_, snapraidErr := exec.LookPath("snapraid")
|
|
_, mergerfsErr := exec.LookPath("mergerfs")
|
|
|
|
_, _, mergerfsMountedErr := system.Run(ctx, "grep", "-q", "fuse.mergerfs", "/proc/mounts")
|
|
|
|
return Capabilities{
|
|
Rsync: rsyncErr == nil,
|
|
MergerfsBin: mergerfsErr == nil,
|
|
MergerfsMounted: mergerfsMountedErr == nil,
|
|
SnapraidBin: snapraidErr == nil,
|
|
}, nil
|
|
}
|
|
|
|
type PathStat struct {
|
|
Path string `json:"path"`
|
|
Available bool `json:"available"`
|
|
TotalBytes uint64 `json:"total_bytes"`
|
|
UsedBytes uint64 `json:"used_bytes"`
|
|
FreeBytes uint64 `json:"free_bytes"`
|
|
UsedPercent float64 `json:"used_percent"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
func StatPath(p string) PathStat {
|
|
out := PathStat{Path: p, Available: false}
|
|
if p == "" {
|
|
out.Error = "path no configurado"
|
|
return out
|
|
}
|
|
var st syscall.Statfs_t
|
|
if err := syscall.Statfs(p, &st); err != nil {
|
|
out.Error = err.Error()
|
|
return out
|
|
}
|
|
out.Available = true
|
|
out.TotalBytes = st.Blocks * uint64(st.Bsize)
|
|
out.FreeBytes = st.Bavail * uint64(st.Bsize)
|
|
out.UsedBytes = out.TotalBytes - out.FreeBytes
|
|
if out.TotalBytes > 0 {
|
|
out.UsedPercent = float64(out.UsedBytes) / float64(out.TotalBytes) * 100
|
|
}
|
|
return out
|
|
}
|
|
|
|
func ValidatePaths(cfg db.StorageConfig) error {
|
|
return nil
|
|
}
|