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>
This commit is contained in:
shuki
2026-03-06 06:05:29 +02:00
parent 97918f259e
commit 1a24371446
5 changed files with 9 additions and 49 deletions

View File

@@ -402,10 +402,10 @@ if [[ "$SUBCOMMAND" == "web" ]]; then
_web_port="" _web_host="" _web_port="" _web_host=""
_web_port=$(_parse_flag "--port" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true _web_port=$(_parse_flag "--port" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
_web_host=$(_parse_flag "--host" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true _web_host=$(_parse_flag "--host" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
_web_args=() _web_args=(--web)
[[ -n "$_web_port" ]] && _web_args+=(--port="$_web_port") [[ -n "$_web_port" ]] && _web_args+=(--port "$_web_port")
[[ -n "$_web_host" ]] && _web_args+=(--host="$_web_host") [[ -n "$_web_host" ]] && _web_args+=(--host "$_web_host")
PYTHONPATH="$GNIZA_DIR:${PYTHONPATH:-}" exec python3 -m web "${_web_args[@]}" PYTHONPATH="$GNIZA_DIR:${PYTHONPATH:-}" exec python3 -m tui "${_web_args[@]}"
;; ;;
install-service) install-service)
_service_src="$GNIZA_DIR/etc/gniza-web.service" _service_src="$GNIZA_DIR/etc/gniza-web.service"
@@ -416,7 +416,7 @@ if [[ "$SUBCOMMAND" == "web" ]]; then
cp "$_service_src" "$_service_dst" cp "$_service_src" "$_service_dst"
systemctl daemon-reload systemctl daemon-reload
systemctl enable gniza-web systemctl enable gniza-web
systemctl start gniza-web systemctl restart gniza-web
echo "GNIZA web service installed and started." echo "GNIZA web service installed and started."
echo "Access the dashboard at http://$(hostname -I | awk '{print $1}'):8080" echo "Access the dashboard at http://$(hostname -I | awk '{print $1}'):8080"
;; ;;

View File

@@ -4,11 +4,9 @@ After=network.target
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/python3 -m web ExecStart=/usr/bin/python3 -m tui --web --host 0.0.0.0 --port 8080
WorkingDirectory=/usr/local/gniza WorkingDirectory=/usr/local/gniza
Environment=GNIZA_DIR=/usr/local/gniza Environment=GNIZA_DIR=/usr/local/gniza
Environment=GNIZA_CONFIG_DIR=/etc/gniza
Environment=LOG_DIR=/var/log/gniza
Environment=PYTHONPATH=/usr/local/gniza Environment=PYTHONPATH=/usr/local/gniza
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5

View File

@@ -160,36 +160,14 @@ for example in target.conf.example remote.conf.example schedule.conf.example; do
done done
# -- Web dashboard setup -- # -- Web dashboard setup --
setup_web_dashboard() { enable_web="n"
local conf="$CONFIG_DIR/gniza.conf" read -rp "Enable web dashboard (TUI in browser)? (y/n) [n]: " enable_web </dev/tty || true
# Update config: WEB_ENABLED if [ "$enable_web" = "y" ] || [ "$enable_web" = "Y" ]; then
if grep -q "^WEB_ENABLED=" "$conf" 2>/dev/null; then
sed -i 's|^WEB_ENABLED=.*|WEB_ENABLED="yes"|' "$conf"
else
echo 'WEB_ENABLED="yes"' >> "$conf"
fi
# Generate random API key
local api_key
api_key="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
if grep -q "^WEB_API_KEY=" "$conf" 2>/dev/null; then
sed -i "s|^WEB_API_KEY=.*|WEB_API_KEY=\"${api_key}\"|" "$conf"
else
echo "WEB_API_KEY=\"${api_key}\"" >> "$conf"
fi
info "Web API key: $api_key"
echo "Save this key -- you will need it to log into the dashboard."
# Install systemd service (root only)
if [ "$MODE" = "root" ]; then if [ "$MODE" = "root" ]; then
"$INSTALL_DIR/bin/gniza" web install-service || warn "Failed to install web service" "$INSTALL_DIR/bin/gniza" web install-service || warn "Failed to install web service"
else else
warn "Systemd service installation requires root. Start manually: gniza web start" warn "Systemd service installation requires root. Start manually: gniza web start"
fi fi
}
enable_web="n"
read -rp "Enable web dashboard? (y/n) [n]: " enable_web </dev/tty || true
if [ "$enable_web" = "y" ] || [ "$enable_web" = "Y" ]; then
setup_web_dashboard
fi fi
# -- Done ----------------------------------------------------- # -- Done -----------------------------------------------------

View File

@@ -203,10 +203,8 @@ class AppSettings:
ssh_retries: str = "3" ssh_retries: str = "3"
rsync_extra_opts: str = "" rsync_extra_opts: str = ""
work_dir: str = "/usr/local/gniza/workdir" work_dir: str = "/usr/local/gniza/workdir"
web_enabled: str = "no"
web_port: str = "8080" web_port: str = "8080"
web_host: str = "0.0.0.0" web_host: str = "0.0.0.0"
web_api_key: str = ""
@classmethod @classmethod
def from_conf(cls, data: dict[str, str]) -> "AppSettings": def from_conf(cls, data: dict[str, str]) -> "AppSettings":
@@ -228,10 +226,8 @@ class AppSettings:
ssh_retries=data.get("SSH_RETRIES", "3"), ssh_retries=data.get("SSH_RETRIES", "3"),
rsync_extra_opts=data.get("RSYNC_EXTRA_OPTS", ""), rsync_extra_opts=data.get("RSYNC_EXTRA_OPTS", ""),
work_dir=data.get("WORK_DIR", "/usr/local/gniza/workdir"), work_dir=data.get("WORK_DIR", "/usr/local/gniza/workdir"),
web_enabled=data.get("WEB_ENABLED", "no"),
web_port=data.get("WEB_PORT", "8080"), web_port=data.get("WEB_PORT", "8080"),
web_host=data.get("WEB_HOST", "0.0.0.0"), web_host=data.get("WEB_HOST", "0.0.0.0"),
web_api_key=data.get("WEB_API_KEY", ""),
) )
def to_conf(self) -> dict[str, str]: def to_conf(self) -> dict[str, str]:
@@ -253,8 +249,6 @@ class AppSettings:
"SSH_RETRIES": self.ssh_retries, "SSH_RETRIES": self.ssh_retries,
"RSYNC_EXTRA_OPTS": self.rsync_extra_opts, "RSYNC_EXTRA_OPTS": self.rsync_extra_opts,
"WORK_DIR": self.work_dir, "WORK_DIR": self.work_dir,
"WEB_ENABLED": self.web_enabled,
"WEB_PORT": self.web_port, "WEB_PORT": self.web_port,
"WEB_HOST": self.web_host, "WEB_HOST": self.web_host,
"WEB_API_KEY": self.web_api_key,
} }

View File

@@ -62,18 +62,10 @@ class SettingsScreen(Screen):
yield Static("Work Directory:") yield Static("Work Directory:")
yield Input(value=settings.work_dir, placeholder="/usr/local/gniza/workdir", id="set-workdir") yield Input(value=settings.work_dir, placeholder="/usr/local/gniza/workdir", id="set-workdir")
yield Static("Web Dashboard", classes="section-label") yield Static("Web Dashboard", classes="section-label")
yield Static("Enabled:")
yield Select(
[("Yes", "yes"), ("No", "no")],
id="set-web-enabled",
value=settings.web_enabled,
)
yield Static("Port:") yield Static("Port:")
yield Input(value=settings.web_port, id="set-web-port") yield Input(value=settings.web_port, id="set-web-port")
yield Static("Host:") yield Static("Host:")
yield Input(value=settings.web_host, id="set-web-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("Back", id="btn-back") yield Button("Back", id="btn-back")
@@ -107,10 +99,8 @@ class SettingsScreen(Screen):
ssh_retries=self.query_one("#set-sshretries", Input).value.strip() or "3", ssh_retries=self.query_one("#set-sshretries", Input).value.strip() or "3",
rsync_extra_opts=self.query_one("#set-rsyncopts", Input).value.strip(), rsync_extra_opts=self.query_one("#set-rsyncopts", Input).value.strip(),
work_dir=self.query_one("#set-workdir", Input).value.strip() or "/usr/local/gniza/workdir", work_dir=self.query_one("#set-workdir", Input).value.strip() or "/usr/local/gniza/workdir",
web_enabled=self._get_select_val("#set-web-enabled", "no"),
web_port=self.query_one("#set-web-port", Input).value.strip() or "8080", web_port=self.query_one("#set-web-port", Input).value.strip() or "8080",
web_host=self.query_one("#set-web-host", Input).value.strip() or "0.0.0.0", web_host=self.query_one("#set-web-host", Input).value.strip() or "0.0.0.0",
web_api_key=self.query_one("#set-web-key", Input).value,
) )
conf_path = CONFIG_DIR / "gniza.conf" conf_path = CONFIG_DIR / "gniza.conf"
write_conf(conf_path, settings.to_conf()) write_conf(conf_path, settings.to_conf())