Fix textual-serve web: robust IP detection for WebSocket URL
The landing page wasn't interactive because public_url resolved to localhost, making WebSocket connections fail from remote browsers. - Added multiple IP detection methods (socket, hostname -I, gethostbyname) - Support --port= and --host= flag formats - Print actual serving URL on startup - Switch web start back to textual-serve (TUI in browser) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -402,10 +402,10 @@ if [[ "$SUBCOMMAND" == "web" ]]; then
|
|||||||
_web_port="" _web_host=""
|
_web_port="" _web_host=""
|
||||||
_web_port=$(_parse_flag "--port" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
|
_web_port=$(_parse_flag "--port" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
|
||||||
_web_host=$(_parse_flag "--host" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
|
_web_host=$(_parse_flag "--host" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
|
||||||
_web_args=()
|
_web_args=(--web)
|
||||||
[[ -n "$_web_port" ]] && _web_args+=(--port="$_web_port")
|
[[ -n "$_web_port" ]] && _web_args+=(--port="$_web_port")
|
||||||
[[ -n "$_web_host" ]] && _web_args+=(--host="$_web_host")
|
[[ -n "$_web_host" ]] && _web_args+=(--host="$_web_host")
|
||||||
PYTHONPATH="$GNIZA_DIR:${PYTHONPATH:-}" exec python3 -m web "${_web_args[@]}"
|
PYTHONPATH="$GNIZA_DIR:${PYTHONPATH:-}" exec python3 -m tui "${_web_args[@]}"
|
||||||
;;
|
;;
|
||||||
install-service)
|
install-service)
|
||||||
_service_src="$GNIZA_DIR/etc/gniza-web.service"
|
_service_src="$GNIZA_DIR/etc/gniza-web.service"
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/python3 -m web
|
ExecStart=/usr/bin/python3 -m tui --web --host 0.0.0.0 --port 8080
|
||||||
WorkingDirectory=/usr/local/gniza
|
WorkingDirectory=/usr/local/gniza
|
||||||
Environment=GNIZA_DIR=/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
|
Environment=PYTHONPATH=/usr/local/gniza
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -11,40 +12,74 @@ _ROOT = os.environ.get("GNIZA_DIR", str(Path(__file__).resolve().parent.parent))
|
|||||||
|
|
||||||
def _get_local_ip() -> str:
|
def _get_local_ip() -> str:
|
||||||
"""Get the machine's LAN IP for public_url."""
|
"""Get the machine's LAN IP for public_url."""
|
||||||
|
# Method 1: UDP socket trick
|
||||||
try:
|
try:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.settimeout(2)
|
||||||
s.connect(("8.8.8.8", 80))
|
s.connect(("8.8.8.8", 80))
|
||||||
ip = s.getsockname()[0]
|
ip = s.getsockname()[0]
|
||||||
s.close()
|
s.close()
|
||||||
return ip
|
if ip and ip != "0.0.0.0":
|
||||||
|
return ip
|
||||||
except Exception:
|
except Exception:
|
||||||
return "localhost"
|
pass
|
||||||
|
# Method 2: hostname -I
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["hostname", "-I"], capture_output=True, text=True, timeout=5
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
ip = result.stdout.strip().split()[0]
|
||||||
|
if ip:
|
||||||
|
return ip
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Method 3: hostname resolution
|
||||||
|
try:
|
||||||
|
return socket.gethostbyname(socket.gethostname())
|
||||||
|
except Exception:
|
||||||
|
return "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_web_args():
|
||||||
|
"""Parse --port and --host from sys.argv."""
|
||||||
|
port = 8080
|
||||||
|
host = "0.0.0.0"
|
||||||
|
for i, arg in enumerate(sys.argv):
|
||||||
|
if arg.startswith("--port="):
|
||||||
|
port = int(arg.split("=", 1)[1])
|
||||||
|
elif arg == "--port" and i + 1 < len(sys.argv):
|
||||||
|
port = int(sys.argv[i + 1])
|
||||||
|
elif arg.startswith("--host="):
|
||||||
|
host = arg.split("=", 1)[1]
|
||||||
|
elif arg == "--host" and i + 1 < len(sys.argv):
|
||||||
|
host = sys.argv[i + 1]
|
||||||
|
return host, port
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if "--web" in sys.argv:
|
if "--web" in sys.argv:
|
||||||
from textual_serve.server import Server
|
from textual_serve.server import Server
|
||||||
port = 8080
|
|
||||||
host = "0.0.0.0"
|
host, port = _parse_web_args()
|
||||||
public_host = None
|
|
||||||
for i, arg in enumerate(sys.argv):
|
|
||||||
if arg == "--port" and i + 1 < len(sys.argv):
|
|
||||||
port = int(sys.argv[i + 1])
|
|
||||||
elif arg == "--host" and i + 1 < len(sys.argv):
|
|
||||||
host = sys.argv[i + 1]
|
|
||||||
public_host = host
|
|
||||||
os.environ["PYTHONPATH"] = f"{_ROOT}:{os.environ.get('PYTHONPATH', '')}"
|
os.environ["PYTHONPATH"] = f"{_ROOT}:{os.environ.get('PYTHONPATH', '')}"
|
||||||
os.environ["GNIZA_DIR"] = _ROOT
|
os.environ["GNIZA_DIR"] = _ROOT
|
||||||
# textual-serve uses public_url to build WebSocket URLs.
|
|
||||||
# If binding to 0.0.0.0, detect the real IP for the browser.
|
# Determine public URL for WebSocket connections
|
||||||
if public_host is None:
|
if host == "0.0.0.0":
|
||||||
public_host = _get_local_ip() if host == "0.0.0.0" else host
|
public_host = _get_local_ip()
|
||||||
|
else:
|
||||||
|
public_host = host
|
||||||
|
|
||||||
public_url = f"http://{public_host}:{port}"
|
public_url = f"http://{public_host}:{port}"
|
||||||
|
print(f"GNIZA web: serving TUI at {public_url}")
|
||||||
|
|
||||||
server = Server(
|
server = Server(
|
||||||
"python3 -m tui",
|
f"python3 -m tui",
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
title="gniza",
|
title="GNIZA Backup",
|
||||||
public_url=public_url,
|
public_url=public_url,
|
||||||
)
|
)
|
||||||
server.serve()
|
server.serve()
|
||||||
|
|||||||
Reference in New Issue
Block a user