Commit Graph

19 Commits

Author SHA1 Message Date
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 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 3e290164c5 fix(nfs): move fsid=N inside client parentheses in /etc/exports
exportfs parses 'path host(flags) fsid=N' as a separate fsid= token
without host or options, causing 'No options for path fsid=N' errors.
The correct format puts fsid=N inside the host parentheses:
  /path host(flags,fsid=N)

Changes:
- clientFlags(c, fsid) now appends fsid=N at the end
- buildExportFlags(e, fsid) same
- buildExportLine removed buildExportSuffix call; fsid is now per-client
- Removed now-unused buildExportSuffix function

Version: 0.5.3
2026-07-06 12:20:35 -04:00
darroyo d380cad36d fix(nfs): add migration 0006 to repair fsid=0 exports
Migration 0005 (MigrateNFSClients) only runs once when first applied,
so any fsid=0 rows present before 0005 was applied never get repaired.

Migration 0006 triggers FixZeroFSIDs() which:
- Queries all nfs_exports rows with fsid=0
- Generates a new random fsid per row
- Updates the row

Version: 0.5.2
2026-07-06 12:10:20 -04:00
darroyo 667576e5bd fix(nfs): assign non-zero fsid during MigrateNFSClients migration
When migrating old clients format (string array) to new format
(NFSClient objects), also fix any exports that have fsid=0 by
generating a new random fsid. This resolves exportfs errors like:
  exportfs: /path fsid=0 requires fsid= for NFS export

The migration now checks if r.FSID == 0 and generates a new one
using system.GenerateRandomFSID() before updating the row.
2026-07-06 12:00:56 -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 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 0da789cd96 fix: populate fsid for legacy NFS exports on migration
Migration 0002 added fsid column with DEFAULT 0 but never
populated existing rows. Now PopulateLegacyFSIDs() runs after 0002
to extract fsid from legacy options string or generate random.

Also: move generateRandomFSID to internal/system to avoid import
cycles. Migration 0004 drops the legacy options column.

Version: 0.3.1 -> 0.3.2
2026-07-06 01:22:16 -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 63e0b5146a fix: tolerate duplicate column errors in migration runner
The previous SAVEPOINT approach failed because Go's tx.Exec() stops at
the first error and never reaches the ROLLBACK TO statements. This caused
every subsequent startup to re-run migration 0002, fail on the first
ALTER (column already exists from a prior partial run), and crash.

Fix runMigration() to:
- Execute migrations without a wrapping transaction (DDL in SQLite is
  auto-commit anyway)
- Treat "duplicate column name" / "already exists" errors as success
  and record the migration anyway, covering cases where a previous
  failed attempt already partially modified the schema

Also simplify 0002_nfs_structured.sql back to plain ALTER TABLE
statements (no SAVEPOINT needed with the new Go-level tolerance).

Bump VERSION 0.1.9 -> 0.1.10
2026-07-06 00:05:02 -04:00
darroyo 9a9d4cc753 fix: implement migration tracking and make 0002 idempotent
- Add schema_migrations table to track applied migrations
- Migrate() now checks if a migration was already applied before running
- 0002 rewritten to use SAVEPOINT+ROLLBACK per column, making it safe
  to run even if some columns were partially added previously
- Each migration runs in its own transaction; INSERT into schema_migrations
  only happens if the SQL executes without error
- Bump VERSION 0.1.8 -> 0.1.9
2026-07-05 23:54:14 -04:00
darroyo 3d956d43c2 fix: split ALTER TABLE into separate ADD COLUMN statements in migration 0002
SQLite does not support adding multiple columns in a single ALTER TABLE
statement. The previous migration tried to add 6 columns at once and
caused a syntax error, crashing the service at startup.

Bump VERSION 0.1.7 -> 0.1.8
2026-07-05 23:26:53 -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 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 4ae7335b31 feat: import existing smb.conf and /etc/exports on first boot
Adds auto-detection of pre-existing Samba shares and NFS exports when
nasctl is installed on a host that already has these configs.

New package internal/importer parses smb.conf (INI-style) and
/etc/exports (line-based) and imports them into SQLite.

Imported shares/exports are marked dirty so the user must review
and apply manually before any file is overwritten.

Backup: before the first Apply, each module backs up the original
config to <path>.nasctl.bak.<timestamp> (one time only).

New CLI flag --import-on-boot / NASCTL_IMPORT_ON_BOOT env var
(default false, opt-in).

New API endpoints:
  GET  /api/import/status
  POST /api/import/samba
  POST /api/import/nfs

New DB methods ReplaceSambaShares/ReplaceNFSExports (transactional
replace-all), guarded by import.samba.done / import.nfs.done
settings flags.
2026-07-05 21:45:25 -04:00
darroyo 4f0754ecc5 Add nasctl: Go NAS control plane with React frontend 2026-07-05 17:37:19 -04:00