wire the reverse proxy in as officer-setup 12
~/npm-setup-draft/setup-npm.sh, adapted to the script's own helpers and placed last
— it is the only step that needs Officer already running.
It ignores --unattended, as asked. Every other question in this script has a
defensible default; a domain name, a DNS provider and that provider's API
credentials do not, and the step is opt-in besides. Its prompts read stdin directly
instead of going through confirm()/ask_required(), and they are NAMED APART
(proxy_confirm, proxy_ask) so nobody later consolidates them into the shared helpers
and quietly makes --unattended agree to publishing a public hostname.
The valve is a TTY check rather than the flag: with no terminal there is nobody to
ask, so it skips and prints the manual instructions. A cron-driven install still
works.
Five fixes to the draft:
- `${OFFICER_REPO}/scripts/store-npm-credential.ts` — OFFICER_REPO is a git URL,
not a directory, so that path was https://…/platform.git/scripts/… and the -f
test could never pass. The whole persist-to-platform branch was dead code
falling through to the print. Dropped it: the comment beside it already argued
that not storing this password is a legitimate outcome, since only a human
logging into the admin UI needs it.
- NOT re-runnable, despite saying so. claim_admin returned early on an already
claimed instance without setting NPM_EMAIL/NPM_PASSWORD, and get_token
dereferenced both under set -u. Second run died on an unbound variable. It now
asks for the existing credentials.
- $HOME/dockers → $OFFICER_ROOT/dockers, matching data-path.ts. And the network
is SETUP_DOCKER_NETWORK (`services`), not a second bridge called `officerdev`.
- dig → getent hosts. dnsutils is not installed by this platform, so the check was
command-not-found on a fresh VPS — and an empty answer is indistinguishable from
"not resolving yet", so it waited the full 30 minutes before failing.
- python3 → jq for host-side JSON. jq is already in the core package list; the one
remaining python3 runs INSIDE the NPM container to read its own credential
template, which is the point of reading it from there.
Failure is contained: every function warns and returns non-zero rather than exiting,
so a proxy that does not come up leaves a finished Officer install behind. Retry
with `--only Proxy`.
Verified: bash -n, shellcheck -S warning clean, --list shows Proxy, all seven
external commands present, and the jq filters checked against sample payloads
including the multi-line DNS credential.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,8 @@ source "$SCRIPT_DIR/officer-setup/lib/secrets.sh"
|
||||
source "$SCRIPT_DIR/officer-setup/lib/build.sh"
|
||||
# shellcheck source=officer-setup/lib/services.sh
|
||||
source "$SCRIPT_DIR/officer-setup/lib/services.sh"
|
||||
# shellcheck source=officer-setup/lib/proxy.sh
|
||||
source "$SCRIPT_DIR/officer-setup/lib/proxy.sh"
|
||||
|
||||
trap 'echo ""; echo -e "${RED}╔══════════════════════════════════════════════════╗${NC}"; echo -e "${RED}║ OFFICER SETUP FAILED${NC}"; echo -e "${RED}║ Step: ${CURRENT_STEP:-unknown}${NC}"; echo -e "${RED}║ Line: $LINENO${NC}"; echo -e "${RED}║ Command: $BASH_COMMAND${NC}"; echo -e "${RED}╚══════════════════════════════════════════════════╝${NC}"' ERR
|
||||
|
||||
@@ -845,6 +847,85 @@ if ! skip; then
|
||||
fi
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 12. Proxy
|
||||
# =============================================================================
|
||||
#
|
||||
# Optional, and last, because it is the only step that needs Officer to be already
|
||||
# running: NPM proxies to it, and the gate below checks the bind address rather than
|
||||
# taking a curl to loopback as proof.
|
||||
#
|
||||
# ── Why this section ignores --unattended ──
|
||||
#
|
||||
# Every other question in this script has a defensible default. None of these do — a
|
||||
# domain name, a DNS provider and that provider's API credentials cannot be guessed —
|
||||
# and the step is opt-in besides. So its prompts read stdin directly instead of going
|
||||
# through confirm()/ask_required(), which honour ASSUME_YES.
|
||||
#
|
||||
# The valve is a TTY check, not the flag: with no terminal there is nobody to ask, so
|
||||
# it skips and prints the manual instructions. That keeps a cron-driven install working
|
||||
# without letting --unattended silently agree to publishing a public hostname.
|
||||
|
||||
report_section "Proxy"
|
||||
step "Proxy"
|
||||
if ! skip; then
|
||||
echo ""
|
||||
info "Reverse proxy — a real hostname and an HTTPS certificate"
|
||||
echo " Optional. Skip it if you already run a proxy elsewhere, or if you"
|
||||
echo " reach this instance over the tailnet and are happy with that."
|
||||
echo ""
|
||||
|
||||
PROXY_PORT="${ENV_PORT:-9000}"
|
||||
|
||||
if [[ ! -t 0 ]]; then
|
||||
warn "no terminal — skipping the proxy, which cannot be answered unattended"
|
||||
proxy_skip_instructions "$PROXY_PORT"
|
||||
SUMMARY+=("Proxy: skipped (no terminal)")
|
||||
elif ! proxy_require_listening "$PROXY_PORT"; then
|
||||
warn "skipping the proxy — Officer is not reachable the way NPM would reach it"
|
||||
echo " Fix the bind address, then: officer-setup.sh --only Proxy"
|
||||
SUMMARY+=("Proxy: skipped (Officer not listening on 0.0.0.0)")
|
||||
elif ! proxy_confirm "Set up Nginx Proxy Manager now?"; then
|
||||
proxy_skip_instructions "$PROXY_PORT"
|
||||
SUMMARY+=("Proxy: skipped by request")
|
||||
else
|
||||
# One failure path for all of it: every function warns and returns non-zero rather
|
||||
# than exiting, so a proxy that does not come up leaves a finished Officer install
|
||||
# behind rather than a failed one. It is the last section for that reason.
|
||||
PROXY_DOMAIN="$(proxy_ask 'Domain for this instance (e.g. officer.example.com)')"
|
||||
if [[ -z "$PROXY_DOMAIN" ]]; then
|
||||
warn "no domain given — skipping"
|
||||
SUMMARY+=("Proxy: skipped (no domain)")
|
||||
elif
|
||||
proxy_detect_target &&
|
||||
proxy_ensure_network &&
|
||||
proxy_order_docker_after_tailscaled &&
|
||||
proxy_write_compose &&
|
||||
proxy_start &&
|
||||
proxy_claim_admin &&
|
||||
proxy_get_token &&
|
||||
{ [[ "$CHALLENGE" != "dns" ]] || proxy_prompt_dns_credentials; } &&
|
||||
proxy_wait_for_dns "$PROXY_DOMAIN" "$TARGET_IP" &&
|
||||
proxy_allow_bridge_to_host "$PROXY_PORT" &&
|
||||
proxy_create_host "$PROXY_DOMAIN" "$PROXY_PORT" &&
|
||||
proxy_issue_certificate "$PROXY_DOMAIN" &&
|
||||
proxy_attach_certificate
|
||||
then
|
||||
proxy_verify "$PROXY_DOMAIN"
|
||||
echo ""
|
||||
ok "Officer is published at https://${PROXY_DOMAIN}"
|
||||
echo " NPM admin: http://127.0.0.1:81$([[ "$CHALLENGE" == "dns" ]] && echo " or http://${TARGET_IP}:81")"
|
||||
SUMMARY+=("Proxy: https://${PROXY_DOMAIN}")
|
||||
else
|
||||
warn "the proxy did not finish — Officer itself is unaffected and still running"
|
||||
echo " Retry just this part with: officer-setup.sh --only Proxy"
|
||||
ERRORS+=("Proxy: did not finish")
|
||||
SUMMARY+=("Proxy: FAILED — retry with --only Proxy")
|
||||
fi
|
||||
fi
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# ── Who you are when this exits ──
|
||||
#
|
||||
# Root, and that surprises people — reasonably, because everything this script just
|
||||
|
||||
Reference in New Issue
Block a user