From ff61e8f733732bbbc153c58d2a65526ef8d3a344 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 12:38:45 +0000 Subject: [PATCH] Chain Security Dashboard + PSTN trunk setup into the Asterisk install flow asterisk.sh and asterisk-digital-ocean.sh now offer, at the end of both their fresh-install and update-mode paths, to also set up the Security Dashboard and configure a real PSTN trunk in the same run - one script walks through the whole stack instead of needing to separately remember and run `sudo ./setup.sh security-dashboard` / `sudo ./setup.sh pstn-trunk` afterward. Both target services keep their own register_service call and stay fully independently invocable - this is purely an additive convenience layer (_asterisk_offer_dashboard_and_trunk / _asterisk_do_offer_dashboard_and_trunk), not a replacement. An already-installed piece is silently refreshed (install_security-dashboard/install_pstn-trunk each already have their own update/fresh/cancel reinstall-mode gate, so calling them again just does the right thing); a not-yet-installed piece gets one y/n instead of every detailed prompt firing. Guarded with declare -F so a standalone `sudo bash asterisk.sh` copy (no sibling services/*.sh files sourced) skips both cleanly with an explanatory message instead of erroring on an undefined function. Verified: full sourcing simulation resolves all four install_* functions correctly, `setup.sh --dry-run --unattended asterisk` and `asterisk-digital-ocean` both complete cleanly end-to-end, and `setup.sh --list` still shows all four services as independently selectable. Documented the pattern in CLAUDE.md under a new "Chaining into another service from within your own" section for future contributors. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ho9mZgAkVpdz7S5wJkg8Nf --- CLAUDE.md | 35 ++++++++++++++++++++ services/asterisk-digital-ocean.sh | 44 +++++++++++++++++++++++++ services/asterisk.sh | 53 ++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index d418374..451aa3c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -438,6 +438,41 @@ a reason (a stray Enter on a service you're just checking on shouldn't trigger anything). `fresh` runs the exact same flow a first-time install would, prompts included. +## Chaining into another service from within your own + +A service can call another service's `install_()` directly as a +convenience step at the end of its own flow, instead of making the user +remember to separately run `sudo ./setup.sh ` afterward. +`services/asterisk.sh`/`services/asterisk-digital-ocean.sh` do this for +`services/security-dashboard.sh` and `services/pstn-trunk.sh` — after +Asterisk itself is installed/updated, each asks once whether to also set up +the dashboard and/or a PSTN trunk (or, if either is already installed, +silently re-invokes it so it gets refreshed as part of the same run — its +own `prompt_reinstall_mode` gate decides update vs. skip, so this never +re-asks the target service's detailed prompts unless the user is actually +setting it up fresh). + +The target service **keeps its own `register_service` call** — it stays +independently selectable/invocable exactly as before (`sudo ./setup.sh +pstn-trunk` still works standalone). Chaining is purely additive, not a +replacement for the target's own entry point, so nothing breaks for anyone +already relying on running it directly. + +Guard every cross-file call with `declare -F`, since a service can also run +completely standalone (`sudo bash asterisk.sh`, no `setup.sh`, no sibling +`services/*.sh` files sourced at all): + +```bash +if declare -F install_security-dashboard >/dev/null 2>&1; then + install_security-dashboard +fi +``` + +Only chain in one direction, and only when the relationship is genuinely +one-way (the target is meaningless without the caller already installed — +`pstn-trunk.sh` itself says so in its own error message when Asterisk isn't +present). Don't have both sides call each other. + ## .env files and secrets Generate passwords with `generate_password` (never hardcode them). diff --git a/services/asterisk-digital-ocean.sh b/services/asterisk-digital-ocean.sh index b529a6e..ee6ce1b 100755 --- a/services/asterisk-digital-ocean.sh +++ b/services/asterisk-digital-ocean.sh @@ -645,6 +645,44 @@ ENV log_info "never alerts by itself, since there's no prior state to compare against yet." } +# See services/asterisk.sh's own copy for the full rationale — identical +# here, just calling into the same install_security-dashboard/ +# install_pstn-trunk entry points (still independently registered/ +# invocable; this is a convenience layer on top, not a replacement). +_asterisk_do_offer_dashboard_and_trunk() { + local EA_DIR="$1" + + if ! declare -F install_security-dashboard >/dev/null 2>&1 && ! declare -F install_pstn-trunk >/dev/null 2>&1; then + log_info "Run this from the full ubuntu-post-install repo (not a standalone copy) to also" + log_info "get prompts here for the Security Dashboard and a PSTN trunk — skipping both." + return 0 + fi + + if declare -F install_security-dashboard >/dev/null 2>&1; then + echo "" + if [[ -f "$DOCKER_DIR/security-dashboard/app.py" ]]; then + log_info "Security Dashboard already installed — refreshing it too..." + install_security-dashboard + else + local _WANT_DASH="" + prompt_yn "Set up the Security Dashboard (Security Log, Extensions, Asterisk Admin, PSTN Trunk, CrowdSec — one page)? (y/n):" "y" _WANT_DASH + [[ "$_WANT_DASH" =~ ^[Yy]$ ]] && install_security-dashboard + fi + fi + + if declare -F install_pstn-trunk >/dev/null 2>&1; then + echo "" + if [[ -f "$EA_DIR/config/asterisk/pstn-trunk-dialplan.conf" ]]; then + log_info "PSTN trunk already configured — refreshing it too..." + install_pstn-trunk + else + local _WANT_TRUNK="" + prompt_yn "Configure a real SIP/PSTN trunk (actual outside phone numbers, e.g. Anveo Direct/VoIP.ms)? (y/n):" "n" _WANT_TRUNK + [[ "$_WANT_TRUNK" =~ ^[Yy]$ ]] && install_pstn-trunk + fi + fi +} + # ── Shared: docker-compose.yml ───────────────────────────────────────────── # Same reasoning as above — one copy of the template used by both fresh # installs and updates. Must be called with $PWD already at $EA_DIR. @@ -740,6 +778,9 @@ install_asterisk-digital-ocean() { echo "[DRY-RUN] gated live on each sender's 'messaging' flag in pstn-permissions.conf (the" echo "[DRY-RUN] same file/flag the Security Dashboard's checkbox writes) — independent of" echo "[DRY-RUN] whether the PSTN trunk is installed; migrates any already-existing devices too" + echo "[DRY-RUN] Would offer to also set up the Security Dashboard and a PSTN trunk in this" + echo "[DRY-RUN] same run (calling services/security-dashboard.sh / services/pstn-trunk.sh" + echo "[DRY-RUN] directly — both stay independently invocable via their own service name too)" return 0 fi @@ -778,6 +819,7 @@ install_asterisk-digital-ocean() { fi _asterisk_do_run_presence_step "$EA_DIR" + _asterisk_do_offer_dashboard_and_trunk "$EA_DIR" local _EXISTING_DOMAIN _EXISTING_PORT _EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)" @@ -1425,6 +1467,8 @@ MD || log_warning "Start failed — check: docker compose logs" fi + _asterisk_do_offer_dashboard_and_trunk "$EA_DIR" + # ── Summary ─────────────────────────────────────────────────────────────── echo "" log_success "Easy Asterisk (DigitalOcean edition) installed at $EA_DIR" diff --git a/services/asterisk.sh b/services/asterisk.sh index 060a1c4..ad5f4fb 100644 --- a/services/asterisk.sh +++ b/services/asterisk.sh @@ -609,6 +609,53 @@ ENV log_info "never alerts by itself, since there's no prior state to compare against yet." } +# Offers to add/refresh the Security Dashboard and a PSTN trunk as part of +# this SAME run, instead of needing to separately remember and run +# `sudo ./setup.sh security-dashboard` / `sudo ./setup.sh pstn-trunk` +# afterward. Neither loses its own independent registration/invocability — +# this is purely a convenience layer on top, called from both the fresh- +# install and update-mode paths below. An already-installed piece is just +# silently refreshed (install_security-dashboard/install_pstn-trunk each +# have their own update/fresh/cancel reinstall-mode gate, so calling them +# again here does the right thing automatically); a not-yet-installed piece +# gets a one-line y/n instead of every detailed prompt firing unconditionally. +_asterisk_offer_dashboard_and_trunk() { + local EA_DIR="$1" + + # Only available when run through the full repo's setup.sh (which + # sources every services/*.sh file, including these two) — a standalone + # `sudo bash asterisk.sh` copy has neither function defined at all. + if ! declare -F install_security-dashboard >/dev/null 2>&1 && ! declare -F install_pstn-trunk >/dev/null 2>&1; then + log_info "Run this from the full ubuntu-post-install repo (not a standalone copy) to also" + log_info "get prompts here for the Security Dashboard and a PSTN trunk — skipping both." + return 0 + fi + + if declare -F install_security-dashboard >/dev/null 2>&1; then + echo "" + if [[ -f "$DOCKER_DIR/security-dashboard/app.py" ]]; then + log_info "Security Dashboard already installed — refreshing it too..." + install_security-dashboard + else + local _WANT_DASH="" + prompt_yn "Set up the Security Dashboard (Security Log, Extensions, Asterisk Admin, PSTN Trunk, CrowdSec — one page)? (y/n):" "y" _WANT_DASH + [[ "$_WANT_DASH" =~ ^[Yy]$ ]] && install_security-dashboard + fi + fi + + if declare -F install_pstn-trunk >/dev/null 2>&1; then + echo "" + if [[ -f "$EA_DIR/config/asterisk/pstn-trunk-dialplan.conf" ]]; then + log_info "PSTN trunk already configured — refreshing it too..." + install_pstn-trunk + else + local _WANT_TRUNK="" + prompt_yn "Configure a real SIP/PSTN trunk (actual outside phone numbers, e.g. Anveo Direct/VoIP.ms)? (y/n):" "n" _WANT_TRUNK + [[ "$_WANT_TRUNK" =~ ^[Yy]$ ]] && install_pstn-trunk + fi + fi +} + # ── Shared: docker-compose.yml ───────────────────────────────────────────── # Same reasoning as above — one copy of the template used by both fresh # installs and updates. Must be called with $PWD already at $EA_DIR. @@ -700,6 +747,9 @@ install_asterisk() { echo "[DRY-RUN] Would offer optional ntfy alerts on extension registration going offline/online" echo "[DRY-RUN] (checked every 2 minutes via systemd timer, cron.d fallback; always asked," echo "[DRY-RUN] update mode included)" + echo "[DRY-RUN] Would offer to also set up the Security Dashboard and a PSTN trunk in this" + echo "[DRY-RUN] same run (calling services/security-dashboard.sh / services/pstn-trunk.sh" + echo "[DRY-RUN] directly — both stay independently invocable via their own service name too)" return 0 fi @@ -737,6 +787,7 @@ install_asterisk() { fi _asterisk_run_presence_step "$EA_DIR" + _asterisk_offer_dashboard_and_trunk "$EA_DIR" local _EXISTING_DOMAIN _EXISTING_PORT _EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)" @@ -1041,6 +1092,8 @@ MD || log_warning "Start failed — check: docker compose logs" fi + _asterisk_offer_dashboard_and_trunk "$EA_DIR" + # ── Summary ─────────────────────────────────────────────────────────────── echo "" log_success "Easy Asterisk installed at $EA_DIR"