From 1509822a316e923bad298d881fc9807f62f77b89 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 28 Jan 2026 19:49:02 +0200 Subject: [PATCH] Add HA ingress UI proxy + optional web terminal --- papur-addon/Dockerfile | 8 ++++-- papur-addon/config.yaml | 11 ++++++++ papur-addon/nginx.conf.tpl | 50 +++++++++++++++++++++++++++++++++++ papur-addon/run.sh | 53 +++++++++++++++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 papur-addon/nginx.conf.tpl diff --git a/papur-addon/Dockerfile b/papur-addon/Dockerfile index 9984118..8c222a5 100644 --- a/papur-addon/Dockerfile +++ b/papur-addon/Dockerfile @@ -20,7 +20,9 @@ RUN apk add --no-cache \ libatomic \ file \ pax-utils \ - python3 + python3 \ + nginx \ + ttyd # Install Node.js 22+ (musl build) from unofficial builds. # Router/base image Alpine may ship an older libstdc++; we pull a newer libstdc++ from edge to satisfy Node's C++ symbols. @@ -45,6 +47,8 @@ RUN npm config set fund false && npm config set audit false \ && npm install -g clawdbot COPY run.sh /run.sh -RUN chmod +x /run.sh +COPY nginx.conf.tpl /etc/nginx/nginx.conf.tpl +RUN chmod +x /run.sh \ + && mkdir -p /run/nginx CMD [ "/run.sh" ] diff --git a/papur-addon/config.yaml b/papur-addon/config.yaml index 52aa75c..b62f608 100644 --- a/papur-addon/config.yaml +++ b/papur-addon/config.yaml @@ -11,6 +11,13 @@ init: false hassio_api: false homeassistant_api: false host_network: true + +# Home Assistant Ingress (UI inside the add-on page) +ingress: true +ingress_port: 8099 +panel_title: Papur +panel_icon: mdi:robot + map: - addon_config:rw options: @@ -41,6 +48,9 @@ options: # Brave Search API key (optional; enables Brave-backed web search tool) brave_api_key: "" + # Enable web terminal inside Home Assistant (Ingress) via ttyd + enable_terminal: false + # Cleanup stale session lock files left after crashes/restarts clean_session_locks_on_start: true clean_session_locks_on_exit: true @@ -60,6 +70,7 @@ schema: mikrotik_ssh_user: str mikrotik_ssh_key_path: str brave_api_key: str? + enable_terminal: bool? clean_session_locks_on_start: bool? clean_session_locks_on_exit: bool? diff --git a/papur-addon/nginx.conf.tpl b/papur-addon/nginx.conf.tpl new file mode 100644 index 0000000..d831a5a --- /dev/null +++ b/papur-addon/nginx.conf.tpl @@ -0,0 +1,50 @@ +worker_processes 1; + +# Log to stderr/stdout (container-friendly) +error_log /dev/stderr notice; + +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Log to stdout/stderr (container-friendly) + access_log /dev/stdout; + error_log /dev/stderr notice; + + sendfile on; + keepalive_timeout 65; + + # Inject gateway token server-side. Token is substituted into this template at runtime. + map $args $args_with_token { + "" "token=__GATEWAY_TOKEN__"; + default "$args&token=__GATEWAY_TOKEN__"; + } + + server { + listen 8099; + + # Web terminal (ttyd) + location /terminal/ { + proxy_pass http://127.0.0.1:7681/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Everything else -> Clawdbot gateway UI (websocket capable) + location / { + proxy_pass http://127.0.0.1:18789$uri?$args_with_token; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/papur-addon/run.sh b/papur-addon/run.sh index b3b0cea..3d3f38e 100644 --- a/papur-addon/run.sh +++ b/papur-addon/run.sh @@ -18,6 +18,7 @@ GW_PORT=$(jq -r '.gateway_port // 18789' "$OPTIONS_FILE") GW_TOKEN=$(jq -r '.gateway_token // empty' "$OPTIONS_FILE") HA_TOKEN=$(jq -r '.homeassistant_token // empty' "$OPTIONS_FILE") BRAVE_KEY=$(jq -r '.brave_api_key // empty' "$OPTIONS_FILE") +ENABLE_TERMINAL=$(jq -r '.enable_terminal // false' "$OPTIONS_FILE") MT_HOST=$(jq -r '.mikrotik_host // "192.168.88.1"' "$OPTIONS_FILE") MT_USER=$(jq -r '.mikrotik_ssh_user // "papur"' "$OPTIONS_FILE") MT_KEY=$(jq -r '.mikrotik_ssh_key_path // "/data/keys/mikrotik_papur_nopw"' "$OPTIONS_FILE") @@ -225,7 +226,18 @@ fi GW_PID="" shutdown() { - echo "Shutdown requested; stopping gateway..." + echo "Shutdown requested; stopping services..." + + if [ -n "${NGINX_PID}" ] && kill -0 "${NGINX_PID}" >/dev/null 2>&1; then + kill -TERM "${NGINX_PID}" >/dev/null 2>&1 || true + wait "${NGINX_PID}" || true + fi + + if [ -n "${TTYD_PID}" ] && kill -0 "${TTYD_PID}" >/dev/null 2>&1; then + kill -TERM "${TTYD_PID}" >/dev/null 2>&1 || true + wait "${TTYD_PID}" || true + fi + if [ -n "${GW_PID}" ] && kill -0 "${GW_PID}" >/dev/null 2>&1; then kill -TERM "${GW_PID}" >/dev/null 2>&1 || true wait "${GW_PID}" || true @@ -238,8 +250,47 @@ shutdown() { trap shutdown INT TERM +NGINX_PID="" +TTYD_PID="" + echo "Starting Clawdbot Gateway..." clawdbot gateway run & GW_PID=$! +# Start web terminal (optional) +if [ "$ENABLE_TERMINAL" = "true" ]; then + echo "Starting web terminal (ttyd) on 127.0.0.1:7681 ..." + # -W: allow clients to write (interactive) + # -p: port + # bind localhost only; exposed to HA via ingress reverse proxy + ttyd -W -i 127.0.0.1 -p 7681 bash & + TTYD_PID=$! +else + echo "Terminal disabled (enable_terminal=false)" +fi + +# Start ingress reverse proxy (nginx). This provides the add-on UI inside HA. +# Token is injected server-side; never put it in the browser URL. + +# Render nginx config from template with the gateway token. +python3 - <<'PY' +import os +from pathlib import Path + +tpl = Path('/etc/nginx/nginx.conf.tpl').read_text() +token = os.environ.get('GW_TOKEN','') +if not token: + # Keep nginx running, but gateway UI will remain inaccessible. + # This avoids breaking the add-on UI completely if token is unset. + print('WARN: gateway_token is empty; ingress proxy will not be able to authenticate to gateway UI.') + +conf = tpl.replace('__GATEWAY_TOKEN__', token) +Path('/etc/nginx/nginx.conf').write_text(conf) +PY + +echo "Starting ingress proxy (nginx) on :8099 ..." +nginx -g 'daemon off;' & +NGINX_PID=$! + +# Wait for gateway; if it exits, shut down others. wait "${GW_PID}"