#!/usr/bin/env bash # Create NPMPlus proxy host for LocalAI + request Let's Encrypt cert. # Usage: ./scripts/configure-localai-proxy.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" cd "${STACK_DIR}" if [[ ! -f .env ]]; then echo "ERROR: .env not found" exit 1 fi set -a # shellcheck disable=SC1091 source .env set +a export INITIAL_ADMIN_EMAIL INITIAL_ADMIN_PASSWORD ACME_EMAIL \ LOCALAI_PROXY_DOMAIN LOCALAI_FORWARD_PORT python3 <<'PY' import json import os import ssl import sys import urllib.error import urllib.request import http.cookiejar domain = os.environ.get("LOCALAI_PROXY_DOMAIN", "llm.rtx1.mobile.agency-ai.dev") forward_port = int(os.environ.get("LOCALAI_FORWARD_PORT", "8070")) admin_email = os.environ.get("INITIAL_ADMIN_EMAIL", "") admin_pass = os.environ.get("INITIAL_ADMIN_PASSWORD", "") acme_email = os.environ.get("ACME_EMAIL", admin_email) if not admin_email or not admin_pass: print("ERROR: Set INITIAL_ADMIN_EMAIL and INITIAL_ADMIN_PASSWORD in .env", file=sys.stderr) sys.exit(1) ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE cj = http.cookiejar.CookieJar() opener = urllib.request.build_opener( urllib.request.HTTPCookieProcessor(cj), urllib.request.HTTPSHandler(context=ctx), ) def api(method: str, path: str, payload=None): data = None headers = {} if payload is not None: data = json.dumps(payload).encode() headers["Content-Type"] = "application/json" req = urllib.request.Request(f"https://127.0.0.1:81{path}", data=data, headers=headers, method=method) with opener.open(req) as resp: body = resp.read() return json.loads(body) if body else {} try: api("POST", "/api/tokens", {"identity": admin_email, "secret": admin_pass}) except urllib.error.HTTPError as e: print(f"ERROR: NPMPlus login failed ({e.code})", file=sys.stderr) print(e.read().decode()[:300], file=sys.stderr) sys.exit(1) if not any(c.name == "__Host-Http-token" for c in cj): print("ERROR: NPMPlus login did not return session cookie", file=sys.stderr) sys.exit(1) hosts = api("GET", "/api/nginx/proxy-hosts") for host in hosts: if domain in host.get("domain_names", []): print(f"Proxy host already exists (id={host['id']}) for {domain}") sys.exit(0) payload = { "domain_names": [domain], "forward_scheme": "http", "forward_host": "127.0.0.1", "forward_port": forward_port, "access_list_id": 0, "certificate_id": "new", "ssl_forced": True, "http2_support": True, "block_exploits": True, "allow_websocket_upgrade": True, "meta": { "letsencrypt_email": acme_email, "letsencrypt_agree": True, "dns_challenge": False, }, } try: result = api("POST", "/api/nginx/proxy-hosts", payload) except urllib.error.HTTPError as e: print(f"ERROR: create proxy host failed ({e.code})", file=sys.stderr) print(e.read().decode()[:500], file=sys.stderr) sys.exit(1) print(json.dumps(result, indent=2)) print(f"\nProxy host created for https://{domain} -> 127.0.0.1:{forward_port}") print("Certificate issuance may take 1-2 minutes (check NPMPlus logs).") PY