#!/usr/bin/env python3 """Set up NPMPlus reverse proxy for Canteen GPS Map.""" import json import urllib.request import ssl import subprocess NPM_BASE = "https://192.168.0.115:81" DOMAIN = "gps.ourpad.casa" BACKEND_HOST = "192.168.0.127" BACKEND_PORT = 8913 BACKEND_SCHEME = "http" # Get token with open("/tmp/npmplus_token.json") as f: token_data = json.load(f) TOKEN = token_data["token"] ctx = ssl._create_unverified_context() def api(method, path, data=None): url = f"{NPM_BASE}{path}" body = json.dumps(data).encode() if data else None req = urllib.request.Request(url, data=body, method=method) req.add_header("Authorization", f"Bearer {TOKEN}") req.add_header("Content-Type", "application/json") try: resp = urllib.request.urlopen(req, context=ctx, timeout=15) return json.loads(resp.read().decode()) except urllib.error.HTTPError as e: err = e.read().decode() print(f"HTTP {e.code}: {err}") return None # Get CF token from pass cf_token = subprocess.check_output( ["pass", "npmplus/cloudflare-api-token"], text=True ).strip() # 1. Check existing certificates print("=== Checking existing certificates ===") certs = api("GET", "/api/nginx/certificates") existing_cert_id = None if certs: for c in certs: domains = c.get("domain_names", []) if DOMAIN in domains: existing_cert_id = c["id"] print(f"Found existing cert for {DOMAIN}: ID={existing_cert_id}, expires={c.get('expires_on','?')}") break if not existing_cert_id: # 2. Create Let's Encrypt cert via DNS challenge print(f"\n=== Creating Let's Encrypt cert for {DOMAIN} ===") cert_result = api( "POST", "/api/nginx/certificates", { "domain_names": [DOMAIN], "provider": "letsencrypt", "meta": { "letsencrypt_agree": True, "dns_challenge": True, "dns_provider": "cloudflare", "dns_provider_credentials": f"dns_cloudflare_api_token={cf_token}", }, }, ) if cert_result and "id" in cert_result: existing_cert_id = cert_result["id"] print(f"Created cert: ID={existing_cert_id}") else: print("Failed to create cert:", cert_result) exit(1) # 3. Check existing proxy hosts print(f"\n=== Checking existing proxy hosts for {DOMAIN} ===") hosts = api("GET", "/api/nginx/proxy-hosts") existing_host_id = None if hosts: for h in hosts: if DOMAIN in h.get("domain_names", []): existing_host_id = h["id"] print(f"Found existing host: ID={existing_host_id}") break if existing_host_id: # Update existing print(f"\n=== Updating proxy host {existing_host_id} ===") result = api( "PUT", f"/api/nginx/proxy-hosts/{existing_host_id}", { "domain_names": [DOMAIN], "forward_host": BACKEND_HOST, "forward_port": BACKEND_PORT, "certificate_id": existing_cert_id, "ssl_forced": True, "forward_scheme": BACKEND_SCHEME, "enabled": True, "block_exploits": False, "locations": [], "advanced_config": "", }, ) if result: print("Updated successfully") else: print("Update failed") else: # Create new print(f"\n=== Creating proxy host for {DOMAIN} ===") result = api( "POST", "/api/nginx/proxy-hosts", { "domain_names": [DOMAIN], "forward_host": BACKEND_HOST, "forward_port": BACKEND_PORT, "certificate_id": existing_cert_id, "ssl_forced": True, "forward_scheme": BACKEND_SCHEME, "enabled": True, "block_exploits": False, "locations": [], "advanced_config": "", }, ) if result and "id" in result: print(f"Created proxy host: ID={result['id']}") else: print("Create failed:", result) print("\n=== Done ===") print(f"Access: https://{DOMAIN}/ (via Tailscale)") print(f"Tailscale DNS override: {DOMAIN} → NPMPlus Tailscale IP")