#!/bin/bash set -euo pipefail DB="/var/lib/archipelago/nginx-proxy-manager/data/database.sqlite" OUT="/etc/nginx/conf.d/public-npm-proxy-hosts.conf" ACME_ROOT="/var/lib/archipelago/nginx-proxy-manager/data/letsencrypt-acme-challenge" LE_ROOT="/var/lib/archipelago/nginx-proxy-manager/letsencrypt/live" [ -f "$DB" ] || exit 0 mkdir -p "$ACME_ROOT/.well-known/acme-challenge" chown -R 1000:1000 /var/lib/archipelago/nginx-proxy-manager 2>/dev/null || true tmp=$(mktemp) trap 'rm -f "$tmp"' EXIT python3 - "$DB" "$ACME_ROOT" "$LE_ROOT" >"$tmp" <<'PY' import json import os import sqlite3 import sys db, acme_root, le_root = sys.argv[1:] con = sqlite3.connect(db) con.row_factory = sqlite3.Row rows = con.execute( """ select p.id, p.domain_names, p.forward_scheme, p.forward_host, p.forward_port, p.certificate_id, p.ssl_forced, c.provider from proxy_host p left join certificate c on c.id = p.certificate_id where p.enabled = 1 and p.certificate_id > 0 order by p.id """ ).fetchall() print("# Generated by sync-npm-public-hosts.sh; do not edit by hand.") for row in rows: try: domains = [d for d in json.loads(row["domain_names"] or "[]") if d] except Exception: domains = [] if not domains: continue cert_id = row["certificate_id"] cert = f"{le_root}/npm-{cert_id}/fullchain.pem" key = f"{le_root}/npm-{cert_id}/privkey.pem" if row["provider"] != "letsencrypt": continue if not os.path.isfile(cert) or not os.path.isfile(key): continue names = " ".join(domains) scheme = row["forward_scheme"] or "http" host = row["forward_host"] port = row["forward_port"] if not host or not port: continue # NPM containers use this name to reach host-published services; host nginx # itself should use loopback for the same services. nginx_host = "127.0.0.1" if host == "host.containers.internal" else host try: forward_port = int(port) except (TypeError, ValueError): forward_port = None graphql_location = "" extra_proxy_headers = "" print(f""" server {{ listen 80; server_name {names}; location ^~ /.well-known/acme-challenge/ {{ default_type text/plain; root {acme_root}; try_files $uri =404; }} location / {{ return 301 https://$host$request_uri; }} }} server {{ listen 443 ssl; server_name {names}; ssl_certificate {cert}; ssl_certificate_key {key}; {graphql_location} location / {{ proxy_pass {scheme}://{nginx_host}:{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 https; proxy_set_header X-Forwarded-Scheme https; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; {extra_proxy_headers} }} }} """) PY backup="" if [ -f "$OUT" ]; then backup=$(mktemp) cp "$OUT" "$backup" fi restore_previous() { if [ -n "$backup" ] && [ -f "$backup" ]; then install -m 0644 "$backup" "$OUT" else rm -f "$OUT" fi } if ! install -m 0644 "$tmp" "$OUT" || ! nginx -t >/dev/null; then restore_previous nginx -t >/dev/null 2>&1 || true exit 1 fi systemctl reload nginx