Commit Graph

79 Commits

Author SHA1 Message Date
shuki
63cc7f842e Fix security and correctness bugs found in code review
- Add shquote() to escape single quotes in paths passed to remote_exec,
  preventing shell injection via REMOTE_BASE containing single quotes
- Apply shquote to remote_exec calls in remotes.sh, backup.sh, transfer.sh, ssh.sh
- Add DISK_USAGE_THRESHOLD validation in config.sh
- Export SMTP_PASSWORD (was missing from export list)
- Fix WEB_PORT default mismatch: use 2323 consistently in from_conf and settings save
- Narrow exception catch in remotes.py disk info fetch to KeyError/LookupError
- Quote REMOTE_KEY in build_rsync_ssh_cmd for paths with spaces

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 08:13:20 +02:00
shuki
590b87843d Add configurable disk usage threshold setting
Disk usage threshold (default 95%) can now be controlled from
Settings. Set to 0 to disable the check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 07:08:42 +02:00
shuki
993a66d8c6 Add include/exclude filter support and disk space pre-check
- Add TARGET_INCLUDE field for rsync include patterns (comma-separated)
- Pass TARGET_INCLUDE and TARGET_EXCLUDE to rsync in transfer_folder
- Include mode uses --include='*/' + patterns + --exclude='*' + --prune-empty-dirs
- Abort backup if remote disk usage >= 95%

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 07:04:52 +02:00
shuki
eb9dda416b Remove Speed Test and Disk Info buttons from remotes screen
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>
2026-03-06 06:47:05 +02:00
shuki
8b3d6ede4f Show disk usage in remotes table with async loading
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 06:44:27 +02:00
shuki
c70fb1991c Add Disk Info and Speed Test buttons to Remotes screen
- 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>
2026-03-06 06:38:15 +02:00
shuki
f7c5836db0 Show system clock in top right corner of all screens
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 06:35:51 +02:00
shuki
5c1fa04657 Add Last Run and Next Run columns to Schedules screen
- 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>
2026-03-06 06:31:49 +02:00
shuki
220d30e515 Change default web port from 8080 to 2323
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 06:27:16 +02:00
shuki
ebe5645fb9 Fix auth middleware: use insert(0) on FrozenList before app starts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 06:18:05 +02:00
shuki
1800babbc2 Add HTTP Basic Auth to textual-serve web dashboard
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>
2026-03-06 06:14:51 +02:00
shuki
fa7eb14369 Fix textual-serve web: robust IP detection for WebSocket URL
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>
2026-03-06 06:12:29 +02:00
shuki
133ae1e7a4 Restore Flask dashboard with redesigned UI and API key auth
- 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>
2026-03-06 06:09:39 +02:00
shuki
1a24371446 Replace Flask web dashboard with textual-serve
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>
2026-03-06 06:05:29 +02:00
shuki
cf00ecdd4b Add web dashboard with systemd service support
- 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>
2026-03-06 05:34:02 +02:00
shuki
0d5977ab22 Rename Snapshots to Snapshots Browser in main menu
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:15:37 +02:00
shuki
d32f98a8c1 Rename screen title to Snapshots Browser
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:14:16 +02:00
shuki
011a2cee8c Start snapshot browser tree collapsed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:13:04 +02:00
shuki
5b4bf33520 Add tree-based file browser for snapshot contents
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>
2026-03-06 05:11:53 +02:00
shuki
98fc263a40 Fix snapshot selection using row key instead of get_cell
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:09:49 +02:00
shuki
e73f38df53 Add Browse Files button to snapshots screen
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>
2026-03-06 05:08:45 +02:00
shuki
d6032d2547 Remove Retention from main menu
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:06:36 +02:00
shuki
152ef61734 Fix menu separator for older Textual versions
Use None separator instead of Separator class which isn't available
in older Textual.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:04:30 +02:00
shuki
3ac1d77e47 Add separator after Restore in main menu
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:03:53 +02:00
shuki
eb88077e42 Center GNIZA text under logo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:02:11 +02:00
shuki
b0465c0877 Uppercase GNIZA in logo and app title
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:01:28 +02:00
shuki
c5f6e8be5c Reorder menu: move Snapshots after Schedules, before Logs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:00:51 +02:00
shuki
68b348e1b7 Fix MySQL restore switch visibility with explicit sizing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:57:26 +02:00
shuki
eeee87b063 Add 'Restore MySQL' toggle switch to restore screen
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>
2026-03-06 04:56:30 +02:00
shuki
75fd25e559 Show spinner only for backup and restore operations
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>
2026-03-06 04:49:22 +02:00
shuki
fa891bbf7b Sync crontab after adding or editing a schedule
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:47:16 +02:00
shuki
07bc23a7a1 Stop spinner after show crontab completes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:46:16 +02:00
shuki
48bd0ab1d4 Move schedule form to separate edit screen and fix crontab sync
- 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>
2026-03-06 04:45:27 +02:00
shuki
1425c416eb Add per-schedule active toggle with crontab sync
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>
2026-03-06 04:38:51 +02:00
shuki
a8d67160a5 Replace Install/Remove crontab buttons with Active toggle switch
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>
2026-03-06 04:35:59 +02:00
shuki
8fc0e5a04d Add Edit button to schedule screen
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>
2026-03-06 04:34:20 +02:00
shuki
691e268dd0 Use arrow3 spinner with 'Running...' text for better visibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:31:12 +02:00
shuki
e32e226c10 Make spinner same height as Close button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:30:06 +02:00
shuki
255d43fcea Move spinner next to Close button in OperationLog footer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:29:25 +02:00
shuki
d596a747a4 Add animated spinner to OperationLog using Rich Spinner
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>
2026-03-06 04:28:08 +02:00
shuki
ced2e9a889 Revert OperationLog and all screens to pre-spinner working state
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>
2026-03-06 04:25:57 +02:00
shuki
75259c9d34 DEBUG: minimal OperationLog - no RichLog, just Static + Button 2026-03-06 04:23:54 +02:00
shuki
9d51a39282 DEBUG: file-based logging to /tmp/gniza_debug.log 2026-03-06 04:22:38 +02:00
shuki
bc2cb8c4ba DEBUG: skip ConfirmDialog, go straight to OperationLog 2026-03-06 04:19:13 +02:00
shuki
c348bb2289 Add debug to ConfirmDialog button handler 2026-03-06 04:17:47 +02:00
shuki
47c93d1208 Add debug notifications to backup flow 2026-03-06 04:13:45 +02:00
shuki
4d4b55047b Fix OperationLog not rendering by removing LoadingIndicator
LoadingIndicator was causing OperationLog ModalScreen to fail silently
during compose. Replaced with a simple Static emoji spinner ().
Reverted screen push patterns back to simple callback approach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:11:16 +02:00
shuki
f70ce53dc5 Fix operation log not showing by awaiting screen mount before CLI
Add wait_ready() to OperationLog and await it in all callers before
starting CLI commands. Ensures the modal with spinner is fully rendered
before streaming begins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 03:25:25 +02:00
shuki
f9981831fa Add loading spinner to operation log during running tasks
Shows an animated spinner next to the title while backup, restore,
retention, remote test, and schedule operations are running. Spinner
hides when the operation completes via finish().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 03:19:57 +02:00
shuki
0e02ba6876 Hide time field for hourly schedules
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 03:13:46 +02:00