99085adc76
- Flask backend with 23 API routes (entertainment, telegram, docker, system) - Alpine.js + Tailwind CSS dark-mode SPA with 4 tabs - pos-dashboard CLI tool with port/config management - Mobile slide-in drawer with swipe-to-close - Sticky header stays pinned on scroll - Tab completion fixes for category-less tools - POST /api/telegram/commands endpoint for adding commands
276 lines
10 KiB
Bash
Executable File
276 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# POS: system dashboard — Web dashboard: start/stop/status/config/url
|
|
# POS_FLAGS: --help
|
|
# POS_SUBCMDS: start stop status port config url
|
|
# POS_CONFIG: dashboard | dashboard.env | DASHBOARD_PORT=num:Port the dashboard listens on (default 8080) | DASHBOARD_HOST=:Bind address (default 0.0.0.0)
|
|
|
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
|
|
|
# ── Config & constants ──────────────────────────────────────────
|
|
CONFIG_DIR="${CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/linux_post_install}"
|
|
CONFIG_FILE="$CONFIG_DIR/dashboard.env"
|
|
SERVICE="pos-dashboard.service"
|
|
USER_SYSTEMD_DIR="${USER_SYSTEMD_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user}"
|
|
DASHBOARD_DIR="/usr/local/share/linux_post_install/dashboard"
|
|
DASHBOARD_SCRIPT="$DASHBOARD_DIR/app.py"
|
|
|
|
# ── load config ─────────────────────────────────────────────────
|
|
load_config() {
|
|
[ -f "$CONFIG_FILE" ] || return 0
|
|
local k v
|
|
while IFS='=' read -r k v; do
|
|
[ -n "$k" ] || continue
|
|
case "$k" in
|
|
\#*) continue ;;
|
|
esac
|
|
v="${v%\"}"; v="${v#\"}"; v="${v%\'}"; v="${v#\'}"
|
|
if [ -z "${!k:-}" ]; then
|
|
export "$k"="$v"
|
|
fi
|
|
done < <(grep -E '^[A-Z_]+=' "$CONFIG_FILE" || true)
|
|
}
|
|
|
|
# ── defaults ────────────────────────────────────────────────────
|
|
load_config
|
|
DASHBOARD_PORT="${DASHBOARD_PORT:-8080}"
|
|
DASHBOARD_HOST="${DASHBOARD_HOST:-0.0.0.0}"
|
|
|
|
# ── usage ───────────────────────────────────────────────────────
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: pos dashboard <command>
|
|
|
|
Web dashboard for the pos toolkit — browser-based UI for managing
|
|
entertainment, Telegram, Docker, and system health.
|
|
|
|
Commands:
|
|
start Install and start the systemd user service
|
|
stop Stop, disable, and remove the service
|
|
status Show service state and the dashboard URL
|
|
port <num> Change the dashboard port (restarts the service)
|
|
config Edit DASHBOARD_PORT, DASHBOARD_HOST
|
|
url Print the dashboard URL
|
|
|
|
Config: $CONFIG_FILE (DASHBOARD_PORT, DASHBOARD_HOST)
|
|
Service: $SERVICE (systemd user unit)
|
|
Dir: $DASHBOARD_DIR
|
|
|
|
The dashboard binds to DASHBOARD_HOST:DASHBOARD_PORT (default 0.0.0.0:8080).
|
|
Access it over Tailscale at: http://<tailscale-ip>:<port>/
|
|
|
|
Examples:
|
|
pos dashboard start
|
|
pos dashboard port 9090
|
|
pos dashboard status
|
|
pos dashboard url
|
|
pos dashboard config
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# ── deps guard (before --help, per convention) ──────────────────
|
|
command -v python3 &>/dev/null || err "python3 not found — install with: sudo apt install python3"
|
|
|
|
# ── help ────────────────────────────────────────────────────────
|
|
case "${1:-}" in
|
|
-h|--help) usage ;;
|
|
esac
|
|
|
|
# ── start ───────────────────────────────────────────────────────
|
|
cmd_start() {
|
|
# Check flask is available
|
|
if ! python3 -c "import flask" 2>/dev/null; then
|
|
err "python3-flask not installed — install with: sudo apt install python3-flask"
|
|
fi
|
|
|
|
# Check dashboard directory exists
|
|
if [ ! -d "$DASHBOARD_DIR" ]; then
|
|
err "Dashboard directory not found: $DASHBOARD_DIR — run install.sh first"
|
|
fi
|
|
if [ ! -f "$DASHBOARD_SCRIPT" ]; then
|
|
err "Dashboard app not found: $DASHBOARD_SCRIPT — run install.sh first"
|
|
fi
|
|
|
|
# Ensure config exists
|
|
if [ ! -f "$CONFIG_FILE" ]; then
|
|
mkdir -p "$CONFIG_DIR"
|
|
cp "${BASH_SOURCE[0]%/bin/pos-dashboard}/../config/dashboard.env" "$CONFIG_FILE" 2>/dev/null \
|
|
|| warn "No config template found — create $CONFIG_FILE manually"
|
|
chmod 600 "$CONFIG_FILE" 2>/dev/null || true
|
|
fi
|
|
|
|
# Install systemd user service
|
|
mkdir -p "$USER_SYSTEMD_DIR"
|
|
|
|
local runner
|
|
if [ -x /usr/local/bin/pos-dashboard ]; then
|
|
runner=/usr/local/bin/pos-dashboard
|
|
else
|
|
runner="$(cd "$(dirname "$0")/.." && pwd)/bin/pos-dashboard"
|
|
warn "using repo path $runner — re-run 'install.sh' so the service survives a deleted repo"
|
|
fi
|
|
|
|
cat >"$USER_SYSTEMD_DIR/$SERVICE" <<EOF
|
|
[Unit]
|
|
Description=pos Web Dashboard
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/bin/python3 $DASHBOARD_SCRIPT
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
EnvironmentFile=%h/.config/linux_post_install/dashboard.env
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
EOF
|
|
chmod 644 "$USER_SYSTEMD_DIR/$SERVICE"
|
|
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now "$SERVICE"
|
|
log "Dashboard service enabled: $SERVICE"
|
|
|
|
cmd_url
|
|
|
|
if command -v loginctl >/dev/null 2>&1; then
|
|
if ! loginctl show-user "$(id -un)" 2>/dev/null | grep -q '^Linger=yes'; then
|
|
warn "enable linger so the dashboard survives logout: sudo loginctl enable-linger $(id -un)"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── stop ────────────────────────────────────────────────────────
|
|
cmd_stop() {
|
|
if [ ! -f "$USER_SYSTEMD_DIR/$SERVICE" ]; then
|
|
warn "no dashboard service installed ($SERVICE)"
|
|
return 0
|
|
fi
|
|
systemctl --user disable --now "$SERVICE" 2>/dev/null || true
|
|
rm -f "$USER_SYSTEMD_DIR/$SERVICE"
|
|
systemctl --user daemon-reload
|
|
log "Dashboard service disabled"
|
|
}
|
|
|
|
# ── status ──────────────────────────────────────────────────────
|
|
cmd_status() {
|
|
load_config
|
|
DASHBOARD_PORT="${DASHBOARD_PORT:-8080}"
|
|
DASHBOARD_HOST="${DASHBOARD_HOST:-0.0.0.0}"
|
|
if systemctl --user is-active --quiet "$SERVICE" 2>/dev/null; then
|
|
echo "service: running"
|
|
else
|
|
echo "service: not running"
|
|
fi
|
|
if systemctl --user is-enabled "$SERVICE" >/dev/null 2>&1; then
|
|
echo "autostart: enabled (starts on login)"
|
|
else
|
|
echo "autostart: disabled"
|
|
fi
|
|
echo "config: $CONFIG_FILE"
|
|
echo "dir: $DASHBOARD_DIR"
|
|
cmd_url
|
|
}
|
|
|
|
# ── port ────────────────────────────────────────────────────────
|
|
cmd_port() {
|
|
local new_port="${1:-}"
|
|
if [ -z "$new_port" ]; then
|
|
err "Usage: pos dashboard port <number>"
|
|
fi
|
|
# Validate port is a number between 1 and 65535
|
|
if ! [[ "$new_port" =~ ^[0-9]+$ ]] || [ "$new_port" -lt 1 ] || [ "$new_port" -gt 65535 ]; then
|
|
err "Invalid port: $new_port — must be a number between 1 and 65535"
|
|
fi
|
|
|
|
# Ensure config file exists
|
|
if [ ! -f "$CONFIG_FILE" ]; then
|
|
mkdir -p "$CONFIG_DIR"
|
|
cp "${BASH_SOURCE[0]%/bin/pos-dashboard}/../config/dashboard.env" "$CONFIG_FILE" 2>/dev/null \
|
|
|| echo -e "DASHBOARD_PORT=8080\nDASHBOARD_HOST=0.0.0.0" > "$CONFIG_FILE"
|
|
chmod 600 "$CONFIG_FILE"
|
|
fi
|
|
|
|
# Update port in config file
|
|
if grep -q '^DASHBOARD_PORT=' "$CONFIG_FILE"; then
|
|
sed -i "s/^DASHBOARD_PORT=.*/DASHBOARD_PORT=$new_port/" "$CONFIG_FILE"
|
|
else
|
|
echo "DASHBOARD_PORT=$new_port" >> "$CONFIG_FILE"
|
|
fi
|
|
log "Port changed to $new_port in $CONFIG_FILE"
|
|
|
|
# Update the running variable so cmd_url shows the new port
|
|
DASHBOARD_PORT="$new_port"
|
|
|
|
# Restart service if running
|
|
if systemctl --user is-active --quiet "$SERVICE" 2>/dev/null; then
|
|
systemctl --user restart "$SERVICE"
|
|
log "Dashboard service restarted on port $new_port"
|
|
else
|
|
warn "Dashboard is not running — start it with: pos dashboard start"
|
|
fi
|
|
|
|
cmd_url
|
|
}
|
|
|
|
# ── config ──────────────────────────────────────────────────────
|
|
cmd_config() {
|
|
# If pos config-ui.sh is available, use the interactive editor
|
|
local cfg_ui_script
|
|
for p in \
|
|
"$(dirname "$0")/../lib/config-ui.sh" \
|
|
"$(dirname "$0")/config-ui.sh" \
|
|
"/usr/local/bin/config-ui.sh"; do
|
|
if [ -f "$p" ]; then
|
|
cfg_ui_script="$p"
|
|
break
|
|
fi
|
|
done
|
|
if [ -n "${cfg_ui_script:-}" ]; then
|
|
source "$cfg_ui_script" 2>/dev/null
|
|
cfg_ui "dashboard"
|
|
return
|
|
fi
|
|
|
|
# Fallback: reload and show current values
|
|
load_config
|
|
DASHBOARD_PORT="${DASHBOARD_PORT:-8080}"
|
|
DASHBOARD_HOST="${DASHBOARD_HOST:-0.0.0.0}"
|
|
echo "Dashboard config: $CONFIG_FILE"
|
|
echo
|
|
printf ' DASHBOARD_PORT=%s\n' "$DASHBOARD_PORT"
|
|
printf ' DASHBOARD_HOST=%s\n' "$DASHBOARD_HOST"
|
|
echo
|
|
echo "Edit with: pos config dashboard"
|
|
}
|
|
|
|
# ── url ─────────────────────────────────────────────────────────
|
|
cmd_url() {
|
|
local ip=""
|
|
# Prefer Tailscale IP
|
|
if command -v tailscale &>/dev/null; then
|
|
ip="$(tailscale ip -4 2>/dev/null || true)"
|
|
fi
|
|
# Fallback to first non-loopback IP
|
|
if [ -z "$ip" ]; then
|
|
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
fi
|
|
if [ -z "$ip" ]; then
|
|
ip="localhost"
|
|
fi
|
|
echo "http://${ip}:${DASHBOARD_PORT}/"
|
|
}
|
|
|
|
# ── dispatch ────────────────────────────────────────────────────
|
|
case "${1:-}" in
|
|
start) cmd_start ;;
|
|
stop) cmd_stop ;;
|
|
status) cmd_status ;;
|
|
port) cmd_port "${2:-}" ;;
|
|
config) cmd_config ;;
|
|
url) cmd_url ;;
|
|
"") usage ;;
|
|
*) err "Unknown subcommand '$1' (see --help)" ;;
|
|
esac
|