Add HA ingress UI proxy + optional web terminal
This commit is contained in:
@@ -20,7 +20,9 @@ RUN apk add --no-cache \
|
|||||||
libatomic \
|
libatomic \
|
||||||
file \
|
file \
|
||||||
pax-utils \
|
pax-utils \
|
||||||
python3
|
python3 \
|
||||||
|
nginx \
|
||||||
|
ttyd
|
||||||
|
|
||||||
# Install Node.js 22+ (musl build) from unofficial builds.
|
# 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.
|
# 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
|
&& npm install -g clawdbot
|
||||||
|
|
||||||
COPY run.sh /run.sh
|
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" ]
|
CMD [ "/run.sh" ]
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ init: false
|
|||||||
hassio_api: false
|
hassio_api: false
|
||||||
homeassistant_api: false
|
homeassistant_api: false
|
||||||
host_network: true
|
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:
|
map:
|
||||||
- addon_config:rw
|
- addon_config:rw
|
||||||
options:
|
options:
|
||||||
@@ -41,6 +48,9 @@ options:
|
|||||||
# Brave Search API key (optional; enables Brave-backed web search tool)
|
# Brave Search API key (optional; enables Brave-backed web search tool)
|
||||||
brave_api_key: ""
|
brave_api_key: ""
|
||||||
|
|
||||||
|
# Enable web terminal inside Home Assistant (Ingress) via ttyd
|
||||||
|
enable_terminal: false
|
||||||
|
|
||||||
# Cleanup stale session lock files left after crashes/restarts
|
# Cleanup stale session lock files left after crashes/restarts
|
||||||
clean_session_locks_on_start: true
|
clean_session_locks_on_start: true
|
||||||
clean_session_locks_on_exit: true
|
clean_session_locks_on_exit: true
|
||||||
@@ -60,6 +70,7 @@ schema:
|
|||||||
mikrotik_ssh_user: str
|
mikrotik_ssh_user: str
|
||||||
mikrotik_ssh_key_path: str
|
mikrotik_ssh_key_path: str
|
||||||
brave_api_key: str?
|
brave_api_key: str?
|
||||||
|
enable_terminal: bool?
|
||||||
clean_session_locks_on_start: bool?
|
clean_session_locks_on_start: bool?
|
||||||
clean_session_locks_on_exit: bool?
|
clean_session_locks_on_exit: bool?
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
-1
@@ -18,6 +18,7 @@ GW_PORT=$(jq -r '.gateway_port // 18789' "$OPTIONS_FILE")
|
|||||||
GW_TOKEN=$(jq -r '.gateway_token // empty' "$OPTIONS_FILE")
|
GW_TOKEN=$(jq -r '.gateway_token // empty' "$OPTIONS_FILE")
|
||||||
HA_TOKEN=$(jq -r '.homeassistant_token // empty' "$OPTIONS_FILE")
|
HA_TOKEN=$(jq -r '.homeassistant_token // empty' "$OPTIONS_FILE")
|
||||||
BRAVE_KEY=$(jq -r '.brave_api_key // 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_HOST=$(jq -r '.mikrotik_host // "192.168.88.1"' "$OPTIONS_FILE")
|
||||||
MT_USER=$(jq -r '.mikrotik_ssh_user // "papur"' "$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")
|
MT_KEY=$(jq -r '.mikrotik_ssh_key_path // "/data/keys/mikrotik_papur_nopw"' "$OPTIONS_FILE")
|
||||||
@@ -225,7 +226,18 @@ fi
|
|||||||
GW_PID=""
|
GW_PID=""
|
||||||
|
|
||||||
shutdown() {
|
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
|
if [ -n "${GW_PID}" ] && kill -0 "${GW_PID}" >/dev/null 2>&1; then
|
||||||
kill -TERM "${GW_PID}" >/dev/null 2>&1 || true
|
kill -TERM "${GW_PID}" >/dev/null 2>&1 || true
|
||||||
wait "${GW_PID}" || true
|
wait "${GW_PID}" || true
|
||||||
@@ -238,8 +250,47 @@ shutdown() {
|
|||||||
|
|
||||||
trap shutdown INT TERM
|
trap shutdown INT TERM
|
||||||
|
|
||||||
|
NGINX_PID=""
|
||||||
|
TTYD_PID=""
|
||||||
|
|
||||||
echo "Starting Clawdbot Gateway..."
|
echo "Starting Clawdbot Gateway..."
|
||||||
clawdbot gateway run &
|
clawdbot gateway run &
|
||||||
GW_PID=$!
|
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}"
|
wait "${GW_PID}"
|
||||||
|
|||||||
Reference in New Issue
Block a user