Add tabbed settings screen and Send Test Email button
Organize settings into General, Email, SSH, and Web Dashboard tabs using TabbedContent. Add test-email CLI command and button that auto-saves settings then sends a test email via SMTP. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
23
bin/gniza
23
bin/gniza
@@ -594,6 +594,29 @@ run_cli() {
|
|||||||
exit "$rc"
|
exit "$rc"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
test-email)
|
||||||
|
if [[ -z "${NOTIFY_EMAIL:-}" ]]; then
|
||||||
|
echo "Error: NOTIFY_EMAIL is not set. Configure it in Settings first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "${SMTP_HOST:-}" ]]; then
|
||||||
|
echo "Error: SMTP_HOST is not set. Configure SMTP settings first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
local hostname; hostname=$(hostname -f 2>/dev/null || hostname)
|
||||||
|
local subject="[gniza] [$hostname] Test Email"
|
||||||
|
local body="This is a test email from gniza on $hostname."$'\n'
|
||||||
|
body+="Sent at: $(date '+%Y-%m-%d %H:%M:%S')"$'\n'
|
||||||
|
body+=""$'\n'
|
||||||
|
body+="If you received this message, your SMTP settings are working correctly."
|
||||||
|
if _send_via_smtp "$subject" "$body"; then
|
||||||
|
echo "Test email sent successfully to $NOTIFY_EMAIL"
|
||||||
|
else
|
||||||
|
echo "Failed to send test email. Check your SMTP settings."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
version)
|
version)
|
||||||
echo "gniza v${GNIZA4LINUX_VERSION}"
|
echo "gniza v${GNIZA4LINUX_VERSION}"
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.widgets import Header, Footer, Static, Button, Input, Select
|
from textual.widgets import Header, Footer, Static, Button, Input, Select, TabbedContent, TabPane
|
||||||
from tui.widgets.header import GnizaHeader as Header # noqa: F811
|
from tui.widgets.header import GnizaHeader as Header # noqa: F811
|
||||||
from textual.containers import Vertical, Horizontal
|
from textual.containers import Vertical, Horizontal
|
||||||
|
from textual import work
|
||||||
|
|
||||||
from tui.config import parse_conf, write_conf, CONFIG_DIR
|
from tui.config import parse_conf, write_conf, CONFIG_DIR
|
||||||
from tui.models import AppSettings
|
from tui.models import AppSettings
|
||||||
from tui.widgets import DocsPanel
|
from tui.backend import run_cli
|
||||||
|
from tui.widgets import DocsPanel, OperationLog
|
||||||
|
|
||||||
|
|
||||||
class SettingsScreen(Screen):
|
class SettingsScreen(Screen):
|
||||||
@@ -20,59 +22,64 @@ class SettingsScreen(Screen):
|
|||||||
with Horizontal(classes="screen-with-docs"):
|
with Horizontal(classes="screen-with-docs"):
|
||||||
with Vertical(id="settings-screen"):
|
with Vertical(id="settings-screen"):
|
||||||
yield Static("Settings", id="screen-title")
|
yield Static("Settings", id="screen-title")
|
||||||
yield Static("Log Level:")
|
with TabbedContent():
|
||||||
yield Select(
|
with TabPane("General", id="tab-general"):
|
||||||
[("Debug", "debug"), ("Info", "info"), ("Warning", "warn"), ("Error", "error")],
|
yield Static("Log Level:")
|
||||||
id="set-loglevel",
|
yield Select(
|
||||||
value=settings.log_level.lower(),
|
[("Debug", "debug"), ("Info", "info"), ("Warning", "warn"), ("Error", "error")],
|
||||||
)
|
id="set-loglevel",
|
||||||
yield Static("Log Retention (days):")
|
value=settings.log_level.lower(),
|
||||||
yield Input(value=settings.log_retain, id="set-logretain")
|
)
|
||||||
yield Static("Default Retention Count:")
|
yield Static("Log Retention (days):")
|
||||||
yield Input(value=settings.retention_count, id="set-retention")
|
yield Input(value=settings.log_retain, id="set-logretain")
|
||||||
yield Static("Default Bandwidth Limit (KB/s, 0=unlimited):")
|
yield Static("Default Retention Count:")
|
||||||
yield Input(value=settings.bwlimit, id="set-bwlimit")
|
yield Input(value=settings.retention_count, id="set-retention")
|
||||||
yield Static("Disk Usage Threshold (%, 0=disable):")
|
yield Static("Default Bandwidth Limit (KB/s, 0=unlimited):")
|
||||||
yield Input(value=settings.disk_usage_threshold, id="set-diskthreshold")
|
yield Input(value=settings.bwlimit, id="set-bwlimit")
|
||||||
yield Static("Notification Email:")
|
yield Static("Disk Usage Threshold (%, 0=disable):")
|
||||||
yield Input(value=settings.notify_email, id="set-email")
|
yield Input(value=settings.disk_usage_threshold, id="set-diskthreshold")
|
||||||
yield Static("Notify On:")
|
yield Static("Extra rsync options:")
|
||||||
yield Select(
|
yield Input(value=settings.rsync_extra_opts, id="set-rsyncopts")
|
||||||
[("Always", "always"), ("Failure only", "failure"), ("Never", "never")],
|
with TabPane("Email", id="tab-email"):
|
||||||
id="set-notifyon",
|
yield Static("Notification Email:")
|
||||||
value=settings.notify_on,
|
yield Input(value=settings.notify_email, id="set-email")
|
||||||
)
|
yield Static("Notify On:")
|
||||||
yield Static("SMTP Host:")
|
yield Select(
|
||||||
yield Input(value=settings.smtp_host, id="set-smtphost")
|
[("Always", "always"), ("Failure only", "failure"), ("Never", "never")],
|
||||||
yield Static("SMTP Port:")
|
id="set-notifyon",
|
||||||
yield Input(value=settings.smtp_port, id="set-smtpport")
|
value=settings.notify_on,
|
||||||
yield Static("SMTP User:")
|
)
|
||||||
yield Input(value=settings.smtp_user, id="set-smtpuser")
|
yield Static("SMTP Host:")
|
||||||
yield Static("SMTP Password:")
|
yield Input(value=settings.smtp_host, id="set-smtphost")
|
||||||
yield Input(value=settings.smtp_password, password=True, id="set-smtppass")
|
yield Static("SMTP Port:")
|
||||||
yield Static("SMTP From:")
|
yield Input(value=settings.smtp_port, id="set-smtpport")
|
||||||
yield Input(value=settings.smtp_from, id="set-smtpfrom")
|
yield Static("SMTP User:")
|
||||||
yield Static("SMTP Security:")
|
yield Input(value=settings.smtp_user, id="set-smtpuser")
|
||||||
yield Select(
|
yield Static("SMTP Password:")
|
||||||
[("TLS", "tls"), ("SSL", "ssl"), ("None", "none")],
|
yield Input(value=settings.smtp_password, password=True, id="set-smtppass")
|
||||||
id="set-smtpsec",
|
yield Static("SMTP From:")
|
||||||
value=settings.smtp_security,
|
yield Input(value=settings.smtp_from, id="set-smtpfrom")
|
||||||
)
|
yield Static("SMTP Security:")
|
||||||
yield Static("SSH Timeout:")
|
yield Select(
|
||||||
yield Input(value=settings.ssh_timeout, id="set-sshtimeout")
|
[("TLS", "tls"), ("SSL", "ssl"), ("None", "none")],
|
||||||
yield Static("SSH Retries:")
|
id="set-smtpsec",
|
||||||
yield Input(value=settings.ssh_retries, id="set-sshretries")
|
value=settings.smtp_security,
|
||||||
yield Static("Extra rsync options:")
|
)
|
||||||
yield Input(value=settings.rsync_extra_opts, id="set-rsyncopts")
|
with TabPane("SSH", id="tab-ssh"):
|
||||||
yield Static("Web Dashboard", classes="section-label")
|
yield Static("SSH Timeout:")
|
||||||
yield Static("Port:")
|
yield Input(value=settings.ssh_timeout, id="set-sshtimeout")
|
||||||
yield Input(value=settings.web_port, id="set-web-port")
|
yield Static("SSH Retries:")
|
||||||
yield Static("Host:")
|
yield Input(value=settings.ssh_retries, id="set-sshretries")
|
||||||
yield Input(value=settings.web_host, id="set-web-host")
|
with TabPane("Web Dashboard", id="tab-web"):
|
||||||
yield Static("API Key:")
|
yield Static("Port:")
|
||||||
yield Input(value=settings.web_api_key, password=True, id="set-web-key")
|
yield Input(value=settings.web_port, id="set-web-port")
|
||||||
|
yield Static("Host:")
|
||||||
|
yield Input(value=settings.web_host, id="set-web-host")
|
||||||
|
yield Static("API Key:")
|
||||||
|
yield Input(value=settings.web_api_key, password=True, id="set-web-key")
|
||||||
with Horizontal(id="set-buttons"):
|
with Horizontal(id="set-buttons"):
|
||||||
yield Button("Save", variant="primary", id="btn-save")
|
yield Button("Save", variant="primary", id="btn-save")
|
||||||
|
yield Button("Send Test Email", id="btn-test-email")
|
||||||
yield Button("Back", id="btn-back")
|
yield Button("Back", id="btn-back")
|
||||||
yield DocsPanel.for_screen("settings-screen")
|
yield DocsPanel.for_screen("settings-screen")
|
||||||
yield Footer()
|
yield Footer()
|
||||||
@@ -82,6 +89,9 @@ class SettingsScreen(Screen):
|
|||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
elif event.button.id == "btn-save":
|
elif event.button.id == "btn-save":
|
||||||
self._save()
|
self._save()
|
||||||
|
elif event.button.id == "btn-test-email":
|
||||||
|
self._save()
|
||||||
|
self._send_test_email()
|
||||||
|
|
||||||
def _get_select_val(self, sel_id: str, default: str) -> str:
|
def _get_select_val(self, sel_id: str, default: str) -> str:
|
||||||
sel = self.query_one(sel_id, Select)
|
sel = self.query_one(sel_id, Select)
|
||||||
@@ -113,5 +123,17 @@ class SettingsScreen(Screen):
|
|||||||
write_conf(conf_path, settings.to_conf())
|
write_conf(conf_path, settings.to_conf())
|
||||||
self.notify("Settings saved.")
|
self.notify("Settings saved.")
|
||||||
|
|
||||||
|
@work
|
||||||
|
async def _send_test_email(self) -> None:
|
||||||
|
log_screen = OperationLog("Send Test Email", show_spinner=True)
|
||||||
|
self.app.push_screen(log_screen)
|
||||||
|
log_screen.write("Sending test email...")
|
||||||
|
rc, stdout, stderr = await run_cli("test-email")
|
||||||
|
if stdout:
|
||||||
|
log_screen.write(stdout)
|
||||||
|
if stderr:
|
||||||
|
log_screen.write(stderr)
|
||||||
|
log_screen.finish()
|
||||||
|
|
||||||
def action_go_back(self) -> None:
|
def action_go_back(self) -> None:
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|||||||
Reference in New Issue
Block a user