26 Commits

Author SHA1 Message Date
darroyo 10d0769071 feat!: complete visual redesign → 1.0.0
BREAKING: version bump to 1.0.0

New design system:
- Emerald/teal color palette with semantic tokens (success, warning, destructive)
- Light/dark mode toggle with localStorage persistence and anti-FOUC
- CSS custom properties via Tailwind darkMode: 'class'
- Zero new dependencies — all primitives hand-rolled

Foundation:
- 70+ inline SVG icons (no external icon library)
- ThemeProvider + useTheme hook
- cn() utility for conditional classes

UI primitives (web/src/components/ui/):
- Button (6 variants, 4 sizes)
- Input, Label, Card with slots, Badge (6 variants + sizes)
- Switch (animated), Checkbox, Separator
- Skeleton, Spinner, EmptyState
- Toast system with auto-dismiss (success/error/warning/info)
- Dialog with focus trap + escape/click-outside
- ConfirmDialog replacing native confirm()
- DropdownMenu with keyboard support

Layout & navigation:
- Sidebar with icon+label nav, active indicator, responsive drawer on mobile
- Topbar for <lg viewports with hamburger
- PageHeader with title/description/actions pattern
- UserMenu with avatar + dropdown (change password, logout)
- DirtyBanner redesigned with semantic styling

Pages redesigned:
- Login: centered card with gradient background
- Dashboard: 4 KPI tiles, disk usage bars, services grid, recent activity
- Users: table with search, empty state, edit modal
- Files: breadcrumbs, drag-drop upload zone, SVG file icons, rename/delete
- Samba: card list with badges, edit modal with PathField
- NFS: card list with client chips, client editor
- Storage: tabs (Mover/SnapRAID/Jobs), real-time job log streaming
- Log: timeline grouped by date with filters
- Settings: watched mounts, import actions, system info
- NotFound: friendly 404 page

Polish:
- Toast notifications replace all native alert()/confirm()
- Tailwind animations (dialog-enter, toast-enter/leave, dropdown-enter)
- Custom scrollbar styling
- Focus-visible rings
- Skeleton loading states across all pages
2026-07-08 01:24:51 -04:00
darroyo 56e5fabe8d feat(storage): paginate operations history with prev/next controls
Backend:
- ListStorageJobs(limit, offset) adds OFFSET for pagination
- CountStorageJobs() returns total row count for UI
- handler returns { jobs, total, limit, offset }
- JobManager.List(limit, offset) updated signature

Frontend:
- loadJobs(offset) with default 0
- Pagination UI: 'Mostrando X-Y de Z' + Anterior/Siguiente buttons
- After job start/end, reloads from offset 0
- listStorageJobs(limit, offset) API updated

Tests: fix List/ListStorageJobs calls to include offset=0
2026-07-07 09:27:06 -04:00
darroyo 599af5a828 feat(storage): rename snapraid_content to snapraid_conf; clarify conf vs content file; add auto-detect and conf viewer 2026-07-07 01:17:55 -04:00
darroyo 197b2ad13e feat(storage): show job error details in history table and output panel 2026-07-07 00:45:03 -04:00
darroyo 428056e1be feat: show disk usage of mover source with configurable warning threshold
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
2026-07-07 00:30:32 -04:00
darroyo d3d59b4e45 fix: nil slice -> empty slice in list API responses
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
2026-07-06 23:31:03 -04:00
darroyo 27a52d2986 feat: add mergerfs mover and snapraid integration
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
2026-07-06 23:23:22 -04:00
darroyo 319030848f fix: wrap Samba page header buttons in same flex container so they stay together 2026-07-06 22:41:46 -04:00
darroyo 155b5a4b21 feat: add re-import from system buttons for Samba and NFS
Add 'Re-importar del sistema' buttons to the Samba and NFS pages,
mirroring the existing user import flow. Both buttons show a confirm
dialog before calling the respective /api/import/{samba,nfs} endpoints
which replace the DB table with the current system config.

Version bump: 0.6.0 → 0.6.1
2026-07-06 22:29:39 -04:00
darroyo 03e9368a91 feat: add invalid_users directive for Samba shares
Samba shares now support an 'invalid users' list (deny list), written
as 'invalid users = u1,u2' in smb.conf. The UI shows a ChipPicker
for valid_users and invalid_users, mutually exclusive, sourced from
the system user list.

feat: add ImportSystemUsers for fresh installations

When NASCTL_IMPORT_ON_BOOT=true, nasctl now imports existing system
users from /etc/passwd (UID 1000-60000) and /etc/group (supplemental
groups), and detects which have Samba accounts via 'pdbedit -L'.
Imported users are marked dirty so the admin can review before applying.
New POST /api/import/users endpoint for manual re-import.

This mirrors the existing import-on-boot flow for smb.conf and /etc/exports.
2026-07-06 15:37:01 -04:00
darroyo 59505b443f fix(nfs): reconcile clients[i].advanced type mismatch (object vs string)
backend: db.NFSClient.Advanced is db.NFSAdvanced (struct → JSON object)
handler: nfsClientRequest.Advanced was string → now db.NFSAdvanced
frontend: api.ts NFSClient.advanced string → NFSAdvanced (object)
frontend: Nfs.tsx updated to work with object advanced per host

Without this fix, editing an existing NFS export with per-host options
(loaded from DB via migration 0005) would 400 on PUT because the
backend expected a string but received a JSON object.
2026-07-06 11:45:44 -04:00
darroyo 512feaffd7 feat(nfs): per-host NFS options (IP/CIDR with own ro/async/squash flags)
This is a backward-compatible MINOR bump (0.4.0 → 0.5.0).

BREAKING NOTES (for users upgrading from pre-0.5.0):
- The nfs_exports.clients column schema changed from []string to
  []NFSClient (per-host options). A migration (0005) transforms existing
  string arrays into object arrays, taking export-level options as
  defaults for each host.
- ValidateNFSClient now only accepts IPv4 (192.168.1.1) or IPv4/CIDR
  (192.168.1.0/24). Hostnames, wildcards, netgroups are rejected.
- If you use NASCTL_IMPORT_ON_BOOT, re-import your /etc/exports to pick
  up per-host options.

What changed:
- NFSClient type: {host, read_only, async, root_squash, subtree_check, advanced}
- NFSExport.Clients is now []NFSClient (was []string)
- export-level flags (ro/async/root_squash/subtree_check/advanced) are
  preserved as template defaults for newly added hosts in the UI.
- buildExportLine generates: path host1(ro,sync,...) host2(rw,async,...) fsid=N
- ValidateNFSClient: strict IPv4/CIDR only (0-255 octets, /0-32 prefix)
- parseExportLine now parses per-host options from /etc/exports (previously
  only the first host's options were kept, others were discarded)
- UI: per-host rows with toggles (ro/async/root_squash/subtree_check) and
  advanced options (all_squash, secure, wdelay, hide, crossmnt)
2026-07-06 11:30:27 -04:00
darroyo 0a4004a9ab fix: show permissions as drwxr-x/755 instead of drwxr-x755 for clarity 2026-07-06 10:31:57 -04:00
darroyo a88f92e786 feat: add direct path input bar to Files page and FileBrowserModal 2026-07-06 09:42:50 -04:00
darroyo 511cdde911 fix: prevent loadDir from clearing search state on every directory load 2026-07-06 09:02:58 -04:00
darroyo 238ca23d5b fix: null guard on API array responses to prevent crash 2026-07-06 08:51:59 -04:00
darroyo 50ed8abe95 feat: file manager with browse, search, upload, chmod/chown
- Full file browser page at /files with lazy-load, breadcrumbs, drag&drop
- FileBrowserModal component for path selection from Samba/NFS forms
- PathField component replaces bare inputs in share/export forms
- Backend: /api/files/* routes with List, Mkdir, Rename, Delete, Chmod, Chown, Upload, Download, Preview, Search
- Reuses NASCTL_ALLOWED_ROOTS for path validation
- NASCTL_UPLOAD_MAX_BYTES (100MB) and NASCTL_PREVIEW_MAX_BYTES (256KB) env vars
- Capabilities endpoint returns chmod/chown availability (requires root)
- Version bump: 0.3.2 -> 0.4.0
2026-07-06 01:56:40 -04:00
darroyo 5c9ece6891 fix: hide used_by when only one source is present
used_by (share names) shown only when path has both samba and nfs
to avoid redundancy with the source chips.
2026-07-06 00:45:28 -04:00
darroyo 508890a232 feat: multi-source disk entries with chips display
diskUsage now carries Sources []string instead of a single Source.
collectDiskUsage accumulates all sources (samba, nfs, manual) per path.
NFS no longer adds a redundant label to used_by.

Dashboard and Settings render one chip per source. When a path
is shared via SMB and NFS, both chips appear.

API breaking: disks[].source replaced by disks[].sources[].
Version: 0.2.1 -> 0.3.0
2026-07-06 00:40:41 -04:00
darroyo e3add68584 feat: structured NFS options with auto-fsid and manual watched mounts
NFS exports: replace plain-text options string with typed booleans
(read_only, async, root_squash, subtree_check) + advanced JSON blob.
fsid is auto-generated via crypto/rand with collision retry and is
never user-settable. Breaking API change (options field removed).

Dashboard: filter disks to manual+samba+nfs sources only; no more
auto-discovery of all /proc/mounts entries.

Settings: new 'Puntos de montaje vigilados' card with add/remove
for manual mount points. AllowedRoots validation applied.

Version bump: 0.1.10 -> 0.2.0
2026-07-06 00:23:49 -04:00
darroyo a61ae04009 fix: use try-restart in postinst so .deb upgrade reloads the service
- Bump VERSION 0.1.6 -> 0.1.7
- Change systemctl start -> try-restart in postinst script so that
  upgrading the package actually restarts nasctl with the new binary
2026-07-05 23:14:31 -04:00
darroyo ac07499e8d feat: bake version into binary and display in UI sidebar
- Inject VERSION via ldflags (-X main=web.Version=) and git short SHA
- Add /api/version endpoint returning {version, commit}
- Display version in Layout sidebar footer (v0.1.6 · 8cfd405)
- Add AGENTS.md section documenting SemVer policy and bump-before-push rule
- Bump VERSION 0.1.5 -> 0.1.6 in Makefile
2026-07-05 23:07:20 -04:00
darroyo 8cfd405ffc cambios 2026-07-05 22:59:37 -04:00
darroyo 65cd753818 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
2026-07-05 22:56:39 -04:00
darroyo 72a8336087 feat: add admin password change via /settings page
Backend:
- DB: add UpdateAdminPasswordHash(id, hash) in queries_users.go
- Auth: add ChangePassword(username, old, new) method that verifies
  old password, validates length >= 8, and stores bcrypt hash
- Handlers: add handleChangePassword on PUT /api/auth/password (auth required)
- Router: register route inside protected group

Frontend:
- api.ts: add changePassword(oldPassword, newPassword)
- Settings.tsx: new page with form (current + new + confirm), client-side
  validation, success/error feedback
- App.tsx: add /settings route
- Layout.tsx: add 'Ajustes' nav item
2026-07-05 22:31:45 -04:00
darroyo 4f0754ecc5 Add nasctl: Go NAS control plane with React frontend 2026-07-05 17:37:19 -04:00