Files
shawn bfcf876d42 Initial commit: Canteen GPS Map - interactive map combining booking + photo EXIF GPS data
Features:
- Dark-themed interactive Leaflet map with marker clustering
- Combined GPS sources: booking (technician check-in) + photo EXIF + account locations
- Collapsible filter bar with search, source toggles, and source type dropdown
- Info popups on click showing machine details, work orders, serials, photos
- Account location layer toggle (3,487 accounts)
- Export filtered data as JSON
- Mobile-responsive (40vh map on mobile, filter collapse)
- FastAPI backend with in-memory caching
- Systemd service for auto-start
- NPMPlus reverse proxy at gps.ourpad.casa

Stats: 94 unique machines (73 booking, 21 photo), 231 total GPS points, 3,487 account locations
2026-05-28 00:22:43 -04:00

138 lines
4.1 KiB
Python

#!/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")