# retoor <retoor@molodetz.nl>
import logging
import subprocess
import threading
from pathlib import Path
from devplacepy.services.tunnel.config import TUNNEL_NGINX_TUNNEL_DIR
logger = logging.getLogger(__name__)
_lock = threading.Lock()
_TEMPLATE = """location / {
proxy_pass http://127.0.0.1:{remote_port};
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
"""
def _tunnel_dir() -> Path:
return TUNNEL_NGINX_TUNNEL_DIR
def _ensure_tunnel_dir() -> None:
_tunnel_dir().mkdir(parents=True, exist_ok=True)
def add_subdomain(subdomain: str, remote_port: int) -> bool:
with _lock:
try:
_ensure_tunnel_dir()
snippet = _TEMPLATE.format(remote_port=remote_port)
config_path = _tunnel_dir() / f"{subdomain}.conf"
config_path.write_text(snippet)
logger.info(
"Wrote nginx tunnel snippet for %s -> port %d",
subdomain,
remote_port,
)
_reload_nginx()
return True
except OSError:
logger.exception("Failed to write nginx config for %s", subdomain)
return False
def remove_subdomain(subdomain: str) -> bool:
with _lock:
config_path = _tunnel_dir() / f"{subdomain}.conf"
try:
if config_path.exists():
config_path.unlink()
logger.info("Removed nginx tunnel snippet for %s", subdomain)
_reload_nginx()
return True
except OSError:
logger.exception("Failed to remove nginx config for %s", subdomain)
return False
def _reload_nginx() -> None:
try:
result = subprocess.run(
["nginx", "-s", "reload"],
capture_output=True,
text=True,
timeout=10,
)
if result.returncode != 0:
logger.error("nginx reload failed: %s", result.stderr.strip())
else:
logger.info("nginx reloaded successfully")
except FileNotFoundError:
logger.warning("nginx command not found - skipping reload")
except (subprocess.TimeoutExpired, OSError):
logger.exception("nginx reload command failed")