diff --git a/bin/gniza b/bin/gniza
index b220e10..77634cf 100755
--- a/bin/gniza
+++ b/bin/gniza
@@ -402,10 +402,10 @@ if [[ "$SUBCOMMAND" == "web" ]]; then
_web_port="" _web_host=""
_web_port=$(_parse_flag "--port" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
_web_host=$(_parse_flag "--host" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
- _web_args=(--web)
- [[ -n "$_web_port" ]] && _web_args+=(--port "$_web_port")
- [[ -n "$_web_host" ]] && _web_args+=(--host "$_web_host")
- PYTHONPATH="$GNIZA_DIR:${PYTHONPATH:-}" exec python3 -m tui "${_web_args[@]}"
+ _web_args=()
+ [[ -n "$_web_port" ]] && _web_args+=(--port="$_web_port")
+ [[ -n "$_web_host" ]] && _web_args+=(--host="$_web_host")
+ PYTHONPATH="$GNIZA_DIR:${PYTHONPATH:-}" exec python3 -m web "${_web_args[@]}"
;;
install-service)
_service_src="$GNIZA_DIR/etc/gniza-web.service"
diff --git a/etc/gniza-web.service b/etc/gniza-web.service
index 5b78bf4..ef2b0bf 100644
--- a/etc/gniza-web.service
+++ b/etc/gniza-web.service
@@ -4,9 +4,11 @@ After=network.target
[Service]
Type=simple
-ExecStart=/usr/bin/python3 -m tui --web --host 0.0.0.0 --port 8080
+ExecStart=/usr/bin/python3 -m web
WorkingDirectory=/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
Restart=on-failure
RestartSec=5
diff --git a/scripts/install.sh b/scripts/install.sh
index e101850..89eb072 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -163,6 +163,21 @@ done
enable_web="n"
read -rp "Enable web dashboard (TUI in browser)? (y/n) [n]: " enable_web /dev/null || \
+ [ -z "$(grep '^WEB_API_KEY=' "$CONFIG_DIR/gniza.conf" 2>/dev/null | sed 's/^WEB_API_KEY="//' | sed 's/"$//')" ]; then
+ api_key="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
+ if grep -q "^WEB_API_KEY=" "$CONFIG_DIR/gniza.conf" 2>/dev/null; then
+ sed -i "s|^WEB_API_KEY=.*|WEB_API_KEY=\"${api_key}\"|" "$CONFIG_DIR/gniza.conf"
+ else
+ echo "WEB_API_KEY=\"${api_key}\"" >> "$CONFIG_DIR/gniza.conf"
+ fi
+ info "Generated Web API key: $api_key"
+ echo "Save this key -- you will need it to access the dashboard."
+ else
+ info "Web API key already configured."
+ fi
+ # Install systemd service
if [ "$MODE" = "root" ]; then
"$INSTALL_DIR/bin/gniza" web install-service || warn "Failed to install web service"
else
diff --git a/tui/models.py b/tui/models.py
index 26a8e76..55284e3 100644
--- a/tui/models.py
+++ b/tui/models.py
@@ -205,6 +205,7 @@ class AppSettings:
work_dir: str = "/usr/local/gniza/workdir"
web_port: str = "8080"
web_host: str = "0.0.0.0"
+ web_api_key: str = ""
@classmethod
def from_conf(cls, data: dict[str, str]) -> "AppSettings":
@@ -228,6 +229,7 @@ class AppSettings:
work_dir=data.get("WORK_DIR", "/usr/local/gniza/workdir"),
web_port=data.get("WEB_PORT", "8080"),
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]:
@@ -251,4 +253,5 @@ class AppSettings:
"WORK_DIR": self.work_dir,
"WEB_PORT": self.web_port,
"WEB_HOST": self.web_host,
+ "WEB_API_KEY": self.web_api_key,
}
diff --git a/tui/screens/settings.py b/tui/screens/settings.py
index a4d8787..3b7af74 100644
--- a/tui/screens/settings.py
+++ b/tui/screens/settings.py
@@ -66,6 +66,8 @@ class SettingsScreen(Screen):
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"):
yield Button("Save", variant="primary", id="btn-save")
yield Button("Back", id="btn-back")
@@ -101,6 +103,7 @@ class SettingsScreen(Screen):
work_dir=self.query_one("#set-workdir", Input).value.strip() or "/usr/local/gniza/workdir",
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_api_key=self.query_one("#set-web-key", Input).value,
)
conf_path = CONFIG_DIR / "gniza.conf"
write_conf(conf_path, settings.to_conf())
diff --git a/web/templates/dashboard.html b/web/templates/dashboard.html
index 50d483c..a0006cb 100644
--- a/web/templates/dashboard.html
+++ b/web/templates/dashboard.html
@@ -5,235 +5,591 @@
GNIZA Dashboard
-
- GNIZA Backup Dashboard
- Logout
-
-
-
-
Targets
- {% if targets %}
-
- | Name | Remote | Status | |
- {% for t in targets %}
-
- | {{ t.name }} |
- {{ t.remote or '-' }} |
-
- {% if t.enabled == 'yes' %}
- enabled
- {% else %}
- disabled
- {% endif %}
- |
-
- {% if t.enabled == 'yes' %}
-
- {% endif %}
- |
-
- {% endfor %}
-
- {% else %}
-
No targets configured.
- {% endif %}
+
+
+
+
+
+
+
+
Targets
+
{{ targets|length }}
+
{{ targets|selectattr('enabled', 'equalto', 'yes')|list|length }} enabled
+
+
+
Remotes
+
{{ remotes|length }}
+
storage destinations
+
+
+
Schedules
+
{{ schedules|length }}
+
{{ schedules|selectattr('active', 'equalto', 'yes')|list|length }} active
+
+
+
Last Backup
+ {% if last_log %}
+
{{ last_log.status|upper }}
+
{{ last_log.name }}
+ {% else %}
+
N/A
+
no logs found
+ {% endif %}
+
-
-
Remotes
- {% if remotes %}
-
- | Name | Type | Host |
- {% for r in remotes %}
-
- | {{ r.name }} |
- {{ r.type }} |
- {{ r.host or r.base }} |
-
- {% endfor %}
-
- {% else %}
-
No remotes configured.
- {% endif %}
+
+
+
+
+
+
+ {% if targets %}
+
+ | Name | Remote | Status | |
+
+ {% for t in targets %}
+
+ | {{ t.name }} |
+ {{ t.remote or '--' }} |
+
+ {% if t.enabled == 'yes' %}
+ Enabled
+ {% else %}
+ Disabled
+ {% endif %}
+ |
+
+ {% if t.enabled == 'yes' %}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+ {% else %}
+
No targets configured
+ {% endif %}
+
+
+
+
+
+
+
+ {% if remotes %}
+
+ | Name | Type | Host / Path |
+
+ {% for r in remotes %}
+
+ | {{ r.name }} |
+ {{ r.type }} |
+ {{ r.host or r.base }} |
+
+ {% endfor %}
+
+
+ {% else %}
+
No remotes configured
+ {% endif %}
+
+
+
+
+
+
+
+ {% if schedules %}
+
+ | Name | Schedule | Time | Status |
+
+ {% for s in schedules %}
+
+ | {{ s.name }} |
+ {{ s.schedule }} |
+ {{ s.time or '--' }} |
+
+ {% if s.active == 'yes' %}
+ Active
+ {% else %}
+ Inactive
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+ {% else %}
+
No schedules configured
+ {% endif %}
+
+
+
+
+
+
+ {% if last_log %}
+
+ {{ last_log.name }}
+ {{ last_log.status|capitalize }}
+
+
{{ last_log.tail }}
+ {% else %}
+
No log files found
+ {% endif %}
+
+
-
-
Schedules
- {% if schedules %}
-
- | Name | Schedule | Time | Status |
- {% for s in schedules %}
-
- | {{ s.name }} |
- {{ s.schedule }} |
- {{ s.time or '-' }} |
-
- {% if s.active == 'yes' %}
- active
- {% else %}
- inactive
- {% endif %}
- |
-
- {% endfor %}
-
- {% else %}
-
No schedules configured.
- {% endif %}
-
-
-
-
Last Backup
- {% if last_log %}
-
- {{ last_log.name }}
- {{ last_log.status }}
-
-
{{ last_log.tail }}
- {% else %}
-
No log files found.
- {% endif %}
-
+