@@ -979,38 +979,61 @@ if command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ':48099 '; th
979979 echo " WARN: Port 48099 still in use after cleanup; nginx may fail to start"
980980fi
981981
982- # Render nginx config from template.
983- # The gateway token is NOT managed by the add-on; OpenClaw will generate/store it.
984- # Read directly from config file — the CLI redacts secrets since v2026.2.22+.
985- GW_TOKEN= " $( python3 -c "
982+ # ------------------------------------------------------------------------------
983+ # render_landing: (re-)render the nginx config + landing page HTML.
984+ #
985+ # Called once before nginx starts (token may be empty on first boot/pre-onboard)
986+ # and again in the background after the gateway comes up so a freshly-generated
987+ # token is immediately reflected in the "Open Gateway Web UI" button.
988+ # nginx is sent SIGHUP to reload the updated config without restarting.
989+ # ------------------------------------------------------------------------------
990+ render_landing () {
991+ local label=" ${1:- startup} "
992+ # Read gateway token directly from openclaw.json (CLI redacts secrets v2026.2.22+)
993+ local token
994+ token=" $( python3 -c "
986995import json, os
987996p = os.environ.get('OPENCLAW_CONFIG_PATH', '/config/.openclaw/openclaw.json')
988997print(json.load(open(p)).get('gateway',{}).get('auth',{}).get('token',''), end='')
989998" 2> /dev/null || true) "
990999
991- # Collect disk usage for landing page status card
992- DISK_TOTAL= " " DISK_USED= " " DISK_AVAIL= " " DISK_PCT= " "
993- if df -h /config > /dev/null 2>&1 ; then
994- DISK_TOTAL=$( df -h /config | awk ' NR==2{print $2}' )
995- DISK_USED=$( df -h /config | awk ' NR==2{print $3}' )
996- DISK_AVAIL=$( df -h /config | awk ' NR==2{print $4}' )
997- DISK_PCT=$( df -h /config | awk ' NR==2{print $5}' )
998- echo " INFO: Disk usage: ${DISK_USED} /${DISK_TOTAL} (${DISK_PCT} used, ${DISK_AVAIL} free)"
999- # Warn early if disk is getting full
1000- DISK_PCT_NUM=${DISK_PCT//%/ }
1001- if [ " $DISK_PCT_NUM " -ge 90 ] 2> /dev/null; then
1002- echo " WARNING: Disk is ${DISK_PCT} full! Add-on updates may fail. Run 'oc-cleanup' in the terminal."
1003- elif [ " $DISK_PCT_NUM " -ge 75 ] 2> /dev/null; then
1004- echo " NOTICE: Disk is ${DISK_PCT} full. Consider running 'oc-cleanup' in the terminal."
1000+ local disk_total=" " disk_used=" " disk_avail=" " disk_pct=" "
1001+ if df -h /config > /dev/null 2>&1 ; then
1002+ disk_total=$( df -h /config | awk ' NR==2{print $2}' )
1003+ disk_used=$( df -h /config | awk ' NR==2{print $3}' )
1004+ disk_avail=$( df -h /config | awk ' NR==2{print $4}' )
1005+ disk_pct=$( df -h /config | awk ' NR==2{print $5}' )
1006+ if [ " $label " = " startup" ]; then
1007+ echo " INFO: Disk usage: ${disk_used} /${disk_total} (${disk_pct} used, ${disk_avail} free)"
1008+ local pct_num=${disk_pct//%/ }
1009+ if [ " $pct_num " -ge 90 ] 2> /dev/null; then
1010+ echo " WARNING: Disk is ${disk_pct} full! Add-on updates may fail. Run 'oc-cleanup' in the terminal."
1011+ elif [ " $pct_num " -ge 75 ] 2> /dev/null; then
1012+ echo " NOTICE: Disk is ${disk_pct} full. Consider running 'oc-cleanup' in the terminal."
1013+ fi
1014+ fi
10051015 fi
1006- fi
10071016
1008- GW_PUBLIC_URL= " $GW_PUBLIC_URL " GW_TOKEN= " $GW_TOKEN " TERMINAL_PORT= " $TERMINAL_PORT " \
1009- ENABLE_HTTPS_PROXY= " $ENABLE_HTTPS_PROXY " HTTPS_PROXY_PORT= " $GATEWAY_PORT " \
1010- GATEWAY_INTERNAL_PORT= " $GATEWAY_INTERNAL_PORT " ACCESS_MODE= " $ACCESS_MODE " \
1011- DISK_TOTAL= " $DISK_TOTAL " DISK_USED= " $DISK_USED " DISK_AVAIL= " $DISK_AVAIL " DISK_PCT= " $DISK_PCT " \
1012- NGINX_LOG_LEVEL= " $NGINX_LOG_LEVEL " \
1013- python3 /render_nginx.py
1017+ GW_PUBLIC_URL=" $GW_PUBLIC_URL " GW_TOKEN=" $token " TERMINAL_PORT=" $TERMINAL_PORT " \
1018+ ENABLE_HTTPS_PROXY=" $ENABLE_HTTPS_PROXY " HTTPS_PROXY_PORT=" $GATEWAY_PORT " \
1019+ GATEWAY_INTERNAL_PORT=" $GATEWAY_INTERNAL_PORT " ACCESS_MODE=" $ACCESS_MODE " \
1020+ DISK_TOTAL=" $disk_total " DISK_USED=" $disk_used " DISK_AVAIL=" $disk_avail " DISK_PCT=" $disk_pct " \
1021+ NGINX_LOG_LEVEL=" $NGINX_LOG_LEVEL " \
1022+ python3 /render_nginx.py
1023+
1024+ if [ " $label " != " startup" ]; then
1025+ # Signal nginx to reload config/landing HTML without dropping connections.
1026+ local nginx_pid
1027+ nginx_pid=$( cat " ${NGINX_PID_FILE:-/ var/ run/ openclaw-nginx.pid} " 2> /dev/null || true)
1028+ if [ -n " $nginx_pid " ] && kill -0 " $nginx_pid " 2> /dev/null; then
1029+ kill -HUP " $nginx_pid " 2> /dev/null || true
1030+ echo " INFO: Landing page re-rendered with gateway token (nginx reloaded)."
1031+ fi
1032+ fi
1033+ }
1034+
1035+ # Initial render (token may be absent if openclaw.json does not exist yet)
1036+ render_landing startup
10141037
10151038echo " Starting ingress proxy (nginx) on :48099 ..."
10161039nginx -g ' daemon off;' &
@@ -1023,6 +1046,28 @@ else
10231046 echo " WARN: nginx failed to start (PID $NGINX_PID exited); ingress UI may be unavailable"
10241047fi
10251048
1049+ # If the token was not available at startup (first boot / pre-onboard), schedule
1050+ # a background re-render so the "Open Gateway Web UI" button gets the real token
1051+ # once openclaw onboard writes openclaw.json (typically within 30-90 s).
1052+ (
1053+ CONFIG_PATH=" ${OPENCLAW_CONFIG_PATH:-/ config/ .openclaw/ openclaw.json} "
1054+ for _i in $( seq 1 24) ; do
1055+ sleep 5
1056+ token=$( python3 -c "
1057+ import json, os
1058+ p='$CONFIG_PATH '
1059+ try:
1060+ print(json.load(open(p)).get('gateway',{}).get('auth',{}).get('token',''), end='')
1061+ except Exception:
1062+ pass
1063+ " 2> /dev/null || true)
1064+ if [ -n " $token " ]; then
1065+ render_landing post-onboard
1066+ break
1067+ fi
1068+ done
1069+ ) &
1070+
10261071# Keep add-on alive even if gateway/node runtime restarts itself (e.g. during onboarding).
10271072# If runtime exits unexpectedly, restart it while nginx/ttyd stay up.
10281073#
0 commit comments