Fix config.sh Pangolin setup and MongoDB init for fresh deployments

- Fix Pangolin endpoint: ask separately from API URL (different hostnames)
- Add pangolin_create_resources() to create resources + targets during setup
- Set all resources as public (no SSO/blockAccess) automatically
- Fix API health check URL to use actual org endpoint
- Fix MongoDB entrypoint: delegate to docker-entrypoint.sh so INITDB user
  creation works on fresh volumes (was bypassing Docker's init sequence)

Bunker Admin
This commit is contained in:
bunker-admin 2026-04-09 11:43:13 -06:00
parent 72dbd0189c
commit 849dea7ce2
3 changed files with 162 additions and 30 deletions

186
config.sh
View File

@ -877,9 +877,15 @@ configure_pangolin() {
read -rp " Pangolin API key: " pang_key
read -rp " Pangolin Organization ID: " pang_org
# The Pangolin endpoint (dashboard/Newt WebSocket URL) may differ from the API URL.
# For example: API at api.example.org vs dashboard at pangolin.example.org
read -rp " Pangolin Endpoint (dashboard/Newt URL) [default: https://pangolin.bnkserve.org]: " pang_endpoint
pang_endpoint=${pang_endpoint:-https://pangolin.bnkserve.org}
update_env_var "PANGOLIN_API_URL" "$pang_url"
update_env_var "PANGOLIN_API_KEY" "$pang_key"
update_env_var "PANGOLIN_ORG_ID" "$pang_org"
update_env_var "PANGOLIN_ENDPOINT" "$pang_endpoint"
success "Pangolin API credentials saved"
@ -896,7 +902,7 @@ configure_pangolin() {
local health_status
health_status=$(curl -s -o /dev/null -w "%{http_code}" -m 10 \
-H "Authorization: Bearer $pang_key" \
"$pang_url/" 2>/dev/null) || true
"$pang_url/org/$pang_org/sites" 2>/dev/null) || true
if [[ "$health_status" != "200" ]]; then
warn "Could not reach Pangolin API (HTTP $health_status)."
@ -919,8 +925,8 @@ configure_pangolin() {
site_choice=${site_choice:-3}
case "$site_choice" in
1) pangolin_create_site "$pang_url" "$pang_key" "$pang_org" ;;
2) pangolin_connect_site "$pang_url" "$pang_key" "$pang_org" ;;
1) pangolin_create_site "$pang_url" "$pang_key" "$pang_org" "$pang_endpoint" ;;
2) pangolin_connect_site "$pang_url" "$pang_key" "$pang_org" "$pang_endpoint" ;;
*)
info "Complete tunnel setup in the admin GUI at /app/pangolin after starting services."
;;
@ -943,8 +949,122 @@ pangolin_api() {
"${body_args[@]}" 2>/dev/null
}
pangolin_create_resources() {
local api_url=$1 api_key=$2 org_id=$3 site_id=$4 domain=$5
info "Creating Pangolin resources and targets for $domain..."
# Look up the domain ID from registered domains
local domains_resp domain_id
domains_resp=$(pangolin_api GET "$api_url/org/$org_id/domains" "$api_key")
domain_id=$(echo "$domains_resp" | jq -r --arg d "$domain" \
'.data.domains[]? | select(.baseDomain == $d) | .domainId' 2>/dev/null)
if [[ -z "$domain_id" ]]; then
warn "Domain '$domain' not found in Pangolin. Register it first in the dashboard."
info "After registering the domain, run the sync from the admin GUI at /app/pangolin."
return
fi
success "Found domain: $domain (ID: $domain_id)"
# Resource definitions: subdomain|name
# Empty subdomain = root domain
local -a resource_defs=(
"app|Admin GUI"
"api|API Server"
"|Public Site"
"db|NocoDB"
"docs|Documentation"
"code|Code Server"
"n8n|Workflows"
"git|Gitea"
"home|Homepage"
"listmonk|Newsletter"
"qr|Mini QR"
"draw|Excalidraw"
"vault|Vaultwarden"
"mail|MailHog"
"chat|Rocket.Chat"
"events|Gancio Events"
"meet|Jitsi Meet"
"grafana|Grafana"
)
local created=0 skipped=0 failed=0
for def in "${resource_defs[@]}"; do
local subdomain="${def%%|*}"
local name="${def#*|}"
local full_domain
if [[ -n "$subdomain" ]]; then
full_domain="$subdomain.$domain"
else
full_domain="$domain"
fi
# Build create payload — omit subdomain entirely for root domain (Pangolin rejects empty string)
local create_payload
if [[ -n "$subdomain" ]]; then
create_payload=$(jq -n \
--arg name "$name" \
--arg domainId "$domain_id" \
--arg subdomain "$subdomain" \
'{name: $name, domainId: $domainId, subdomain: $subdomain, http: true, protocol: "tcp"}')
else
create_payload=$(jq -n \
--arg name "$name" \
--arg domainId "$domain_id" \
'{name: $name, domainId: $domainId, http: true, protocol: "tcp"}')
fi
# Create the resource
local res_resp
res_resp=$(pangolin_api PUT "$api_url/org/$org_id/resource" "$api_key" "$create_payload")
local resource_id
resource_id=$(echo "$res_resp" | jq -r '.data.resourceId // empty' 2>/dev/null)
if [[ -z "$resource_id" ]]; then
local err_msg
err_msg=$(echo "$res_resp" | jq -r '.message // "unknown error"' 2>/dev/null)
if echo "$err_msg" | grep -qi "already exists\|duplicate\|conflict"; then
((skipped++))
else
warn " Failed to create $full_domain: $err_msg"
((failed++))
fi
continue
fi
# Create target pointing to nginx:80
local target_payload
target_payload=$(jq -n \
--argjson siteId "$site_id" \
'{siteId: $siteId, ip: "nginx", port: 80, method: "http", enabled: true}')
pangolin_api PUT "$api_url/resource/$resource_id/target" "$api_key" "$target_payload" >/dev/null
# Set resource as public (no SSO, no access block)
pangolin_api POST "$api_url/resource/$resource_id" "$api_key" \
'{"sso":false,"blockAccess":false}' >/dev/null
((created++))
done
if [[ $created -gt 0 ]]; then
success "Created $created resources with targets → nginx:80"
fi
if [[ $skipped -gt 0 ]]; then
info "$skipped resources already existed (skipped)"
fi
if [[ $failed -gt 0 ]]; then
warn "$failed resources failed to create"
fi
}
pangolin_create_site() {
local api_url=$1 api_key=$2 org_id=$3
local api_url=$1 api_key=$2 org_id=$3 endpoint=$4
local domain="${CONFIGURED_DOMAIN:-cmlite.org}"
local site_name
@ -993,22 +1113,19 @@ pangolin_create_site() {
success "Site created: $site_id ($site_name)"
# Derive endpoint from API URL (strip /v1 path)
local endpoint
endpoint=$(echo "$api_url" | sed 's|/v1/*$||')
# Write credentials to .env
update_env_var "PANGOLIN_SITE_ID" "$site_id"
update_env_var "PANGOLIN_NEWT_ID" "$newt_id"
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret"
update_env_var "PANGOLIN_ENDPOINT" "$endpoint"
success "Tunnel credentials written to .env"
info "Resources will be created automatically via the admin GUI or sync endpoint."
# Create resources and targets
pangolin_create_resources "$api_url" "$api_key" "$org_id" "$site_id" "$domain"
}
pangolin_connect_site() {
local api_url=$1 api_key=$2 org_id=$3
local api_url=$1 api_key=$2 org_id=$3 endpoint=$4
info "Fetching sites from organization..."
local sites_resp
@ -1066,37 +1183,50 @@ pangolin_connect_site() {
sel_id=$(echo "$selected" | jq -r '.siteId')
sel_name=$(echo "$selected" | jq -r '.name')
# Derive endpoint from API URL
local endpoint
endpoint=$(echo "$api_url" | sed 's|/v1/*$||')
# Write site ID to .env
update_env_var "PANGOLIN_SITE_ID" "$sel_id"
update_env_var "PANGOLIN_ENDPOINT" "$endpoint"
success "Connected to site: $sel_name (ID: $sel_id)"
# Check if we also need Newt credentials
# Fetch Newt credentials for this site from the API
local existing_newt_id
existing_newt_id=$(grep "^PANGOLIN_NEWT_ID=" "$ENV_FILE" 2>/dev/null | cut -d= -f2-)
if [[ -z "$existing_newt_id" ]]; then
info "Newt credentials not yet set."
info "If you have the Newt ID and Secret for this site, enter them now."
info "Otherwise, set them later via the admin GUI."
echo ""
read -rp " Newt ID (leave blank to skip): " newt_id_input
if [[ -n "$newt_id_input" ]]; then
read -rp " Newt Secret: " newt_secret_input
if [[ -n "$newt_secret_input" ]]; then
update_env_var "PANGOLIN_NEWT_ID" "$newt_id_input"
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret_input"
success "Newt credentials written to .env"
info "Fetching Newt credentials for this site..."
# Try pickSiteDefaults for fresh Newt creds
local defaults_resp
defaults_resp=$(pangolin_api GET "$api_url/org/$org_id/pick-site-defaults" "$api_key")
local newt_id newt_secret
newt_id=$(echo "$defaults_resp" | jq -r '.data.newtId // .newtId // empty' 2>/dev/null)
newt_secret=$(echo "$defaults_resp" | jq -r '.data.newtSecret // .newtSecret // .data.secret // .secret // empty' 2>/dev/null)
if [[ -n "$newt_id" && -n "$newt_secret" ]]; then
update_env_var "PANGOLIN_NEWT_ID" "$newt_id"
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret"
success "Newt credentials fetched and saved (newtId: $newt_id)"
else
info "Could not auto-fetch Newt credentials."
info "Enter them manually (from your Pangolin dashboard)."
echo ""
read -rp " Newt ID (leave blank to skip): " newt_id_input
if [[ -n "$newt_id_input" ]]; then
read -rp " Newt Secret: " newt_secret_input
if [[ -n "$newt_secret_input" ]]; then
update_env_var "PANGOLIN_NEWT_ID" "$newt_id_input"
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret_input"
success "Newt credentials written to .env"
fi
fi
fi
else
info "Existing Newt credentials found in .env (newtId: $existing_newt_id)"
fi
# Create resources and targets
local domain="${CONFIGURED_DOMAIN:-cmlite.org}"
pangolin_create_resources "$api_url" "$api_key" "$org_id" "$sel_id" "$domain"
}
configure_control_panel() {

View File

@ -914,7 +914,8 @@ services:
image: ${GITEA_REGISTRY:-gitea.bnkops.com/admin}/mongo:6.0
container_name: mongodb-rocketchat
restart: unless-stopped
entrypoint: ["/bin/bash", "-c", "if [ ! -f /data/replica.key ]; then openssl rand -base64 756 > /data/replica.key; fi && chmod 400 /data/replica.key && chown 999:999 /data/replica.key && exec mongod --replSet rs0 --bind_ip_all --auth --keyFile /data/replica.key"]
# Generate keyfile then delegate to Docker's standard entrypoint (creates INITDB user)
entrypoint: ["/bin/bash", "-c", "if [ ! -f /data/replica.key ]; then openssl rand -base64 756 > /data/replica.key; fi && chmod 400 /data/replica.key && chown 999:999 /data/replica.key && exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --keyFile /data/replica.key"]
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-rocketchat}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD must be set in .env}

View File

@ -940,7 +940,8 @@ services:
image: mongo:6.0
container_name: mongodb-rocketchat
restart: unless-stopped
entrypoint: ["/bin/bash", "-c", "if [ ! -f /data/replica.key ]; then openssl rand -base64 756 > /data/replica.key; fi && chmod 400 /data/replica.key && chown 999:999 /data/replica.key && exec mongod --replSet rs0 --bind_ip_all --auth --keyFile /data/replica.key"]
# Generate keyfile then delegate to Docker's standard entrypoint (creates INITDB user)
entrypoint: ["/bin/bash", "-c", "if [ ! -f /data/replica.key ]; then openssl rand -base64 756 > /data/replica.key; fi && chmod 400 /data/replica.key && chown 999:999 /data/replica.key && exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --keyFile /data/replica.key"]
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-rocketchat}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD must be set in .env}