Disk info is now shown directly in the table. Speed test and detailed
disk info buttons/commands are no longer needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Disk Info: runs df -h and df -i on remote via SSH (or locally)
- Speed Test: uploads/downloads 10MB test file via rsync, measures Mbps
- Both available as CLI commands: gniza remotes disk-info/speed-test --name=NAME
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Last Run: timestamp of most recent backup log file
- Next Run: calculated from schedule type, time, and day settings
- Handles hourly, daily, weekly, monthly schedule types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browser prompts for username/password before showing the TUI.
Credentials from gniza.conf: WEB_USER (default: admin) + WEB_API_KEY.
- Monkey-patches textual-serve's aiohttp app with auth middleware
- Uses secrets.compare_digest for timing-safe comparison
- Install script generates credentials and prints them
- Skips auth if no WEB_API_KEY configured
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The landing page wasn't interactive because public_url resolved to
localhost, making WebSocket connections fail from remote browsers.
- Added multiple IP detection methods (socket, hostname -I, gethostbyname)
- Support --port= and --host= flag formats
- Print actual serving URL on startup
- Switch web start back to textual-serve (TUI in browser)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Revert from textual-serve back to Flask (textual-serve had WebSocket issues)
- Completely redesigned dashboard: modern dark theme, stat cards, clean tables
- Redesigned login page to match
- Restored API key generation in install script
- Keep API key field in TUI settings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Serves the exact same TUI in the browser via textual-serve.
No more separate Flask app, API keys, or login page needed.
- gniza web start now runs textual-serve instead of Flask
- Simplified systemd service to use python3 -m tui --web
- Removed web_enabled and web_api_key from settings/models
- Simplified install script web setup (no API key generation)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix GNIZA_CONFIG_DIR in systemd service: /usr/local/gniza/etc -> /etc/gniza
- Add LOG_DIR and PYTHONPATH to systemd environment
- Fix default CONFIG_DIR in web/app.py and web/__main__.py
- Refactor install.sh web setup into function for robustness
- Replace Unicode box-drawing chars with ASCII in install.sh
- Read from /dev/tty to prevent stdin issues
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use POSIX [ ] tests instead of [[ ]], avoid case statement,
replace em-dash with ASCII, use simple if/then/fi structure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace nested if/fi with case statement and inline sed calls
instead of helper function. Add error handling for service install.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The login handler was re-reading the config on each attempt, which
returned empty when no key was set, causing all logins to fail.
Now uses the key resolved at startup (from config or generated).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Flask web dashboard with dark theme matching TUI
- Login with API key authentication
- Dashboard shows targets, remotes, schedules, last backup status
- Trigger backups from web UI per target
- View logs via /api/logs endpoint
- systemd service: gniza web install-service / remove-service / status
- CLI: gniza web start [--port=PORT] [--host=HOST]
- TUI settings: web enabled, port, host, API key fields
- Install script: optional web dashboard setup with auto-generated API key
- Uninstall script: removes systemd service
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace flat file list with a Tree widget that shows directory
structure. Strips remote path prefix to show relative paths only.
Folders shown in bold with trailing /, sorted dirs-first.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Select a snapshot and click Browse Files to see all files in that
snapshot on the remote. Also adds 'gniza snapshots browse' CLI command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows a switch to include/skip MySQL restore when the selected target
has MySQL enabled. Hidden for targets without MySQL. Passes --skip-mysql
to CLI when toggled off.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- mysql_dump_grants(): backs up user grants via SHOW CREATE USER + SHOW GRANTS,
skipping system users (root, mysql.sys, etc.)
- mysql_restore_databases(): restores .sql.gz dumps and grants.sql from snapshot
- Backup flow: grants dumped alongside database dumps into _mysql/
- Restore flow: automatically restores MySQL databases and grants when _mysql/
exists in snapshot and target has MySQL enabled
- CLI: --skip-mysql flag to opt out of MySQL restore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Other OperationLog dialogs (crontab show, remote test, retention) no
longer display the spinner since they complete quickly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract schedule add/edit form into ScheduleEditScreen (follows target_edit pattern)
- Fix toggle active: now properly installs/removes crontab entries with error reporting
- Delete also syncs crontab to remove deleted schedule entries
- Handle case where all schedules deactivated (calls remove instead of install)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each schedule has SCHEDULE_ACTIVE field (yes/no). Table shows active
status with checkmark/cross. Toggle Active button flips state and
reinstalls crontab with only active schedules. Inactive schedules
are skipped during crontab install.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch checks current crontab status on mount. Toggling ON runs
schedule install, toggling OFF runs schedule remove. Show crontab
button remains for inspection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Select a schedule in the table, click Edit to load its config into
the form. The Add button now works as Save (creates or updates).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses rich.spinner.Spinner with set_interval refresh instead of
Textual's LoadingIndicator which caused rendering failures.
Spinner shows dots animation while running, changes to checkmark on finish.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reverts all spinner/loading indicator changes and debug code.
Restores the exact code from commit 0e02ba6 which was confirmed working.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>