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:
parent
72dbd0189c
commit
849dea7ce2
186
config.sh
186
config.sh
@ -877,9 +877,15 @@ configure_pangolin() {
|
|||||||
read -rp " Pangolin API key: " pang_key
|
read -rp " Pangolin API key: " pang_key
|
||||||
read -rp " Pangolin Organization ID: " pang_org
|
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_URL" "$pang_url"
|
||||||
update_env_var "PANGOLIN_API_KEY" "$pang_key"
|
update_env_var "PANGOLIN_API_KEY" "$pang_key"
|
||||||
update_env_var "PANGOLIN_ORG_ID" "$pang_org"
|
update_env_var "PANGOLIN_ORG_ID" "$pang_org"
|
||||||
|
update_env_var "PANGOLIN_ENDPOINT" "$pang_endpoint"
|
||||||
|
|
||||||
success "Pangolin API credentials saved"
|
success "Pangolin API credentials saved"
|
||||||
|
|
||||||
@ -896,7 +902,7 @@ configure_pangolin() {
|
|||||||
local health_status
|
local health_status
|
||||||
health_status=$(curl -s -o /dev/null -w "%{http_code}" -m 10 \
|
health_status=$(curl -s -o /dev/null -w "%{http_code}" -m 10 \
|
||||||
-H "Authorization: Bearer $pang_key" \
|
-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
|
if [[ "$health_status" != "200" ]]; then
|
||||||
warn "Could not reach Pangolin API (HTTP $health_status)."
|
warn "Could not reach Pangolin API (HTTP $health_status)."
|
||||||
@ -919,8 +925,8 @@ configure_pangolin() {
|
|||||||
site_choice=${site_choice:-3}
|
site_choice=${site_choice:-3}
|
||||||
|
|
||||||
case "$site_choice" in
|
case "$site_choice" in
|
||||||
1) pangolin_create_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" ;;
|
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."
|
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
|
"${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() {
|
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 domain="${CONFIGURED_DOMAIN:-cmlite.org}"
|
||||||
local site_name
|
local site_name
|
||||||
|
|
||||||
@ -993,22 +1113,19 @@ pangolin_create_site() {
|
|||||||
|
|
||||||
success "Site created: $site_id ($site_name)"
|
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
|
# Write credentials to .env
|
||||||
update_env_var "PANGOLIN_SITE_ID" "$site_id"
|
update_env_var "PANGOLIN_SITE_ID" "$site_id"
|
||||||
update_env_var "PANGOLIN_NEWT_ID" "$newt_id"
|
update_env_var "PANGOLIN_NEWT_ID" "$newt_id"
|
||||||
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret"
|
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret"
|
||||||
update_env_var "PANGOLIN_ENDPOINT" "$endpoint"
|
|
||||||
|
|
||||||
success "Tunnel credentials written to .env"
|
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() {
|
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..."
|
info "Fetching sites from organization..."
|
||||||
local sites_resp
|
local sites_resp
|
||||||
@ -1066,37 +1183,50 @@ pangolin_connect_site() {
|
|||||||
sel_id=$(echo "$selected" | jq -r '.siteId')
|
sel_id=$(echo "$selected" | jq -r '.siteId')
|
||||||
sel_name=$(echo "$selected" | jq -r '.name')
|
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
|
# Write site ID to .env
|
||||||
update_env_var "PANGOLIN_SITE_ID" "$sel_id"
|
update_env_var "PANGOLIN_SITE_ID" "$sel_id"
|
||||||
update_env_var "PANGOLIN_ENDPOINT" "$endpoint"
|
|
||||||
|
|
||||||
success "Connected to site: $sel_name (ID: $sel_id)"
|
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
|
local existing_newt_id
|
||||||
existing_newt_id=$(grep "^PANGOLIN_NEWT_ID=" "$ENV_FILE" 2>/dev/null | cut -d= -f2-)
|
existing_newt_id=$(grep "^PANGOLIN_NEWT_ID=" "$ENV_FILE" 2>/dev/null | cut -d= -f2-)
|
||||||
|
|
||||||
if [[ -z "$existing_newt_id" ]]; then
|
if [[ -z "$existing_newt_id" ]]; then
|
||||||
info "Newt credentials not yet set."
|
info "Fetching Newt credentials for this site..."
|
||||||
info "If you have the Newt ID and Secret for this site, enter them now."
|
|
||||||
info "Otherwise, set them later via the admin GUI."
|
# Try pickSiteDefaults for fresh Newt creds
|
||||||
echo ""
|
local defaults_resp
|
||||||
read -rp " Newt ID (leave blank to skip): " newt_id_input
|
defaults_resp=$(pangolin_api GET "$api_url/org/$org_id/pick-site-defaults" "$api_key")
|
||||||
if [[ -n "$newt_id_input" ]]; then
|
local newt_id newt_secret
|
||||||
read -rp " Newt Secret: " newt_secret_input
|
newt_id=$(echo "$defaults_resp" | jq -r '.data.newtId // .newtId // empty' 2>/dev/null)
|
||||||
if [[ -n "$newt_secret_input" ]]; then
|
newt_secret=$(echo "$defaults_resp" | jq -r '.data.newtSecret // .newtSecret // .data.secret // .secret // empty' 2>/dev/null)
|
||||||
update_env_var "PANGOLIN_NEWT_ID" "$newt_id_input"
|
|
||||||
update_env_var "PANGOLIN_NEWT_SECRET" "$newt_secret_input"
|
if [[ -n "$newt_id" && -n "$newt_secret" ]]; then
|
||||||
success "Newt credentials written to .env"
|
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
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
info "Existing Newt credentials found in .env (newtId: $existing_newt_id)"
|
info "Existing Newt credentials found in .env (newtId: $existing_newt_id)"
|
||||||
fi
|
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() {
|
configure_control_panel() {
|
||||||
|
|||||||
@ -914,7 +914,8 @@ services:
|
|||||||
image: ${GITEA_REGISTRY:-gitea.bnkops.com/admin}/mongo:6.0
|
image: ${GITEA_REGISTRY:-gitea.bnkops.com/admin}/mongo:6.0
|
||||||
container_name: mongodb-rocketchat
|
container_name: mongodb-rocketchat
|
||||||
restart: unless-stopped
|
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:
|
environment:
|
||||||
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-rocketchat}
|
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-rocketchat}
|
||||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD must be set in .env}
|
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD must be set in .env}
|
||||||
|
|||||||
@ -940,7 +940,8 @@ services:
|
|||||||
image: mongo:6.0
|
image: mongo:6.0
|
||||||
container_name: mongodb-rocketchat
|
container_name: mongodb-rocketchat
|
||||||
restart: unless-stopped
|
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:
|
environment:
|
||||||
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-rocketchat}
|
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-rocketchat}
|
||||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD must be set in .env}
|
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?MONGO_ROOT_PASSWORD must be set in .env}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user