Backend:
- ListStorageJobs, ListSambaShares, ListNFSExports, ListUsers,
ListDirty, ListApplyLog, ListWatchedMounts: initialize with
make([]T, 0) instead of var x []T to avoid JSON null on empty.
Fixes "Cannot read properties of null" crash on /storage.
Frontend:
- Storage.tsx: defensive setJobs(res.jobs ?? []) to guard against
API returning null.
Tests:
- Add TestListStorageJobsEmptyReturnsSlice.
Version: 0.7.1
New 'Almacenamiento' page with:
- Auto-detection of rsync, mergerfs, snapraid binaries and mergerfs mount
- Configurable pool settings (source/dest, macOS cleanup, rsync flags)
- Mergerfs mover with dry-run preview and live SSE output streaming
- SnapRAID diff/sync/scrub/check with live SSE output
- Async job system (1 concurrent job) with SSE streaming
- Job history table
Backend:
- internal/storage/ package with capabilities, mergerfs, snapraid, jobs
- storage_config and storage_jobs DB tables (migration 0008)
- GET/PUT /api/storage/config, GET /api/storage/capabilities
- POST/GET /api/storage/jobs, GET /api/storage/jobs/{id}/stream
- Storage operations disabled when NASCTL_EXEC_SYSTEM=false
Closes #new-feature