mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-07 14:23:08 +00:00
feat: add welcome page dashboard for post-install credentials
replace terminal-based final report with web-based welcome page that displays service credentials, hostnames, and quick start guide. - add welcome/index.html with tailwind css and dark mode support - add welcome/app.js with service metadata and password toggle/copy - add scripts/generate_welcome_page.sh to generate data.json from env - simplify 07_final_report.sh to show welcome page url and make commands - add welcome page basic auth credentials to caddy and secret generation - update add-new-service documentation with new welcome page steps
This commit is contained in:
@@ -62,6 +62,7 @@ declare -A VARS_TO_GENERATE=(
|
||||
["VAULT_ENC_KEY"]="alphanum:32"
|
||||
["WAHA_DASHBOARD_PASSWORD"]="password:32"
|
||||
["WEAVIATE_API_KEY"]="secret:48" # API Key for Weaviate service (36 bytes -> 48 chars base64)
|
||||
["WELCOME_PASSWORD"]="password:32" # Welcome page basic auth password
|
||||
["WHATSAPP_SWAGGER_PASSWORD"]="password:32"
|
||||
)
|
||||
|
||||
@@ -272,6 +273,7 @@ generated_values["LIGHTRAG_USERNAME"]="$USER_EMAIL" # Set LightRAG username for
|
||||
generated_values["WAHA_DASHBOARD_USERNAME"]="$USER_EMAIL" # WAHA dashboard username default
|
||||
generated_values["WHATSAPP_SWAGGER_USERNAME"]="$USER_EMAIL" # WAHA swagger username default
|
||||
generated_values["DOCLING_USERNAME"]="$USER_EMAIL" # Set Docling username for Caddy
|
||||
generated_values["WELCOME_USERNAME"]="$USER_EMAIL" # Set Welcome page username for Caddy
|
||||
|
||||
|
||||
# Create a temporary file for processing
|
||||
@@ -299,6 +301,7 @@ found_vars["DOCLING_USERNAME"]=0
|
||||
found_vars["LT_USERNAME"]=0
|
||||
found_vars["LIGHTRAG_USERNAME"]=0
|
||||
found_vars["WAHA_DASHBOARD_USERNAME"]=0
|
||||
found_vars["WELCOME_USERNAME"]=0
|
||||
found_vars["WHATSAPP_SWAGGER_USERNAME"]=0
|
||||
|
||||
# Read template, substitute domain, generate initial values
|
||||
@@ -346,7 +349,7 @@ while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# This 'else' block is for lines from template not covered by existing values or VARS_TO_GENERATE.
|
||||
# Check if it is one of the user input vars - these are handled by found_vars later if not in template.
|
||||
is_user_input_var=0 # Reset for each line
|
||||
user_input_vars=("FLOWISE_USERNAME" "DASHBOARD_USERNAME" "LETSENCRYPT_EMAIL" "RUN_N8N_IMPORT" "PROMETHEUS_USERNAME" "SEARXNG_USERNAME" "OPENAI_API_KEY" "LANGFUSE_INIT_USER_EMAIL" "N8N_WORKER_COUNT" "WEAVIATE_USERNAME" "NEO4J_AUTH_USERNAME" "COMFYUI_USERNAME" "RAGAPP_USERNAME" "PADDLEOCR_USERNAME" "LT_USERNAME" "LIGHTRAG_USERNAME" "WAHA_DASHBOARD_USERNAME" "WHATSAPP_SWAGGER_USERNAME")
|
||||
user_input_vars=("FLOWISE_USERNAME" "DASHBOARD_USERNAME" "LETSENCRYPT_EMAIL" "RUN_N8N_IMPORT" "PROMETHEUS_USERNAME" "SEARXNG_USERNAME" "OPENAI_API_KEY" "LANGFUSE_INIT_USER_EMAIL" "N8N_WORKER_COUNT" "WEAVIATE_USERNAME" "NEO4J_AUTH_USERNAME" "COMFYUI_USERNAME" "RAGAPP_USERNAME" "PADDLEOCR_USERNAME" "LT_USERNAME" "LIGHTRAG_USERNAME" "WAHA_DASHBOARD_USERNAME" "WELCOME_USERNAME" "WHATSAPP_SWAGGER_USERNAME")
|
||||
for uivar in "${user_input_vars[@]}"; do
|
||||
if [[ "$varName" == "$uivar" ]]; then
|
||||
is_user_input_var=1
|
||||
@@ -428,7 +431,7 @@ if [[ -z "${generated_values[SERVICE_ROLE_KEY]}" ]]; then
|
||||
fi
|
||||
|
||||
# Add any custom variables that weren't found in the template
|
||||
for var in "FLOWISE_USERNAME" "DASHBOARD_USERNAME" "LETSENCRYPT_EMAIL" "RUN_N8N_IMPORT" "OPENAI_API_KEY" "PROMETHEUS_USERNAME" "SEARXNG_USERNAME" "LANGFUSE_INIT_USER_EMAIL" "N8N_WORKER_COUNT" "WEAVIATE_USERNAME" "NEO4J_AUTH_USERNAME" "COMFYUI_USERNAME" "RAGAPP_USERNAME" "PADDLEOCR_USERNAME" "LT_USERNAME" "LIGHTRAG_USERNAME" "WAHA_DASHBOARD_USERNAME" "WHATSAPP_SWAGGER_USERNAME" "DOCLING_USERNAME"; do
|
||||
for var in "FLOWISE_USERNAME" "DASHBOARD_USERNAME" "LETSENCRYPT_EMAIL" "RUN_N8N_IMPORT" "OPENAI_API_KEY" "PROMETHEUS_USERNAME" "SEARXNG_USERNAME" "LANGFUSE_INIT_USER_EMAIL" "N8N_WORKER_COUNT" "WEAVIATE_USERNAME" "NEO4J_AUTH_USERNAME" "COMFYUI_USERNAME" "RAGAPP_USERNAME" "PADDLEOCR_USERNAME" "LT_USERNAME" "LIGHTRAG_USERNAME" "WAHA_DASHBOARD_USERNAME" "WELCOME_USERNAME" "WHATSAPP_SWAGGER_USERNAME" "DOCLING_USERNAME"; do
|
||||
if [[ ${found_vars["$var"]} -eq 0 && -v generated_values["$var"] ]]; then
|
||||
# Before appending, check if it's already in TMP_ENV_FILE to avoid duplicates
|
||||
if ! grep -q -E "^${var}=" "$TMP_ENV_FILE"; then
|
||||
@@ -618,6 +621,18 @@ if [[ -z "$FINAL_DOCLING_HASH" && -n "$DOCLING_PLAIN_PASS" ]]; then
|
||||
fi
|
||||
_update_or_add_env_var "DOCLING_PASSWORD_HASH" "$FINAL_DOCLING_HASH"
|
||||
|
||||
# --- WELCOME PAGE ---
|
||||
WELCOME_PLAIN_PASS="${generated_values["WELCOME_PASSWORD"]}"
|
||||
FINAL_WELCOME_HASH="${generated_values[WELCOME_PASSWORD_HASH]}"
|
||||
if [[ -z "$FINAL_WELCOME_HASH" && -n "$WELCOME_PLAIN_PASS" ]]; then
|
||||
NEW_HASH=$(_generate_and_get_hash "$WELCOME_PLAIN_PASS")
|
||||
if [[ -n "$NEW_HASH" ]]; then
|
||||
FINAL_WELCOME_HASH="$NEW_HASH"
|
||||
generated_values["WELCOME_PASSWORD_HASH"]="$NEW_HASH"
|
||||
fi
|
||||
fi
|
||||
_update_or_add_env_var "WELCOME_PASSWORD_HASH" "$FINAL_WELCOME_HASH"
|
||||
|
||||
if [ $? -eq 0 ]; then # This $? reflects the status of the last mv command from the last _update_or_add_env_var call.
|
||||
# For now, assuming if we reached here and mv was fine, primary operations were okay.
|
||||
echo ".env file generated successfully in the project root ($OUTPUT_FILE)."
|
||||
|
||||
@@ -17,439 +17,87 @@ if [ ! -f "$ENV_FILE" ]; then
|
||||
fi
|
||||
|
||||
# Load environment variables from .env file
|
||||
# Use set -a to export all variables read from the file
|
||||
set -a
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
# Generate welcome page data
|
||||
if [ -f "$SCRIPT_DIR/generate_welcome_page.sh" ]; then
|
||||
log_info "Generating welcome page..."
|
||||
bash "$SCRIPT_DIR/generate_welcome_page.sh" || log_warning "Failed to generate welcome page"
|
||||
fi
|
||||
|
||||
# Function to check if a profile is active
|
||||
is_profile_active() {
|
||||
local profile_to_check="$1"
|
||||
# COMPOSE_PROFILES is sourced from .env and will be available here
|
||||
if [ -z "$COMPOSE_PROFILES" ]; then
|
||||
return 1 # Not active if COMPOSE_PROFILES is empty or not set
|
||||
return 1
|
||||
fi
|
||||
# Check if the profile_to_check is in the comma-separated list
|
||||
# Adding commas at the beginning and end of both strings handles edge cases
|
||||
# (e.g., single profile, profile being a substring of another)
|
||||
if [[ ",$COMPOSE_PROFILES," == *",$profile_to_check,"* ]]; then
|
||||
return 0 # Active
|
||||
return 0
|
||||
else
|
||||
return 1 # Not active
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Service Access Credentials ---
|
||||
|
||||
# Display credentials, checking if variables exist
|
||||
echo
|
||||
log_info "Service Access Credentials. Save this information securely!"
|
||||
# Display credentials, checking if variables exist
|
||||
|
||||
if is_profile_active "n8n"; then
|
||||
echo
|
||||
echo "================================= n8n ================================="
|
||||
echo
|
||||
echo "Host: ${N8N_HOSTNAME:-<hostname_not_set>}"
|
||||
N8N_WORKER_COUNT_VAL="${N8N_WORKER_COUNT:-1}"
|
||||
echo "Workers: $N8N_WORKER_COUNT_VAL (each with dedicated task runner sidecar)"
|
||||
fi
|
||||
|
||||
if is_profile_active "open-webui"; then
|
||||
echo
|
||||
echo "================================= WebUI ==============================="
|
||||
echo
|
||||
echo "Host: ${WEBUI_HOSTNAME:-<hostname_not_set>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "flowise"; then
|
||||
echo
|
||||
echo "================================= Flowise ============================="
|
||||
echo
|
||||
echo "Host: ${FLOWISE_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${FLOWISE_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${FLOWISE_PASSWORD:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "dify"; then
|
||||
echo
|
||||
echo "================================= Dify ================================="
|
||||
echo
|
||||
echo "Host: ${DIFY_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Description: AI Application Development Platform with LLMOps"
|
||||
echo
|
||||
echo "API Access:"
|
||||
echo " - Web Interface: https://${DIFY_HOSTNAME:-<hostname_not_set>}"
|
||||
echo " - API Endpoint: https://${DIFY_HOSTNAME:-<hostname_not_set>}/v1"
|
||||
echo " - Internal API: http://dify-api:5001"
|
||||
fi
|
||||
|
||||
if is_profile_active "supabase"; then
|
||||
echo
|
||||
echo "================================= Supabase ============================"
|
||||
echo
|
||||
echo "External Host (via Caddy): ${SUPABASE_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Studio User: ${DASHBOARD_USERNAME:-<not_set_in_env>}"
|
||||
echo "Studio Password: ${DASHBOARD_PASSWORD:-<not_set_in_env>}"
|
||||
echo
|
||||
echo "Internal API Gateway: http://kong:8000"
|
||||
echo "Service Role Secret: ${SERVICE_ROLE_KEY:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "langfuse"; then
|
||||
echo
|
||||
echo "================================= Langfuse ============================"
|
||||
echo
|
||||
echo "Host: ${LANGFUSE_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${LANGFUSE_INIT_USER_EMAIL:-<not_set_in_env>}"
|
||||
echo "Password: ${LANGFUSE_INIT_USER_PASSWORD:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "monitoring"; then
|
||||
echo
|
||||
echo "================================= Grafana ============================="
|
||||
echo
|
||||
echo "Host: ${GRAFANA_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: admin"
|
||||
echo "Password: ${GRAFANA_ADMIN_PASSWORD:-<not_set_in_env>}"
|
||||
echo
|
||||
echo "================================= Prometheus =========================="
|
||||
echo
|
||||
echo "Host: ${PROMETHEUS_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${PROMETHEUS_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${PROMETHEUS_PASSWORD:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "searxng"; then
|
||||
echo
|
||||
echo "================================= Searxng ============================="
|
||||
echo
|
||||
echo "Host: ${SEARXNG_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${SEARXNG_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${SEARXNG_PASSWORD:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "portainer"; then
|
||||
echo
|
||||
echo "================================= Portainer ==========================="
|
||||
echo
|
||||
echo "Host: ${PORTAINER_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "(Note: On first login, Portainer will prompt to set up an admin user.)"
|
||||
fi
|
||||
|
||||
if is_profile_active "postiz"; then
|
||||
echo
|
||||
echo "================================= Postiz =============================="
|
||||
echo
|
||||
echo "Host: ${POSTIZ_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Internal Access (e.g., from n8n): http://postiz:5000"
|
||||
fi
|
||||
|
||||
if is_profile_active "postgresus"; then
|
||||
echo
|
||||
echo "================================= Postgresus =========================="
|
||||
echo
|
||||
echo "Host: ${POSTGRESUS_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "UI (external via Caddy): https://${POSTGRESUS_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "UI (internal): http://postgresus:4005"
|
||||
echo "------ Backup Target (internal PostgreSQL) ------"
|
||||
echo "PG version: ${POSTGRES_VERSION:-17}"
|
||||
echo "Host: postgres"
|
||||
echo "Port: ${POSTGRES_PORT:-5432}"
|
||||
echo "Username: ${POSTGRES_USER:-postgres}"
|
||||
echo "Password: ${POSTGRES_PASSWORD:-<not_set_in_env>}"
|
||||
echo "DB name: ${POSTGRES_DB:-postgres}"
|
||||
echo "Use HTTPS: false"
|
||||
fi
|
||||
|
||||
if is_profile_active "ragapp"; then
|
||||
echo
|
||||
echo "================================= RAGApp =============================="
|
||||
echo
|
||||
echo "Host: ${RAGAPP_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Internal Access (e.g., from n8n): http://ragapp:8000"
|
||||
echo "User: ${RAGAPP_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${RAGAPP_PASSWORD:-<not_set_in_env>}"
|
||||
echo "Admin: https://${RAGAPP_HOSTNAME:-<hostname_not_set>}/admin"
|
||||
echo "API Docs: https://${RAGAPP_HOSTNAME:-<hostname_not_set>}/docs"
|
||||
fi
|
||||
|
||||
if is_profile_active "ragflow"; then
|
||||
echo
|
||||
echo "================================= RAGFlow ============================="
|
||||
echo
|
||||
echo "Host: ${RAGFLOW_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "API (external via Caddy): https://${RAGFLOW_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "API (internal): http://ragflow:80"
|
||||
echo "Note: Uses built-in authentication (login/registration available in web UI)"
|
||||
fi
|
||||
|
||||
if is_profile_active "comfyui"; then
|
||||
echo
|
||||
echo "================================= ComfyUI ============================="
|
||||
echo
|
||||
echo "Host: ${COMFYUI_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${COMFYUI_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${COMFYUI_PASSWORD:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "libretranslate"; then
|
||||
echo
|
||||
echo "================================= LibreTranslate ==========================="
|
||||
echo
|
||||
echo "Host: ${LT_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${LT_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${LT_PASSWORD:-<not_set_in_env>}"
|
||||
echo "API (external via Caddy): https://${LT_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "API (internal): http://libretranslate:5000"
|
||||
echo "Docs: https://github.com/LibreTranslate/LibreTranslate"
|
||||
fi
|
||||
|
||||
if is_profile_active "qdrant"; then
|
||||
echo
|
||||
echo "================================= Qdrant =============================="
|
||||
echo
|
||||
echo "Dashboard: https://${QDRANT_HOSTNAME:-<hostname_not_set>}/dashboard"
|
||||
echo "Host: https://${QDRANT_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "API Key: ${QDRANT_API_KEY:-<not_set_in_env>}"
|
||||
echo "Internal REST API Access (e.g., from backend): http://qdrant:6333"
|
||||
fi
|
||||
|
||||
if is_profile_active "crawl4ai"; then
|
||||
echo
|
||||
echo "================================= Crawl4AI ============================"
|
||||
echo
|
||||
echo "Internal Access (e.g., from n8n): http://crawl4ai:11235"
|
||||
echo "(Note: Not exposed externally via Caddy by default)"
|
||||
fi
|
||||
|
||||
if is_profile_active "docling"; then
|
||||
echo
|
||||
echo "================================= Docling ============================="
|
||||
echo
|
||||
echo "Web UI: https://${DOCLING_HOSTNAME:-<hostname_not_set>}/ui"
|
||||
echo "API Docs: https://${DOCLING_HOSTNAME:-<hostname_not_set>}/docs"
|
||||
echo
|
||||
echo "Credentials (Caddy Basic Auth):"
|
||||
echo "User: ${DOCLING_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${DOCLING_PASSWORD:-<not_set_in_env>}"
|
||||
echo
|
||||
echo "API Endpoints:"
|
||||
echo "External (via Caddy): https://${DOCLING_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Internal (from n8n): http://docling:5001"
|
||||
echo
|
||||
echo "VLM Pipeline (Vision Language Model):"
|
||||
echo " 1. Load VLM model in Ollama via Open WebUI -> Settings -> Models"
|
||||
echo " Example: granite3.2-vision:2b"
|
||||
echo
|
||||
echo " 2. API request with VLM pipeline:"
|
||||
echo ' curl -X POST "https://'"${DOCLING_HOSTNAME:-<hostname_not_set>}"'/v1/convert/source" \'
|
||||
echo ' -H "Content-Type: application/json" \'
|
||||
echo ' -u "'"${DOCLING_USERNAME:-<not_set_in_env>}"':'"${DOCLING_PASSWORD:-<not_set_in_env>}"'" \'
|
||||
echo " -d '{"
|
||||
echo ' "source": "https://arxiv.org/pdf/2501.17887",'
|
||||
echo ' "options": {'
|
||||
echo ' "pipeline": "vlm",'
|
||||
echo ' "vlm_pipeline_model_api": {'
|
||||
echo ' "url": "http://ollama:11434/v1/chat/completions",'
|
||||
echo ' "params": {"model": "granite3.2-vision:2b"},'
|
||||
echo ' "prompt": "Convert this page to docling.",'
|
||||
echo ' "timeout": 300'
|
||||
echo " }"
|
||||
echo " }"
|
||||
echo " }'"
|
||||
fi
|
||||
|
||||
if is_profile_active "gotenberg"; then
|
||||
echo
|
||||
echo "================================= Gotenberg ============================"
|
||||
echo
|
||||
echo "Internal Access (e.g., from n8n): http://gotenberg:3000"
|
||||
echo "API Documentation: https://gotenberg.dev/docs"
|
||||
echo
|
||||
echo "Common API Endpoints:"
|
||||
echo " HTML to PDF: POST /forms/chromium/convert/html"
|
||||
echo " URL to PDF: POST /forms/chromium/convert/url"
|
||||
echo " Markdown to PDF: POST /forms/chromium/convert/markdown"
|
||||
echo " Office to PDF: POST /forms/libreoffice/convert"
|
||||
fi
|
||||
|
||||
if is_profile_active "waha"; then
|
||||
echo
|
||||
echo "============================== WAHA (WhatsApp HTTP API) =============================="
|
||||
echo
|
||||
echo "Dashboard: https://${WAHA_HOSTNAME:-<hostname_not_set>}/dashboard"
|
||||
echo "Swagger: https://${WAHA_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Internal: http://waha:3000"
|
||||
echo
|
||||
echo "Dashboard User: ${WAHA_DASHBOARD_USERNAME:-<not_set_in_env>}"
|
||||
echo "Dashboard Pass: ${WAHA_DASHBOARD_PASSWORD:-<not_set_in_env>}"
|
||||
echo "Swagger User: ${WHATSAPP_SWAGGER_USERNAME:-<not_set_in_env>}"
|
||||
echo "Swagger Pass: ${WHATSAPP_SWAGGER_PASSWORD:-<not_set_in_env>}"
|
||||
echo "API key (plain): ${WAHA_API_KEY_PLAIN:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "paddleocr"; then
|
||||
echo
|
||||
echo "================================= PaddleOCR ==========================="
|
||||
echo
|
||||
echo "Host: ${PADDLEOCR_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "User: ${PADDLEOCR_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password: ${PADDLEOCR_PASSWORD:-<not_set_in_env>}"
|
||||
echo "API (external via Caddy): https://${PADDLEOCR_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "API (internal): http://paddleocr:8080"
|
||||
echo "Docs: https://paddleocr.a2.fyi/docs"
|
||||
echo "Notes: PaddleX Basic Serving (CPU), pipeline=OCR"
|
||||
fi
|
||||
|
||||
if is_profile_active "python-runner"; then
|
||||
echo
|
||||
echo "================================= Python Runner ========================"
|
||||
echo
|
||||
echo "Internal Container DNS: python-runner"
|
||||
echo "Mounted Code Directory: ./python-runner (host) -> /app (container)"
|
||||
echo "Entry File: /app/main.py"
|
||||
echo "(Note: Internal-only service with no exposed ports; view output via logs)"
|
||||
echo "Logs: docker compose -p localai logs -f python-runner"
|
||||
fi
|
||||
|
||||
if is_profile_active "n8n" || is_profile_active "langfuse"; then
|
||||
echo
|
||||
echo "================================= Redis (Valkey) ======================"
|
||||
echo
|
||||
echo "Internal Host: ${REDIS_HOST:-redis}"
|
||||
echo "Internal Port: ${REDIS_PORT:-6379}"
|
||||
echo "Password: ${REDIS_AUTH:-}"
|
||||
echo "(Note: Primarily for internal service communication, not exposed externally by default)"
|
||||
fi
|
||||
|
||||
if is_profile_active "letta"; then
|
||||
echo
|
||||
echo "================================= Letta ================================"
|
||||
echo
|
||||
echo "Host: ${LETTA_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Authorization: Bearer ${LETTA_SERVER_PASSWORD}"
|
||||
fi
|
||||
|
||||
if is_profile_active "lightrag"; then
|
||||
echo
|
||||
echo "================================= LightRAG ============================="
|
||||
echo
|
||||
echo "Host: ${LIGHTRAG_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Web UI: https://${LIGHTRAG_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Internal Access (e.g., from n8n): http://lightrag:9621"
|
||||
echo ""
|
||||
echo "Authentication (Web UI):"
|
||||
echo " User: ${LIGHTRAG_USERNAME:-<not_set_in_env>}"
|
||||
echo " Password: ${LIGHTRAG_PASSWORD:-<not_set_in_env>}"
|
||||
echo ""
|
||||
echo "API Access:"
|
||||
echo " API Key: ${LIGHTRAG_API_KEY:-<not_set_in_env>}"
|
||||
echo " API Docs: https://${LIGHTRAG_HOSTNAME:-<hostname_not_set>}/docs"
|
||||
echo " Ollama-compatible: https://${LIGHTRAG_HOSTNAME:-<hostname_not_set>}/v1/chat/completions"
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " LLM: Ollama (qwen2.5:32b) at http://ollama:11434"
|
||||
echo " Embeddings: Ollama (bge-m3:latest) at http://ollama:11434"
|
||||
echo " Storage: Flexible (JSON/PostgreSQL/Neo4j based on installed services)"
|
||||
echo ""
|
||||
echo "Note: Requires Ollama to be installed and running for LLM and embeddings."
|
||||
echo " Upload documents via /app/data/inputs volume or Web UI."
|
||||
fi
|
||||
|
||||
if is_profile_active "cpu" || is_profile_active "gpu-nvidia" || is_profile_active "gpu-amd"; then
|
||||
echo
|
||||
echo "================================= Ollama =============================="
|
||||
echo
|
||||
echo "Internal Access (e.g., from n8n, Open WebUI): http://ollama:11434"
|
||||
echo "(Note: Ollama runs with the selected profile: cpu, gpu-nvidia, or gpu-amd)"
|
||||
fi
|
||||
|
||||
if is_profile_active "weaviate"; then
|
||||
echo
|
||||
echo "================================= Weaviate ============================"
|
||||
echo
|
||||
echo "Host: ${WEAVIATE_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Admin User (for Weaviate RBAC): ${WEAVIATE_USERNAME:-<not_set_in_env>}"
|
||||
echo "Weaviate API Key: ${WEAVIATE_API_KEY:-<not_set_in_env>}"
|
||||
fi
|
||||
|
||||
if is_profile_active "neo4j"; then
|
||||
echo
|
||||
echo "================================= Neo4j =================================="
|
||||
echo
|
||||
echo "Web UI Host: https://${NEO4J_HOSTNAME:-<hostname_not_set>}"
|
||||
echo "Bolt Port (for drivers): 7687 (e.g., neo4j://\\${NEO4J_HOSTNAME:-<hostname_not_set>}:7687)"
|
||||
echo "User (for Web UI & API): ${NEO4J_AUTH_USERNAME:-<not_set_in_env>}"
|
||||
echo "Password (for Web UI & API): ${NEO4J_AUTH_PASSWORD:-<not_set_in_env>}"
|
||||
echo
|
||||
echo "HTTP API Access (e.g., for N8N):"
|
||||
echo " Authentication: Basic (use User/Password above)"
|
||||
echo " Cypher API Endpoint (POST): https://\\${NEO4J_HOSTNAME:-<hostname_not_set>}/db/neo4j/tx/commit"
|
||||
echo " Authorization Header Value (for 'Authorization: Basic <value>'): \$(echo -n \"${NEO4J_AUTH_USERNAME:-neo4j}:${NEO4J_AUTH_PASSWORD}\" | base64)"
|
||||
fi
|
||||
|
||||
# Standalone PostgreSQL (used by n8n, Langfuse, etc.)
|
||||
# Check if n8n or langfuse is active, as they use this PostgreSQL instance.
|
||||
# The Supabase section already details its own internal Postgres.
|
||||
if is_profile_active "n8n" || is_profile_active "langfuse"; then
|
||||
# Check if Supabase is NOT active, to avoid confusion with Supabase's Postgres if both are present
|
||||
# However, the main POSTGRES_PASSWORD is used by this standalone instance.
|
||||
# Supabase has its own environment variables for its internal Postgres if configured differently,
|
||||
# but the current docker-compose.yml uses the main POSTGRES_PASSWORD for langfuse's postgres dependency too.
|
||||
# For clarity, we will label this distinctly.
|
||||
echo
|
||||
echo "==================== Standalone PostgreSQL (for n8n, Langfuse, etc.) ====================="
|
||||
echo
|
||||
echo "Host: postgres"
|
||||
echo "Port: ${POSTGRES_PORT:-5432}"
|
||||
echo "Database: ${POSTGRES_DB:-postgres}" # This is typically 'postgres' or 'n8n' for n8n, and 'langfuse' for langfuse, but refers to the service.
|
||||
echo "User: ${POSTGRES_USER:-postgres}"
|
||||
echo "Password: ${POSTGRES_PASSWORD:-<not_set_in_env>}"
|
||||
echo "(Note: This is the PostgreSQL instance used by services like n8n and Langfuse.)"
|
||||
echo "(It is separate from Supabase's internal PostgreSQL if Supabase is also enabled.)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "======================================================================="
|
||||
echo " Installation Complete!"
|
||||
echo "======================================================================="
|
||||
echo
|
||||
|
||||
# --- Update Script Info (Placeholder) ---
|
||||
log_info "To update the services, run the 'update.sh' script: bash ./scripts/update.sh"
|
||||
# --- Make Commands ---
|
||||
echo "================================= Make Commands ========================="
|
||||
echo
|
||||
echo " make logs View logs (all services)"
|
||||
echo " make logs s=<service> View logs for specific service"
|
||||
echo " make status Show container status"
|
||||
echo " make monitor Live CPU/memory monitoring"
|
||||
echo " make restarts Show restart count per container"
|
||||
echo
|
||||
echo " make update Update system and services"
|
||||
echo " make update-preview Preview available updates (dry-run)"
|
||||
echo " make doctor Run system diagnostics"
|
||||
echo " make clean Remove unused Docker resources"
|
||||
echo
|
||||
echo " make switch-beta Switch to beta (develop branch)"
|
||||
echo " make switch-stable Switch to stable (main branch)"
|
||||
echo
|
||||
|
||||
# ============================================
|
||||
# Cloudflare Tunnel Security Notice
|
||||
# ============================================
|
||||
if is_profile_active "cloudflare-tunnel"; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔒 CLOUDFLARE TUNNEL SECURITY"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "✅ Cloudflare Tunnel is configured and running!"
|
||||
echo ""
|
||||
echo "Your services are accessible through Cloudflare's secure network."
|
||||
echo "All traffic is encrypted and routed through the tunnel."
|
||||
echo ""
|
||||
echo "🛡️ RECOMMENDED SECURITY ENHANCEMENT:"
|
||||
echo " For maximum security, close the following ports in your VPS firewall:"
|
||||
echo " • Port 80 (HTTP)"
|
||||
echo " • Port 443 (HTTPS)"
|
||||
echo " • Port 7687 (Neo4j Bolt)"
|
||||
echo ""
|
||||
echo " ⚠️ Only close ports AFTER confirming tunnel connectivity!"
|
||||
echo ""
|
||||
fi
|
||||
# --- Welcome Page ---
|
||||
echo "================================= Welcome Page =========================="
|
||||
echo
|
||||
echo "All your service credentials are available on the Welcome Page:"
|
||||
echo
|
||||
echo " URL: https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
echo " Username: ${WELCOME_USERNAME:-<not_set>}"
|
||||
echo " Password: ${WELCOME_PASSWORD:-<not_set>}"
|
||||
echo
|
||||
echo "The Welcome Page displays:"
|
||||
echo " - All installed services with their hostnames"
|
||||
echo " - Login credentials (username/password/API keys)"
|
||||
echo " - Internal URLs for service-to-service communication"
|
||||
echo
|
||||
|
||||
# --- Next Steps ---
|
||||
echo "======================================================================="
|
||||
echo " Next Steps"
|
||||
echo "======================================================================="
|
||||
echo
|
||||
echo "======================================================================"
|
||||
echo "1. Visit your Welcome Page to view all service credentials"
|
||||
echo " https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
echo
|
||||
echo "Next Steps:"
|
||||
echo "1. Review the credentials above and store them safely."
|
||||
echo "2. Access the services via their respective URLs (check \`docker compose ps\` if needed)."
|
||||
echo "3. Configure services as needed (e.g., first-run setup for n8n)."
|
||||
echo "2. Store the Welcome Page credentials securely"
|
||||
echo " (Username: ${WELCOME_USERNAME:-<not_set>})"
|
||||
echo
|
||||
echo "======================================================================"
|
||||
echo "3. Configure services as needed:"
|
||||
echo " - n8n: Complete first-run setup with your email"
|
||||
echo " - Portainer: Create admin account on first login"
|
||||
echo " - Open WebUI: Register your account"
|
||||
echo
|
||||
log_info "Thank you for using this repository!"
|
||||
echo "4. Run 'make doctor' if you experience any issues"
|
||||
echo
|
||||
echo "======================================================================="
|
||||
echo
|
||||
log_info "Thank you for using n8n-install!"
|
||||
echo
|
||||
|
||||
483
scripts/generate_welcome_page.sh
Executable file
483
scripts/generate_welcome_page.sh
Executable file
@@ -0,0 +1,483 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Generate data.json for the welcome page with active services and credentials
|
||||
|
||||
set -e
|
||||
|
||||
# Source the utilities file
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
|
||||
# Get the directory where the script resides
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." &> /dev/null && pwd )"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
OUTPUT_FILE="$PROJECT_ROOT/welcome/data.json"
|
||||
|
||||
# Check if .env file exists
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
log_error "The .env file ('$ENV_FILE') was not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# Load environment variables from .env file
|
||||
set -a
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
# Function to check if a profile is active
|
||||
is_profile_active() {
|
||||
local profile_to_check="$1"
|
||||
if [ -z "$COMPOSE_PROFILES" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [[ ",$COMPOSE_PROFILES," == *",$profile_to_check,"* ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to escape JSON strings
|
||||
json_escape() {
|
||||
local str="$1"
|
||||
# Escape backslashes, double quotes, and control characters
|
||||
printf '%s' "$str" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr -d '\n\r'
|
||||
}
|
||||
|
||||
# Start building JSON
|
||||
GENERATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Build services object (key-based)
|
||||
SERVICES_JSON=""
|
||||
|
||||
# n8n
|
||||
if is_profile_active "n8n"; then
|
||||
N8N_WORKER_COUNT_VAL="${N8N_WORKER_COUNT:-1}"
|
||||
SERVICES_JSON+='"n8n": {
|
||||
"hostname": "'"$(json_escape "$N8N_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"note": "Use the email you provided during installation"
|
||||
},
|
||||
"extra": {
|
||||
"workers": "'"$N8N_WORKER_COUNT_VAL"'"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# Flowise
|
||||
if is_profile_active "flowise"; then
|
||||
SERVICES_JSON+='"flowise": {
|
||||
"hostname": "'"$(json_escape "$FLOWISE_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"username": "'"$(json_escape "$FLOWISE_USERNAME")"'",
|
||||
"password": "'"$(json_escape "$FLOWISE_PASSWORD")"'"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# Open WebUI
|
||||
if is_profile_active "open-webui"; then
|
||||
SERVICES_JSON+='"open-webui": {
|
||||
"hostname": "'"$(json_escape "$WEBUI_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"note": "Create account on first login"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# Grafana (monitoring)
|
||||
if is_profile_active "monitoring"; then
|
||||
SERVICES_JSON+='"grafana": {
|
||||
"hostname": "'"$(json_escape "$GRAFANA_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"username": "admin",
|
||||
"password": "'"$(json_escape "$GRAFANA_ADMIN_PASSWORD")"'"
|
||||
}
|
||||
},'
|
||||
SERVICES_JSON+='"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_JSON+='"portainer": {
|
||||
"hostname": "'"$(json_escape "$PORTAINER_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"note": "Create admin account on first login"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# Postgresus
|
||||
if is_profile_active "postgresus"; then
|
||||
SERVICES_JSON+='"postgresus": {
|
||||
"hostname": "'"$(json_escape "$POSTGRESUS_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"note": "Uses PostgreSQL credentials from .env"
|
||||
},
|
||||
"extra": {
|
||||
"pg_host": "postgres",
|
||||
"pg_port": "'"${POSTGRES_PORT:-5432}"'",
|
||||
"pg_user": "'"$(json_escape "${POSTGRES_USER:-postgres}")"'",
|
||||
"pg_password": "'"$(json_escape "$POSTGRES_PASSWORD")"'",
|
||||
"pg_db": "'"$(json_escape "${POSTGRES_DB:-postgres}")"'"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# Langfuse
|
||||
if is_profile_active "langfuse"; then
|
||||
SERVICES_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"letta": {
|
||||
"hostname": "'"$(json_escape "$LETTA_HOSTNAME")"'",
|
||||
"credentials": {
|
||||
"api_key": "'"$(json_escape "$LETTA_SERVER_PASSWORD")"'"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# ComfyUI
|
||||
if is_profile_active "comfyui"; then
|
||||
SERVICES_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"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_JSON+='"redis": {
|
||||
"hostname": null,
|
||||
"credentials": {
|
||||
"password": "'"$(json_escape "$REDIS_AUTH")"'"
|
||||
},
|
||||
"extra": {
|
||||
"internal_host": "'"${REDIS_HOST:-redis}"'",
|
||||
"internal_port": "'"${REDIS_PORT:-6379}"'"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# PostgreSQL (internal only, shown if n8n or langfuse active)
|
||||
if is_profile_active "n8n" || is_profile_active "langfuse"; then
|
||||
SERVICES_JSON+='"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_JSON+='"python-runner": {
|
||||
"hostname": null,
|
||||
"credentials": {
|
||||
"note": "Internal service only"
|
||||
},
|
||||
"extra": {
|
||||
"logs_command": "docker compose -p localai logs -f python-runner"
|
||||
}
|
||||
},'
|
||||
fi
|
||||
|
||||
# Cloudflare Tunnel
|
||||
if is_profile_active "cloudflare-tunnel"; then
|
||||
SERVICES_JSON+='"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
|
||||
|
||||
# Remove trailing comma from services JSON
|
||||
SERVICES_JSON=$(echo "$SERVICES_JSON" | sed 's/,$//')
|
||||
|
||||
# Write final JSON
|
||||
cat > "$OUTPUT_FILE" << EOF
|
||||
{
|
||||
"domain": "$(json_escape "$USER_DOMAIN_NAME")",
|
||||
"generated_at": "$GENERATED_AT",
|
||||
"services": {
|
||||
$SERVICES_JSON
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "Welcome page data generated at: $OUTPUT_FILE"
|
||||
log_info "Access it at: https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
Reference in New Issue
Block a user