Commit Graph
36 Commits
Author SHA1 Message Date
marcandClaude Sonnet 4.6 1de49d51fb WebDAV: move DAV On to <Location> block, fixes 405 with Alias
<Directory> handles filesystem options; <Location> handles DAV method
routing and auth. DAV On in a <Directory> block doesn't reliably enable
WebDAV methods on Alias-mapped paths in Apache 2.4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 19:02:58 +02:00
marcandClaude Sonnet 4.6 e4b23d7e2f Remove nobody from owner whitelist — not useful for folder ownership
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 17:44:13 +02:00
marcandClaude Sonnet 4.6 06013a1e59 Add www-data/nobody to owner dropdown via _USER_WHITELIST
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 17:31:01 +02:00
marcandClaude Sonnet 4.6 b6179865b5 Remove user rename feature; add www-data hint in WebDAV edit modal
- Removed PATCH /api/users/<name> route (rename_user)
- Removed renameModal from index.html
- Removed openRenameModal/submitRename from users.js
- Removed Rename button from users table
- Removed rename docs from README
- Added green info box in WebDAV edit modal: set group=www-data, mode=775

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 17:01:56 +02:00
marcandClaude Sonnet 4.6 f081496b4b README: remove one-time migration section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:58:27 +02:00
marcandClaude Sonnet 4.6 1ef4f6f743 Extract _ensure_conf_dir(); call it from both _save_credentials and _write_webdav_conf
Guarantees /etc/micronas is created with 750 root:www-data on any
code path that writes config — first-time setup OR first WebDAV share.
Fixes fresh-install permission issue without manual chmod.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:56:57 +02:00
marcandClaude Sonnet 4.6 7ec926ddf7 Fix /etc/micronas dir permissions: 750 root:www-data so Apache can traverse
700 blocked www-data from entering the dir even though the passwd file
had correct group permissions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:54:25 +02:00
marcandClaude Sonnet 4.6 aef2e8a01b README: document Phase 5 features and upgrade migration
- Admin credential change (Settings button in topbar)
- User rename (usermod -l, password reset required after)
- PBKDF2 hashing detail in Auth section
- Upgrade migration steps for /etc/micronas/ path change
- Fix dev/test snippet to use MICRONAS_PASS_HASH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:53:20 +02:00
marcandClaude Sonnet 4.6 1f40f08e12 Fix stale /etc/micronas.env path reference in setup.html
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:51:35 +02:00
marcandClaude Sonnet 4.6 7194aaf7e9 Move config files to /etc/micronas/, fix WebDAV passwd permissions
- /etc/micronas.env      → /etc/micronas/micronas.env
- /etc/micronas-webdav.passwd → /etc/micronas/webdav.passwd
- _save_credentials creates /etc/micronas/ if missing
- _write_webdav_conf: chown root:www-data on passwd file so Apache can read it
- micronas.service EnvironmentFile updated
- README: credentials now set via /setup, config dir step added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:48:02 +02:00
marcandClaude Sonnet 4.6 a29862860d Phase 5: secure credentials, first-login setup, user rename, admin settings
Security:
- PBKDF2-HMAC-SHA256 password hashing (260k iterations, random salt per password)
- Legacy MICRONAS_PASS plaintext still works for existing installs
- MICRONAS_PASS_HASH replaces plaintext in env file after first setup/change
- Timing-safe comparison via hmac.compare_digest

First-login setup:
- /setup route: shown when no credentials configured (fresh install)
- Replaces editing /etc/micronas.env manually
- setup.html dark-themed form matching login.html

Admin credential change:
- POST /api/admin/credentials (requires current password to confirm)
- Settings button in topbar opens modal for username + password change
- Session cleared after change → redirected to login

User management:
- PATCH /api/users/<name>: rename Linux user (usermod -l)
  SMB + WebDAV entries removed, password reset required after rename
- Rename button in users table, modal with yellow warning

WebDAV:
- Options Indexes: directories now browsable in a regular web browser

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 16:45:16 +02:00
marcandClaude Sonnet 4.6 559954b222 Fix WebDAV 500 when htpasswd file doesn't exist yet
Create an empty /etc/micronas-webdav.passwd on first webdav config write
so Apache returns 401 instead of 500 before any user password is set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 09:24:36 +02:00
marcandClaude Sonnet 4.6 9db51ca927 Add WebDAV settings reference to README
Documents allowed users, password sync, www-data folder permissions,
and the no-toggle limitation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 09:18:36 +02:00
marcandClaude Sonnet 4.6 7c9fe07a17 Add WebDAV support via Apache mod_dav
Backend:
- WebDAV config managed at /etc/apache2/conf-available/micronas-webdav.conf
  with BEGIN/END markers per share; reload apache2 on every write
- htpasswd file at /etc/micronas-webdav.passwd synced automatically on
  create_user, change_password, delete_user
- Full CRUD: POST/PATCH/DELETE /api/share/webdav[/<name>]
- _validate_webdav_users(): space-separated, no @group (not supported by Apache)
- Fix: _validate_users() now allows @groupname for SMB (was incorrectly rejected)
- Dashboard: apache2 status + restart button; webdav shares in /api/dashboard

Frontend:
- WebDAV tab: create form + table with clickable access URLs
- Edit modal supports 'webdav' type (name, path, users, permissions section)
- badge-webdav CSS class (green tint) in app.css
- dashboard.js renders badge-<type> dynamically (smb/nfs/webdav)

Docs: README WebDAV mount guide (Linux davfs2, Windows, Mac), PLANNING phase 4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 01:22:19 +02:00
marcandClaude Sonnet 4.6 1be6a05cef Add dashboard IPs/restart/connections, SMB read-only and recycle bin
Dashboard:
- Server IPs card showing all non-loopback IPv4 interfaces
- Restart buttons next to smbd/nfsd status dots (POST /api/service/<name>/restart)
- Active SMB connections table (smbstatus -b parsed in backend)

SMB edit modal:
- Read only toggle (writable vs read only in smb.conf)
- Recycle Bin toggle (vfs objects = recycle, files go to .recycle folder)

Docs: PLANNING.md phase 3.5, README recycle bin + read-only, CLAUDE.md JS overview

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 01:13:32 +02:00
marcandClaude Sonnet 4.6 1810d60d21 Document SMB/NFS share settings in README
Explains valid users, folder permissions, force user/group, guest ok,
no_root_squash, all_squash with use cases and recipes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 01:00:02 +02:00
marcandClaude Sonnet 4.6 0d09882268 Add extra SMB/NFS settings to edit modal
SMB: force user, force group, guest ok fields + dropdowns for owner/group
NFS: no_root_squash and all_squash toggles exposed in edit modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 00:55:40 +02:00
marcandClaude Sonnet 4.6 596365b805 Add group management and owner/group dropdowns in permission editor
Backend:
- _list_groups(): reads /etc/group, returns groups with gid >= 1000
  plus a small whitelist (root, staff, sambashare, www-data)
- GET/POST /api/groups: list and create groups (groupadd)
- DELETE /api/groups/<name>: delete group (groupdel)
- POST /api/groups/<name>/members: add user to group (gpasswd -a)
- DELETE /api/groups/<name>/members/<user>: remove user (gpasswd -d)

Frontend:
- Users tab now has a Groups section: table with GID, member badges
  with × remove buttons, per-row "add member" select populated with
  users not already in the group, delete group button
- loadUsers() caches result in _cachedUsers for groups table to use
- openEditModal is now async; fetches /api/users + /api/groups before
  rendering so Owner and Group inputs have datalist autocomplete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 00:47:43 +02:00
marcandClaude Sonnet 4.6 302a5cb023 Add folder permission editor to share edit modal
Backend:
- Import pwd/grp for uid/gid → name resolution
- _path_stat(path): returns current owner, group, octal mode via os.stat
- get_all_shares: attaches stat info to each share so the modal can
  pre-fill current values
- POST /api/permissions: validates and applies chown/chmod to a share
  path; returns updated stat on success

Frontend:
- Edit modal now has a Permissions section (Owner / Group / Mode)
  pre-filled with current values, with a hint line showing common modes
- Valid Users label notes @groupname syntax for Samba group access
- submitEdit applies permissions after saving the share config if any
  permission field is non-empty; shows specific error on failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 00:26:00 +02:00
marcandClaude Sonnet 4.6 e03905b26d README: add Linux client mount guide for SMB and NFS
Covers one-time and permanent (fstab) mounts for both protocols,
credentials file for SMB, _netdev flag, multi-network NFS clients,
and useful commands (showmount, umount, mount -a).
Also remove stale "quotas" from the project description line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 23:55:50 +02:00
marcandClaude Sonnet 4.6 d7892fa7c5 Fix NFS parser showing /etc/exports example comments as disabled exports
The default nfs-kernel-server package ships /etc/exports with commented
example lines like '# /srv/homes hostname1(rw,...)'. The old parser
treated any comment containing a path as a disabled export, so these
examples showed up in the UI.

Fix: use a distinct '## ' prefix (double-hash + space) for exports that
MicroNAS disables. The parser now only recognises '## /path ...' as a
disabled export — any other '#' line is left as a plain comment.
_write_exports updated to use the same prefix when toggling off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 23:52:11 +02:00
marcandClaude Sonnet 4.6 96055e6375 Codebase cleanup: remove dead code, fix docker, update docs
app.py:
- Remove 'raw' field from export dict entries (only comment/blank
  kinds need raw; export entries are always reconstructed from
  path/rest/enabled when writing back)
- Remove extra blank line

shares.js:
- Remove unused pathJson variable in SMB row (was only needed for
  the quota button, which was removed)

docker-compose.yml:
- Deleted entirely: was missing templates/ and static/ mounts so the
  Flask app couldn't serve pages; SYS_ADMIN cap_add was only needed
  for quotas; deployment is systemd, not Docker

README.md:
- Remove quota package from apt install command
- Remove quota setup section (feature removed)
- Remove docker-compose from Dev section

CLAUDE.md / PLANNING.md:
- Remove docker-compose.yml references and quotas.js from file tree

.gitignore:
- Remove redundant micronas.env entry (already covered by *.env)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 23:11:41 +02:00
marcandClaude Sonnet 4.6 bf7daf1c2c Remove quota feature (incompatible with ZFS-backed LXC)
Linux setquota/repquota require quota support on the underlying
filesystem. The target deployment is a Proxmox LXC on ZFS storage where
this is not available inside the container.

Removed: /api/quota endpoint, _quota_supported, _quota_usage,
_find_quota_fs, quotas.js, quota modal, dashboard quota table,
Quota button from share rows.

PLANNING.md updated: notes quota as out of scope with reason.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 23:07:28 +02:00
marcandClaude Sonnet 4.6 af315b37b2 Detect unsupported quota filesystems (ZFS) and hide quota UI
- _quota_supported() checks df -T /srv; returns false + reason for ZFS,
  tmpfs, overlay — surfaces as quota_supported/quota_reason in dashboard API
- Dashboard quota section shows the reason string instead of an empty table
- Quota button hidden from share rows when quota_supported is false
- Prevents setquota errors for ZFS-backed Proxmox LXC containers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 23:00:18 +02:00
marcandClaude Sonnet 4.6 f4943b6b6d Add refresh countdown, fix quota fs lookup, update README
- main.js: show live countdown "Updated HH:MM:SS — next in Xs" so the
  30s auto-refresh is visually obvious instead of just a static timestamp
- app.py: rename _find_mountpoint → _find_quota_fs and fix the stale
  call site that caused a NameError; function now returns mountpoint from
  df, falling back to the path itself if df returns /
- README: add full quota setup section (fstab usrquota, quotacheck,
  quotaon, verify with repquota); fix stale "127.0.0.1" bind note
  (service already uses 0.0.0.0); update Auth section (sessions, not
  Basic Auth)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:38:42 +02:00
marcandClaude Sonnet 4.6 6631b9e832 Fix quota endpoint: path detection, error handling, better messages
- Backend: detect the actual filesystem mountpoint via 'df' instead of
  hardcoding /srv/samba — setquota needs the mountpoint, not a subpath
- Backend: catch CalledProcessError and FileNotFoundError from setquota
  and return the actual error message (e.g. quota not enabled, package
  not installed) instead of crashing with 500
- Frontend: always send the share path in the quota API call so the
  backend can find the right filesystem, even for SMB per-user quotas
- Frontend: read the actual error message from the response and show it
  in the toast instead of just "N quota(s) failed"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:36:15 +02:00
marcandClaude Sonnet 4.6 74737568b0 Fix broken action buttons and add delete confirmation modal
- shares.js, users.js: JSON.stringify() inside onclick="..." attributes
  produced unescaped " chars that broke HTML attribute parsing — all
  dynamic onclick JSON now passed through esc() so the browser decodes
  &quot; back to " before executing the handler
- Replace browser confirm() dialogs with a styled Bootstrap modal
  (#deleteModal) for share and user deletion — reusable across all tabs
- users.js: rename shadow variable 'confirm' → 'pwConfirm' to avoid
  shadowing the now-removed confirm() call
- main.js: auto-refresh now reloads shares and users every 30 s,
  not just the dashboard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:33:37 +02:00
marcandClaude Sonnet 4.6 67dacd4044 Update PLANNING.md and CLAUDE.md to reflect current state
- Mark Phase 1, 2, 2.5, 3 as complete
- Add project structure diagram to PLANNING.md
- Update CLAUDE.md: correct file layout, session auth, safe config
  rewrite pattern, remove stale references to index.html and Basic Auth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:30:13 +02:00
marcandClaude Sonnet 4.6 5d9a9e62ec Phase 3: full share CRUD + session login page
Backend:
- SMB config parser/writer (_parse_smb_conf, _write_smb_conf) for
  safe whole-file rewrites instead of append-only
- NFS exports parser/writer (_parse_exports, _write_exports) with
  support for disabled (commented-out) exports
- DELETE/PATCH/toggle endpoints for SMB and NFS shares
- parse_smb() now returns enabled field (reads available = no)
- get_nfs_shares() uses the new parser and returns enabled field
- Replace HTTP Basic Auth with Flask session auth (secret key derived
  from MICRONAS_PASS so sessions survive restarts)
- /login GET/POST, /logout routes

Frontend:
- Login page (templates/login.html) with same dark theme
- Sign out button in header
- Edit modal pre-filled with current share values
- Delete buttons wired up (with confirm dialog)
- Enable/Disable toggle per share (green/grey dot status indicator)
- Disabled shares shown at reduced opacity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:26:32 +02:00
marcandClaude Sonnet 4.6 1118681fd2 Restructure frontend into templates/ and static/
- Move index.html → templates/index.html (Jinja2, uses url_for)
- Extract CSS → static/css/app.css
- Split JS into static/js/: utils.js, dashboard.js, shares.js,
  quotas.js, users.js, main.js
- Switch app.py from send_file to render_template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:19:46 +02:00
marcandClaude Sonnet 4.6 822e6b3b87 Phase 2.5: UI redesign, inline quotas, toasts, Users tab
- Full UI redesign: GitHub-dark palette, custom CSS vars, better
  contrast, card layout, cleaner tables and typography
- Dashboard auto-refresh every 30s (was 60s)
- Remove Quotas tab; each share row now has a Quota button that opens
  a modal pre-filled with the share's valid users (SMB) or path (NFS)
- Replace all alert() with Bootstrap toast notifications
- Add Users tab: list system users (uid >= 1000) with SMB status,
  create user (useradd + smbpasswd), delete user, change SMB password
- Backend: /api/users GET/POST, /api/users/<name> DELETE,
  /api/users/<name>/password POST

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:14:10 +02:00
marcandClaude Sonnet 4.6 d237f4d290 Bind gunicorn to 0.0.0.0 for LAN access
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:05:50 +02:00
marcandClaude Sonnet 4.6 4d697c7ca4 Fix README git clone URL to use Gitea
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:00:48 +02:00
marcandClaude Sonnet 4.6 458092a3c6 Add Phase 2 dashboard + fix Phase 1 issues
- Implement parse_smb(), remove shell=True, add input validation, HTTP Basic Auth (Phase 1)
- Add /api/dashboard with daemon status, disk usage, per-share du, quota usage (Phase 2)
- Rewrite index.html with Dashboard as landing tab, progress bars, XSS-safe rendering
- Update README with full LXC install guide including git clone and update steps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 21:58:47 +02:00
Honey 10a1e24420 Rename project to MicroNAS 2026-06-19 21:06:15 +02:00
Honey f2aca2e3ea Initial commit: MicroNAS share manager (Phase 1) 2026-06-19 21:02:46 +02:00