Files
n8n-install/scripts/generate_welcome_page.sh
Yury Kossakovsky 26485b32c0 feat(gost): require upstream proxy for geo-bypass
gost now always requires an external upstream proxy to function.
wizard prompts for upstream proxy url when gost is selected.
if no upstream provided, gost is removed from selection.
2025-12-20 15:21:27 -07:00

529 lines
15 KiB
Bash
Executable File

#!/bin/bash
# Generate data.json for the welcome page with active services and credentials
set -e
# Source the utilities file and initialize paths
source "$(dirname "$0")/utils.sh"
init_paths
OUTPUT_FILE="$PROJECT_ROOT/welcome/data.json"
# Load environment variables from .env file
load_env || exit 1
# Ensure welcome directory exists
mkdir -p "$PROJECT_ROOT/welcome"
# Remove existing data.json if it exists (always regenerate)
if [ -f "$OUTPUT_FILE" ]; then
rm -f "$OUTPUT_FILE"
fi
# Start building JSON
GENERATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Build services array - each entry is a formatted JSON block
declare -a SERVICES_ARRAY
# n8n
if is_profile_active "n8n"; then
N8N_WORKER_COUNT_VAL="${N8N_WORKER_COUNT:-1}"
SERVICES_ARRAY+=(" \"n8n\": {
\"hostname\": \"$(json_escape "$N8N_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create your account on first login\"
},
\"extra\": {
\"workers\": \"$N8N_WORKER_COUNT_VAL\"
}
}")
fi
# Flowise
if is_profile_active "flowise"; then
SERVICES_ARRAY+=(" \"flowise\": {
\"hostname\": \"$(json_escape "$FLOWISE_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create your account on first login\"
}
}")
fi
# Open WebUI
if is_profile_active "open-webui"; then
SERVICES_ARRAY+=(" \"open-webui\": {
\"hostname\": \"$(json_escape "$WEBUI_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create account on first login\"
}
}")
fi
# Grafana (monitoring)
if is_profile_active "monitoring"; then
SERVICES_ARRAY+=(" \"grafana\": {
\"hostname\": \"$(json_escape "$GRAFANA_HOSTNAME")\",
\"credentials\": {
\"username\": \"admin\",
\"password\": \"$(json_escape "$GRAFANA_ADMIN_PASSWORD")\"
}
}")
SERVICES_ARRAY+=(" \"prometheus\": {
\"hostname\": \"$(json_escape "$PROMETHEUS_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$PROMETHEUS_USERNAME")\",
\"password\": \"$(json_escape "$PROMETHEUS_PASSWORD")\"
}
}")
fi
# Portainer
if is_profile_active "portainer"; then
SERVICES_ARRAY+=(" \"portainer\": {
\"hostname\": \"$(json_escape "$PORTAINER_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create admin account on first login\"
}
}")
fi
# Postgresus
if is_profile_active "postgresus"; then
SERVICES_ARRAY+=(" \"postgresus\": {
\"hostname\": \"$(json_escape "$POSTGRESUS_HOSTNAME")\",
\"credentials\": {
\"note\": \"PostgreSQL credentials are shown in the PostgreSQL card\"
}
}")
fi
# Langfuse
if is_profile_active "langfuse"; then
SERVICES_ARRAY+=(" \"langfuse\": {
\"hostname\": \"$(json_escape "$LANGFUSE_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$LANGFUSE_INIT_USER_EMAIL")\",
\"password\": \"$(json_escape "$LANGFUSE_INIT_USER_PASSWORD")\"
}
}")
fi
# Supabase
if is_profile_active "supabase"; then
SERVICES_ARRAY+=(" \"supabase\": {
\"hostname\": \"$(json_escape "$SUPABASE_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$DASHBOARD_USERNAME")\",
\"password\": \"$(json_escape "$DASHBOARD_PASSWORD")\"
},
\"extra\": {
\"internal_api\": \"http://kong:8000\",
\"service_role_key\": \"$(json_escape "$SERVICE_ROLE_KEY")\"
}
}")
fi
# Dify
if is_profile_active "dify"; then
SERVICES_ARRAY+=(" \"dify\": {
\"hostname\": \"$(json_escape "$DIFY_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create account on first login\"
},
\"extra\": {
\"api_endpoint\": \"https://$(json_escape "$DIFY_HOSTNAME")/v1\",
\"internal_api\": \"http://dify-api:5001\"
}
}")
fi
# Qdrant
if is_profile_active "qdrant"; then
SERVICES_ARRAY+=(" \"qdrant\": {
\"hostname\": \"$(json_escape "$QDRANT_HOSTNAME")\",
\"credentials\": {
\"api_key\": \"$(json_escape "$QDRANT_API_KEY")\"
},
\"extra\": {
\"dashboard\": \"https://$(json_escape "$QDRANT_HOSTNAME")/dashboard\",
\"internal_api\": \"http://qdrant:6333\"
}
}")
fi
# Weaviate
if is_profile_active "weaviate"; then
SERVICES_ARRAY+=(" \"weaviate\": {
\"hostname\": \"$(json_escape "$WEAVIATE_HOSTNAME")\",
\"credentials\": {
\"api_key\": \"$(json_escape "$WEAVIATE_API_KEY")\",
\"username\": \"$(json_escape "$WEAVIATE_USERNAME")\"
}
}")
fi
# Neo4j
if is_profile_active "neo4j"; then
SERVICES_ARRAY+=(" \"neo4j\": {
\"hostname\": \"$(json_escape "$NEO4J_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$NEO4J_AUTH_USERNAME")\",
\"password\": \"$(json_escape "$NEO4J_AUTH_PASSWORD")\"
},
\"extra\": {
\"bolt_port\": \"7687\"
}
}")
fi
# SearXNG
if is_profile_active "searxng"; then
SERVICES_ARRAY+=(" \"searxng\": {
\"hostname\": \"$(json_escape "$SEARXNG_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$SEARXNG_USERNAME")\",
\"password\": \"$(json_escape "$SEARXNG_PASSWORD")\"
}
}")
fi
# RAGApp
if is_profile_active "ragapp"; then
SERVICES_ARRAY+=(" \"ragapp\": {
\"hostname\": \"$(json_escape "$RAGAPP_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$RAGAPP_USERNAME")\",
\"password\": \"$(json_escape "$RAGAPP_PASSWORD")\"
},
\"extra\": {
\"admin\": \"https://$(json_escape "$RAGAPP_HOSTNAME")/admin\",
\"docs\": \"https://$(json_escape "$RAGAPP_HOSTNAME")/docs\",
\"internal_api\": \"http://ragapp:8000\"
}
}")
fi
# RAGFlow
if is_profile_active "ragflow"; then
SERVICES_ARRAY+=(" \"ragflow\": {
\"hostname\": \"$(json_escape "$RAGFLOW_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create account on first login\"
},
\"extra\": {
\"internal_api\": \"http://ragflow:80\"
}
}")
fi
# LightRAG
if is_profile_active "lightrag"; then
SERVICES_ARRAY+=(" \"lightrag\": {
\"hostname\": \"$(json_escape "$LIGHTRAG_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$LIGHTRAG_USERNAME")\",
\"password\": \"$(json_escape "$LIGHTRAG_PASSWORD")\",
\"api_key\": \"$(json_escape "$LIGHTRAG_API_KEY")\"
},
\"extra\": {
\"docs\": \"https://$(json_escape "$LIGHTRAG_HOSTNAME")/docs\",
\"internal_api\": \"http://lightrag:9621\"
}
}")
fi
# Letta
if is_profile_active "letta"; then
SERVICES_ARRAY+=(" \"letta\": {
\"hostname\": \"$(json_escape "$LETTA_HOSTNAME")\",
\"credentials\": {
\"api_key\": \"$(json_escape "$LETTA_SERVER_PASSWORD")\"
}
}")
fi
# ComfyUI
if is_profile_active "comfyui"; then
SERVICES_ARRAY+=(" \"comfyui\": {
\"hostname\": \"$(json_escape "$COMFYUI_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$COMFYUI_USERNAME")\",
\"password\": \"$(json_escape "$COMFYUI_PASSWORD")\"
}
}")
fi
# LibreTranslate
if is_profile_active "libretranslate"; then
SERVICES_ARRAY+=(" \"libretranslate\": {
\"hostname\": \"$(json_escape "$LT_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$LT_USERNAME")\",
\"password\": \"$(json_escape "$LT_PASSWORD")\"
},
\"extra\": {
\"internal_api\": \"http://libretranslate:5000\"
}
}")
fi
# Docling
if is_profile_active "docling"; then
SERVICES_ARRAY+=(" \"docling\": {
\"hostname\": \"$(json_escape "$DOCLING_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$DOCLING_USERNAME")\",
\"password\": \"$(json_escape "$DOCLING_PASSWORD")\"
},
\"extra\": {
\"ui\": \"https://$(json_escape "$DOCLING_HOSTNAME")/ui\",
\"docs\": \"https://$(json_escape "$DOCLING_HOSTNAME")/docs\",
\"internal_api\": \"http://docling:5001\"
}
}")
fi
# PaddleOCR
if is_profile_active "paddleocr"; then
SERVICES_ARRAY+=(" \"paddleocr\": {
\"hostname\": \"$(json_escape "$PADDLEOCR_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$PADDLEOCR_USERNAME")\",
\"password\": \"$(json_escape "$PADDLEOCR_PASSWORD")\"
},
\"extra\": {
\"internal_api\": \"http://paddleocr:8080\"
}
}")
fi
# Postiz
if is_profile_active "postiz"; then
SERVICES_ARRAY+=(" \"postiz\": {
\"hostname\": \"$(json_escape "$POSTIZ_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create account on first login\"
},
\"extra\": {
\"internal_api\": \"http://postiz:5000\"
}
}")
fi
# WAHA
if is_profile_active "waha"; then
SERVICES_ARRAY+=(" \"waha\": {
\"hostname\": \"$(json_escape "$WAHA_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$WAHA_DASHBOARD_USERNAME")\",
\"password\": \"$(json_escape "$WAHA_DASHBOARD_PASSWORD")\",
\"api_key\": \"$(json_escape "$WAHA_API_KEY_PLAIN")\"
},
\"extra\": {
\"dashboard\": \"https://$(json_escape "$WAHA_HOSTNAME")/dashboard\",
\"swagger_user\": \"$(json_escape "$WHATSAPP_SWAGGER_USERNAME")\",
\"swagger_pass\": \"$(json_escape "$WHATSAPP_SWAGGER_PASSWORD")\",
\"internal_api\": \"http://waha:3000\"
}
}")
fi
# Crawl4AI (internal only)
if is_profile_active "crawl4ai"; then
SERVICES_ARRAY+=(" \"crawl4ai\": {
\"hostname\": null,
\"credentials\": {
\"note\": \"Internal service only\"
},
\"extra\": {
\"internal_api\": \"http://crawl4ai:11235\"
}
}")
fi
# Gotenberg (internal only)
if is_profile_active "gotenberg"; then
SERVICES_ARRAY+=(" \"gotenberg\": {
\"hostname\": null,
\"credentials\": {
\"note\": \"Internal service only\"
},
\"extra\": {
\"internal_api\": \"http://gotenberg:3000\",
\"docs\": \"https://gotenberg.dev/docs\"
}
}")
fi
# Ollama (internal only)
if is_profile_active "cpu" || is_profile_active "gpu-nvidia" || is_profile_active "gpu-amd"; then
SERVICES_ARRAY+=(" \"ollama\": {
\"hostname\": null,
\"credentials\": {
\"note\": \"Internal service only\"
},
\"extra\": {
\"internal_api\": \"http://ollama:11434\"
}
}")
fi
# Redis/Valkey (internal only, shown if n8n or langfuse active)
if is_profile_active "n8n" || is_profile_active "langfuse"; then
SERVICES_ARRAY+=(" \"redis\": {
\"hostname\": null,
\"credentials\": {
\"password\": \"$(json_escape "$REDIS_AUTH")\"
},
\"extra\": {
\"internal_url\": \"${REDIS_HOST:-redis}:${REDIS_PORT:-6379}\"
}
}")
fi
# PostgreSQL (internal only, shown if n8n or langfuse active)
if is_profile_active "n8n" || is_profile_active "langfuse"; then
SERVICES_ARRAY+=(" \"postgres\": {
\"hostname\": null,
\"credentials\": {
\"username\": \"$(json_escape "${POSTGRES_USER:-postgres}")\",
\"password\": \"$(json_escape "$POSTGRES_PASSWORD")\"
},
\"extra\": {
\"internal_host\": \"postgres\",
\"internal_port\": \"${POSTGRES_PORT:-5432}\",
\"database\": \"$(json_escape "${POSTGRES_DB:-postgres}")\"
}
}")
fi
# Python Runner (internal only)
if is_profile_active "python-runner"; then
SERVICES_ARRAY+=(" \"python-runner\": {
\"hostname\": null,
\"credentials\": {
\"note\": \"Mount: ./python-runner → /app\\nEntry: /app/main.py\\nLogs: make logs s=python-runner\"
}
}")
fi
# Cloudflare Tunnel
if is_profile_active "cloudflare-tunnel"; then
SERVICES_ARRAY+=(" \"cloudflare-tunnel\": {
\"hostname\": null,
\"credentials\": {
\"note\": \"Zero-trust access via Cloudflare network\"
},
\"extra\": {
\"recommendation\": \"Close ports 80, 443, 7687 in your VPS firewall after confirming tunnel connectivity\"
}
}")
fi
# Gost Proxy (internal only)
if is_profile_active "gost"; then
SERVICES_ARRAY+=(" \"gost\": {
\"hostname\": null,
\"credentials\": {
\"username\": \"$(json_escape "$GOST_USERNAME")\",
\"password\": \"$(json_escape "$GOST_PASSWORD")\"
},
\"extra\": {
\"note\": \"Routes AI traffic through external proxy for geo-bypass\",
\"proxy_url\": \"$(json_escape "$GOST_PROXY_URL")\",
\"upstream_proxy\": \"$(json_escape "$GOST_UPSTREAM_PROXY")\",
\"internal_api\": \"http://gost:8080\"
}
}")
fi
# Join array with commas and newlines
SERVICES_JSON=""
for i in "${!SERVICES_ARRAY[@]}"; do
if [ $i -gt 0 ]; then
SERVICES_JSON+=",
"
fi
SERVICES_JSON+="${SERVICES_ARRAY[$i]}"
done
# Build quick_start array based on active profiles
declare -a QUICK_START_ARRAY
STEP_NUM=1
# Step 1: Log into primary service (n8n or Flowise)
if is_profile_active "n8n"; then
QUICK_START_ARRAY+=(" {
\"step\": $STEP_NUM,
\"title\": \"Log into n8n\",
\"description\": \"Create your account on first login\"
}")
((STEP_NUM++))
elif is_profile_active "flowise"; then
QUICK_START_ARRAY+=(" {
\"step\": $STEP_NUM,
\"title\": \"Log into Flowise\",
\"description\": \"Create your account on first login\"
}")
((STEP_NUM++))
fi
# Step 2: Create first workflow (if n8n active)
if is_profile_active "n8n"; then
QUICK_START_ARRAY+=(" {
\"step\": $STEP_NUM,
\"title\": \"Create your first workflow\",
\"description\": \"Start with Manual Trigger + HTTP Request nodes\"
}")
((STEP_NUM++))
fi
# Step 3: Configure database backups (if postgresus active)
if is_profile_active "postgresus"; then
QUICK_START_ARRAY+=(" {
\"step\": $STEP_NUM,
\"title\": \"Configure database backups\",
\"description\": \"Set up Postgresus for automated PostgreSQL backups\"
}")
((STEP_NUM++))
fi
# Step 4: Monitor system (if monitoring active)
if is_profile_active "monitoring"; then
QUICK_START_ARRAY+=(" {
\"step\": $STEP_NUM,
\"title\": \"Monitor your system\",
\"description\": \"Use Grafana to track performance metrics\"
}")
((STEP_NUM++))
fi
# Join quick_start array
QUICK_START_JSON=""
for i in "${!QUICK_START_ARRAY[@]}"; do
if [ $i -gt 0 ]; then
QUICK_START_JSON+=",
"
fi
QUICK_START_JSON+="${QUICK_START_ARRAY[$i]}"
done
# Write final JSON with proper formatting
cat > "$OUTPUT_FILE" << EOF
{
"domain": "$(json_escape "$USER_DOMAIN_NAME")",
"generated_at": "$GENERATED_AT",
"services": {
$SERVICES_JSON
},
"quick_start": [
$QUICK_START_JSON
]
}
EOF
log_success "Welcome page data generated at: $OUTPUT_FILE"
log_info "Access it at: https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"