662 lines
24 KiB
Bash
Executable File
662 lines
24 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
cat << "EOF"
|
|
██████╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ███████╗
|
|
██╔════╝██║ ██║██╔══██╗████╗ ██║██╔════╝ ██╔════╝
|
|
██║ ███████║███████║██╔██╗ ██║██║ ███╗█████╗
|
|
██║ ██╔══██║██╔══██║██║╚██╗██║██║ ██║██╔══╝
|
|
╚██████╗██║ ██║██║ ██║██║ ╚████║╚██████╔╝███████╗
|
|
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝
|
|
|
|
███╗ ███╗ █████╗ ██╗ ██╗███████╗██████╗
|
|
████╗ ████║██╔══██╗██║ ██╔╝██╔════╝██╔══██╗
|
|
██╔████╔██║███████║█████╔╝ █████╗ ██████╔╝
|
|
██║╚██╔╝██║██╔══██║██╔═██╗ ██╔══╝ ██╔══██╗
|
|
██║ ╚═╝ ██║██║ ██║██║ ██╗███████╗██║ ██║
|
|
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
|
Start Production Wizard
|
|
EOF
|
|
|
|
echo "#############################################################"
|
|
echo "# "
|
|
echo "# Changemaker.lite Production Deployment "
|
|
echo "# "
|
|
echo "# This script will: "
|
|
echo "# 1. Install and configure cloudflared "
|
|
echo "# 2. Create a systemd service for the tunnel "
|
|
echo "# 3. Configure DNS records "
|
|
echo "# 4. Set up access policies "
|
|
echo "# "
|
|
echo "#############################################################"
|
|
echo ""
|
|
|
|
# Get script directory
|
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
ENV_FILE="$SCRIPT_DIR/.env"
|
|
TUNNEL_CONFIG_FILE="$SCRIPT_DIR/configs/cloudflare/tunnel-config.yml"
|
|
TUNNEL_CREDS_DIR="$SCRIPT_DIR/configs/cloudflare"
|
|
SERVICE_NAME="cloudflared-changemaker"
|
|
|
|
# Source environment variables
|
|
if [ -f "$ENV_FILE" ]; then
|
|
export $(grep -v '^#' "$ENV_FILE" | xargs)
|
|
else
|
|
echo "Error: .env file not found. Please run ./config.sh first."
|
|
exit 1
|
|
fi
|
|
|
|
# Prompt user for tunnel name suffix
|
|
echo "Choose a name for your tunnel:"
|
|
echo "This will create a tunnel named 'changemaker-lite-[your-input]'"
|
|
echo "Examples: 'prod', 'staging', 'myproject', 'server1'"
|
|
echo ""
|
|
read -p "Enter tunnel name suffix (or press Enter for default 'main'): " TUNNEL_SUFFIX
|
|
|
|
# Set default if empty
|
|
if [ -z "$TUNNEL_SUFFIX" ]; then
|
|
TUNNEL_SUFFIX="main"
|
|
fi
|
|
|
|
# Validate tunnel suffix (alphanumeric and hyphens only)
|
|
if [[ ! "$TUNNEL_SUFFIX" =~ ^[a-zA-Z0-9-]+$ ]]; then
|
|
echo "Error: Tunnel suffix can only contain letters, numbers, and hyphens."
|
|
exit 1
|
|
fi
|
|
|
|
TUNNEL_NAME="changemaker-lite-$TUNNEL_SUFFIX"
|
|
SERVICE_NAME="cloudflared-changemaker-lite-$TUNNEL_SUFFIX"
|
|
echo "Using tunnel name: $TUNNEL_NAME"
|
|
echo "Using service name: $SERVICE_NAME"
|
|
echo ""
|
|
|
|
# Check if Cloudflare credentials are properly configured
|
|
if [ -z "$CF_API_TOKEN" ] || [ "$CF_API_TOKEN" == "your_cloudflare_api_token" ] || \
|
|
[ -z "$CF_ZONE_ID" ] || [ "$CF_ZONE_ID" == "your_cloudflare_zone_id" ] || \
|
|
[ -z "$CF_DOMAIN" ] || [ -z "$CF_ACCOUNT_ID" ] || [ "$CF_ACCOUNT_ID" == "your_cloudflare_account_id" ]; then
|
|
echo "Error: Cloudflare configuration incomplete."
|
|
echo ""
|
|
echo "Current values:"
|
|
echo " CF_API_TOKEN: ${CF_API_TOKEN:-not set}"
|
|
echo " CF_ZONE_ID: ${CF_ZONE_ID:-not set}"
|
|
echo " CF_DOMAIN: ${CF_DOMAIN:-not set}"
|
|
echo " CF_ACCOUNT_ID: ${CF_ACCOUNT_ID:-not set}"
|
|
echo ""
|
|
echo "Please run ./config.sh and complete the Cloudflare configuration."
|
|
exit 1
|
|
fi
|
|
|
|
# Check if cloudflared is installed
|
|
check_cloudflared() {
|
|
if ! command -v cloudflared &> /dev/null; then
|
|
echo "cloudflared not found. Installing..."
|
|
install_cloudflared
|
|
else
|
|
echo "✅ cloudflared is already installed: $(cloudflared --version | head -n1)"
|
|
fi
|
|
}
|
|
|
|
# Install cloudflared
|
|
install_cloudflared() {
|
|
echo "Installing cloudflared..."
|
|
|
|
# Check if we need to add the Cloudflare repository
|
|
if [ ! -f /usr/share/keyrings/cloudflare-main.gpg ]; then
|
|
# Add the cloudflare gpg key
|
|
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
|
|
|
|
# Add the repository
|
|
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
|
|
fi
|
|
|
|
# Update and install
|
|
sudo apt-get update && sudo apt-get install -y cloudflared
|
|
|
|
if ! command -v cloudflared &> /dev/null; then
|
|
echo "Failed to install cloudflared. Please install it manually following the instructions at:"
|
|
echo "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/"
|
|
exit 1
|
|
else
|
|
echo "✅ cloudflared installed successfully: $(cloudflared --version | head -n1)"
|
|
fi
|
|
}
|
|
|
|
# Authenticate with Cloudflare
|
|
authenticate_cloudflare() {
|
|
echo "Checking Cloudflare authentication..."
|
|
|
|
# Remove --account-tag parameter as it's not supported in this version
|
|
if ! cloudflared tunnel list &> /dev/null; then
|
|
echo "Not authenticated with Cloudflare. Starting authentication process..."
|
|
echo "Log in to Cloudflare in the browser window that opens..."
|
|
|
|
# Run login
|
|
if ! cloudflared tunnel login; then
|
|
echo "Authentication failed. Please try again or authenticate manually using:"
|
|
echo "cloudflared tunnel login"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "✅ Already authenticated with Cloudflare"
|
|
fi
|
|
}
|
|
|
|
# Create or get existing tunnel
|
|
setup_tunnel() {
|
|
echo "Setting up Cloudflare tunnel..."
|
|
|
|
# Check if we have a tunnel ID in the environment
|
|
local tunnel_id=""
|
|
|
|
# Check if tunnel exists by name
|
|
echo "Checking for existing tunnel named '$TUNNEL_NAME'..."
|
|
local tunnel_list_output
|
|
# Remove --account-tag parameter as it's not supported in this version
|
|
tunnel_list_output=$(cloudflared tunnel list --output json 2>&1)
|
|
|
|
# Check if the command was successful
|
|
if [ $? -ne 0 ]; then
|
|
echo "Error listing tunnels: $tunnel_list_output"
|
|
echo "Please ensure you're properly authenticated."
|
|
exit 1
|
|
fi
|
|
|
|
# Fix for invalid JSON output - try to clean the output if needed
|
|
if ! echo "$tunnel_list_output" | jq '.' &>/dev/null; then
|
|
echo "Warning: Invalid JSON returned from cloudflared. Attempting to clean output..."
|
|
# Try to extract valid JSON portion
|
|
tunnel_list_output=$(echo "$tunnel_list_output" | grep -o '\[.*\]' || echo "[]")
|
|
fi
|
|
|
|
# Now try to extract the tunnel info
|
|
local tunnel_info
|
|
if echo "$tunnel_list_output" | jq '.' &>/dev/null; then
|
|
tunnel_info=$(echo "$tunnel_list_output" | jq -r '.[] | select(.name=="'$TUNNEL_NAME'")' 2>/dev/null || echo "")
|
|
else
|
|
echo "Failed to parse tunnel list. Cannot check for existing tunnels."
|
|
tunnel_info=""
|
|
fi
|
|
|
|
if [ -n "$tunnel_info" ]; then
|
|
# Tunnel exists
|
|
tunnel_id=$(echo "$tunnel_info" | jq -r '.id' 2>/dev/null)
|
|
if [ -n "$tunnel_id" ] && [ "$tunnel_id" != "null" ]; then
|
|
echo "✅ Found existing tunnel: $TUNNEL_NAME (ID: $tunnel_id)"
|
|
|
|
# Update the environment variable if needed
|
|
if [ "$CF_TUNNEL_ID" != "$tunnel_id" ]; then
|
|
echo "Updating CF_TUNNEL_ID in .env file..."
|
|
sed -i "s/CF_TUNNEL_ID=.*/CF_TUNNEL_ID=$tunnel_id/" "$ENV_FILE"
|
|
# Reload the variable
|
|
export CF_TUNNEL_ID="$tunnel_id"
|
|
fi
|
|
else
|
|
echo "Warning: Found tunnel but couldn't extract ID"
|
|
fi
|
|
else
|
|
# Create new tunnel
|
|
echo "Creating new tunnel: $TUNNEL_NAME"
|
|
local tunnel_create_output
|
|
# Remove --account-tag parameter as it's not supported in this version
|
|
tunnel_create_output=$(cloudflared tunnel create "$TUNNEL_NAME" 2>&1)
|
|
echo "Tunnel creation output:"
|
|
echo "$tunnel_create_output"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to create tunnel. Please check your Cloudflare credentials."
|
|
exit 1
|
|
fi
|
|
|
|
# Try multiple methods to extract the tunnel ID from output
|
|
# Method 1: Try the original regex pattern with new tunnel name
|
|
tunnel_id=$(echo "$tunnel_create_output" | grep -oP "Created tunnel $TUNNEL_NAME with id \K[a-f0-9-]+" || echo "")
|
|
|
|
# Method 2: Look for UUID pattern
|
|
if [ -z "$tunnel_id" ]; then
|
|
tunnel_id=$(echo "$tunnel_create_output" | grep -oP '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}' || echo "")
|
|
fi
|
|
|
|
# Method 3: Look for hex string that might be the ID
|
|
if [ -z "$tunnel_id" ]; then
|
|
tunnel_id=$(echo "$tunnel_create_output" | grep -oP '\b[a-f0-9-]{20,}\b' || echo "")
|
|
fi
|
|
|
|
# Interactive method as fallback
|
|
if [ -z "$tunnel_id" ]; then
|
|
echo "Could not automatically extract tunnel ID from output."
|
|
echo "Please check the output above and enter the tunnel ID manually:"
|
|
read -p "Tunnel ID: " tunnel_id
|
|
|
|
if [ -z "$tunnel_id" ]; then
|
|
echo "No tunnel ID provided. Please create the tunnel manually and set CF_TUNNEL_ID in the .env file."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
echo "✅ Created new tunnel: $TUNNEL_NAME (ID: $tunnel_id)"
|
|
|
|
# Update the environment variable
|
|
sed -i "s/CF_TUNNEL_ID=.*/CF_TUNNEL_ID=$tunnel_id/" "$ENV_FILE"
|
|
export CF_TUNNEL_ID="$tunnel_id"
|
|
fi
|
|
|
|
# Make sure credentials directory exists
|
|
mkdir -p "$TUNNEL_CREDS_DIR"
|
|
|
|
# Check if we need to copy the credentials file
|
|
local creds_file="$HOME/.cloudflared/$tunnel_id.json"
|
|
local target_creds_file="$TUNNEL_CREDS_DIR/$tunnel_id.json"
|
|
|
|
if [ -f "$creds_file" ] && [ ! -f "$target_creds_file" ]; then
|
|
echo "Copying tunnel credentials to project directory..."
|
|
cp "$creds_file" "$target_creds_file"
|
|
echo "✅ Credentials copied to $target_creds_file"
|
|
elif [ ! -f "$creds_file" ]; then
|
|
echo "Warning: Tunnel credentials not found at $creds_file"
|
|
echo "Checking for credentials in alternative locations..."
|
|
|
|
# Try to find credentials in ~/.cloudflared directory
|
|
local found_creds=false
|
|
for cred_file in "$HOME/.cloudflared/"*.json; do
|
|
if [ -f "$cred_file" ]; then
|
|
echo "Found credentials file: $cred_file"
|
|
echo "Checking if this file contains credentials for our tunnel..."
|
|
|
|
# Check if the file contains the tunnel ID
|
|
if grep -q "$tunnel_id" "$cred_file"; then
|
|
echo "✅ This appears to be the correct credentials file!"
|
|
cp "$cred_file" "$target_creds_file"
|
|
echo "✅ Credentials copied to $target_creds_file"
|
|
found_creds=true
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ "$found_creds" = false ]; then
|
|
echo "Error: Could not find tunnel credentials."
|
|
echo "Please locate the credentials file for tunnel $tunnel_id and copy it to:"
|
|
echo "$target_creds_file"
|
|
echo ""
|
|
echo "You can also run this command manually to create a new tunnel with credentials:"
|
|
echo "cloudflared tunnel create --account-tag \"$CF_ACCOUNT_ID\" \"$tunnel_name\""
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Update tunnel configuration file
|
|
update_tunnel_config "$target_creds_file"
|
|
|
|
return 0
|
|
}
|
|
|
|
# Update tunnel configuration
|
|
update_tunnel_config() {
|
|
local creds_file="$1"
|
|
|
|
echo "Updating tunnel configuration..."
|
|
|
|
# Create or update the config file
|
|
cat > "$TUNNEL_CONFIG_FILE" << EOL
|
|
# Cloudflare Tunnel Configuration for $CF_DOMAIN
|
|
# Generated by Changemaker.lite start-production.sh on $(date)
|
|
|
|
tunnel: ${CF_TUNNEL_ID}
|
|
credentials-file: ${creds_file}
|
|
ingress:
|
|
- hostname: homepage.${CF_DOMAIN}
|
|
service: http://localhost:${HOMEPAGE_PORT:-3010}
|
|
- hostname: code.${CF_DOMAIN}
|
|
service: http://localhost:${CODE_SERVER_PORT:-8888}
|
|
- hostname: listmonk.${CF_DOMAIN}
|
|
service: http://localhost:${LISTMONK_PORT:-9000}
|
|
- hostname: docs.${CF_DOMAIN}
|
|
service: http://localhost:${MKDOCS_PORT:-4000}
|
|
- hostname: ${CF_DOMAIN}
|
|
service: http://localhost:${MKDOCS_SITE_SERVER_PORT:-4001}
|
|
- hostname: n8n.${CF_DOMAIN}
|
|
service: http://localhost:${N8N_PORT:-5678}
|
|
- hostname: db.${CF_DOMAIN}
|
|
service: http://localhost:${NOCODB_PORT:-8090}
|
|
- hostname: git.${CF_DOMAIN}
|
|
service: http://localhost:${GITEA_WEB_PORT:-3030}
|
|
- hostname: map.${CF_DOMAIN}
|
|
service: http://localhost:${MAP_PORT:-3000}
|
|
- hostname: qr.${CF_DOMAIN}
|
|
service: http://localhost:${MINI_QR_PORT:-8089}
|
|
- service: http_status:404
|
|
EOL
|
|
|
|
echo "✅ Tunnel configuration updated at $TUNNEL_CONFIG_FILE"
|
|
}
|
|
|
|
# Create systemd service for cloudflared
|
|
create_systemd_service() {
|
|
echo "Creating systemd service for cloudflared tunnel: $SERVICE_NAME"
|
|
|
|
local cloudflared_path=$(which cloudflared)
|
|
local username=$(whoami)
|
|
|
|
# Create service file content
|
|
local service_content="[Unit]
|
|
Description=Cloudflare Tunnel for Changemaker.lite
|
|
After=network.target
|
|
|
|
[Service]
|
|
User=$username
|
|
ExecStart=$cloudflared_path tunnel --config $TUNNEL_CONFIG_FILE run
|
|
Restart=always
|
|
RestartSec=5
|
|
StartLimitInterval=0
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target"
|
|
|
|
# Write service file
|
|
echo "$service_content" | sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null
|
|
|
|
# Reload systemd
|
|
sudo systemctl daemon-reload
|
|
|
|
# Enable and start the service
|
|
sudo systemctl enable $SERVICE_NAME.service
|
|
sudo systemctl restart $SERVICE_NAME.service
|
|
|
|
# Check service status
|
|
sleep 3
|
|
if sudo systemctl is-active --quiet $SERVICE_NAME.service; then
|
|
echo "✅ Cloudflared service is running"
|
|
else
|
|
echo "⚠️ Cloudflared service failed to start. Checking logs..."
|
|
sudo systemctl status $SERVICE_NAME.service
|
|
echo "Viewing recent logs:"
|
|
sudo journalctl -u $SERVICE_NAME.service --no-pager -n 20
|
|
|
|
echo "Please check the configuration and try again."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to create/update DNS record
|
|
create_dns_record() {
|
|
local name=$1
|
|
local full_name
|
|
local content="$CF_TUNNEL_ID.cfargotunnel.com"
|
|
|
|
# Determine the full DNS name
|
|
if [ "$name" == "@" ]; then
|
|
full_name="$CF_DOMAIN"
|
|
else
|
|
full_name="$name.$CF_DOMAIN"
|
|
fi
|
|
|
|
echo "Configuring DNS record for $full_name..."
|
|
|
|
# Check if record exists
|
|
EXISTING=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$full_name" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json")
|
|
|
|
RECORD_ID=$(echo "$EXISTING" | jq -r '.result[] | select(.name == "'$full_name'") | .id' | head -1)
|
|
|
|
if [ ! -z "$RECORD_ID" ] && [ "$RECORD_ID" != "null" ]; then
|
|
# Update existing record
|
|
RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"type\": \"CNAME\",
|
|
\"name\": \"$name\",
|
|
\"content\": \"$content\",
|
|
\"ttl\": 1,
|
|
\"proxied\": true
|
|
}")
|
|
else
|
|
# Create new record
|
|
RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"type\": \"CNAME\",
|
|
\"name\": \"$name\",
|
|
\"content\": \"$content\",
|
|
\"ttl\": 1,
|
|
\"proxied\": true
|
|
}")
|
|
fi
|
|
|
|
if echo "$RESPONSE" | jq -e '.success' > /dev/null; then
|
|
echo " ✅ DNS record configured"
|
|
else
|
|
echo " ❌ Failed to configure DNS record"
|
|
echo "$RESPONSE" | jq '.errors'
|
|
fi
|
|
}
|
|
|
|
# Set up access policies
|
|
setup_access_policies() {
|
|
echo ""
|
|
echo "Setting up Cloudflare Access policies..."
|
|
read -p "Enter email address for admin access to protected services: " ADMIN_EMAIL
|
|
|
|
if [[ "$ADMIN_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
|
# Protected services
|
|
PROTECTED_SERVICES=("homepage" "code")
|
|
|
|
for service in "${PROTECTED_SERVICES[@]}"; do
|
|
echo "Creating access policy for $service.$CF_DOMAIN..."
|
|
|
|
# Check if application already exists
|
|
EXISTING_APPS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json")
|
|
|
|
APP_ID=$(echo "$EXISTING_APPS" | jq -r ".result[] | select(.domain == \"$service.$CF_DOMAIN\") | .id" | head -1)
|
|
|
|
if [ -z "$APP_ID" ] || [ "$APP_ID" == "null" ]; then
|
|
# Create new access application
|
|
APP_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"name\": \"$service.$CF_DOMAIN\",
|
|
\"domain\": \"$service.$CF_DOMAIN\",
|
|
\"type\": \"self_hosted\",
|
|
\"session_duration\": \"24h\",
|
|
\"auto_redirect_to_identity\": false
|
|
}")
|
|
|
|
APP_ID=$(echo "$APP_RESPONSE" | jq -r '.result.id')
|
|
fi
|
|
|
|
if [ ! -z "$APP_ID" ] && [ "$APP_ID" != "null" ]; then
|
|
# Create/update policy
|
|
POLICY_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$APP_ID/policies" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"name\": \"Email Policy\",
|
|
\"decision\": \"allow\",
|
|
\"include\": [{
|
|
\"email\": {\"email\": \"$ADMIN_EMAIL\"}
|
|
}]
|
|
}")
|
|
|
|
if echo "$POLICY_RESPONSE" | jq -e '.success' > /dev/null; then
|
|
echo " ✅ Access policy configured"
|
|
else
|
|
echo " ⚠️ Policy may already exist or update failed"
|
|
fi
|
|
else
|
|
echo " ❌ Failed to create access application"
|
|
fi
|
|
done
|
|
else
|
|
echo "Invalid email format. Skipping access policy setup."
|
|
echo "You can configure access policies manually in the Cloudflare dashboard."
|
|
fi
|
|
}
|
|
|
|
# Check if services are running
|
|
check_services() {
|
|
echo "Checking if services are running..."
|
|
|
|
local all_running=true
|
|
|
|
# Check common ports to see if services are running
|
|
if ! nc -z localhost ${HOMEPAGE_PORT:-3010} >/dev/null 2>&1; then
|
|
echo "❌ Homepage service not detected on port ${HOMEPAGE_PORT:-3010}"
|
|
all_running=false
|
|
fi
|
|
|
|
if ! nc -z localhost ${CODE_SERVER_PORT:-8888} >/dev/null 2>&1; then
|
|
echo "❌ Code Server service not detected on port ${CODE_SERVER_PORT:-8888}"
|
|
all_running=false
|
|
fi
|
|
|
|
if ! nc -z localhost ${LISTMONK_PORT:-9000} >/dev/null 2>&1; then
|
|
echo "❌ Listmonk service not detected on port ${LISTMONK_PORT:-9000}"
|
|
all_running=false
|
|
fi
|
|
|
|
# At least check that some services are running
|
|
if [ "$all_running" = false ]; then
|
|
echo "Warning: Some services may not be running."
|
|
read -p "Continue anyway? (y/n): " continue_answer
|
|
if [[ ! "$continue_answer" =~ ^[Yy]$ ]]; then
|
|
echo "Please start your services with 'docker compose up -d' first."
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "✅ Services appear to be running"
|
|
fi
|
|
}
|
|
|
|
# Add this function to verify Cloudflare API credentials
|
|
verify_cloudflare_credentials() {
|
|
echo "Verifying Cloudflare API credentials..."
|
|
|
|
# Test the API token and Zone ID with a simple request
|
|
local TEST_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID" \
|
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
|
-H "Content-Type: application/json")
|
|
|
|
if echo "$TEST_RESPONSE" | jq -e '.success' > /dev/null 2>&1; then
|
|
echo "✅ Cloudflare API credentials verified successfully"
|
|
return 0
|
|
else
|
|
echo "❌ Cloudflare API credentials verification failed"
|
|
echo "Error details:"
|
|
echo "$TEST_RESPONSE" | jq '.errors'
|
|
echo ""
|
|
echo "Common issues:"
|
|
echo "1. Zone ID may be incorrect - make sure you're using the correct Zone ID from the Cloudflare dashboard"
|
|
echo "2. API Token may not have the required permissions - make sure it has Zone:DNS and Zone:Settings permissions"
|
|
echo ""
|
|
echo "Your current Zone ID: $CF_ZONE_ID"
|
|
echo "To continue without configuring DNS records, enter 'continue'. To quit and fix the credentials, enter 'quit'."
|
|
read -p "Continue or quit? [continue/quit]: " cf_choice
|
|
|
|
if [[ "$cf_choice" == "quit" ]]; then
|
|
echo "Exiting. Please update your credentials in .env and try again."
|
|
exit 1
|
|
else
|
|
echo "Continuing without configuring DNS records..."
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Main execution flow
|
|
echo "Starting Changemaker.lite production deployment..."
|
|
|
|
# Check if services are running
|
|
check_services
|
|
|
|
# Check if cloudflared is installed, install if needed
|
|
check_cloudflared
|
|
|
|
# Authenticate with Cloudflare
|
|
authenticate_cloudflare
|
|
|
|
# Set up tunnel
|
|
setup_tunnel
|
|
|
|
# Create systemd service
|
|
create_systemd_service
|
|
|
|
# Verify Cloudflare credentials before proceeding with DNS configuration
|
|
CF_CREDS_VALID=true
|
|
if ! verify_cloudflare_credentials; then
|
|
CF_CREDS_VALID=false
|
|
echo "Skipping DNS record creation and access policy setup due to invalid credentials"
|
|
fi
|
|
|
|
# Only proceed with DNS configuration if credentials are valid
|
|
if [ "$CF_CREDS_VALID" = true ]; then
|
|
# Create/update DNS records
|
|
echo ""
|
|
echo "Creating/updating DNS records..."
|
|
|
|
# Define subdomains
|
|
declare -A SUBDOMAINS=(
|
|
["homepage"]="Homepage"
|
|
["code"]="Code Server"
|
|
["listmonk"]="Listmonk"
|
|
["docs"]="Documentation"
|
|
["n8n"]="n8n"
|
|
["db"]="NocoDB"
|
|
["git"]="Gitea"
|
|
["map"]="Map"
|
|
["qr"]="Mini QR"
|
|
)
|
|
|
|
for subdomain in "${!SUBDOMAINS[@]}"; do
|
|
create_dns_record "$subdomain"
|
|
done
|
|
|
|
# Create root domain record
|
|
create_dns_record "@"
|
|
|
|
echo ""
|
|
echo "✅ All DNS records configured"
|
|
|
|
# Set up access policies
|
|
setup_access_policies
|
|
else
|
|
echo ""
|
|
echo "⚠️ DNS records and access policies were not configured."
|
|
echo "To manually configure DNS records, you can use the Cloudflare dashboard."
|
|
echo "Add CNAME records for each subdomain pointing to: $CF_TUNNEL_ID.cfargotunnel.com"
|
|
echo ""
|
|
fi
|
|
|
|
echo ""
|
|
echo "#############################################################"
|
|
echo "# "
|
|
echo "# Production Deployment Complete! "
|
|
echo "# "
|
|
echo "#############################################################"
|
|
echo ""
|
|
echo "Tunnel Name: $TUNNEL_NAME"
|
|
echo "Tunnel ID: $CF_TUNNEL_ID"
|
|
echo "Service Name: $SERVICE_NAME"
|
|
echo ""
|
|
echo "Your services are now accessible at:"
|
|
echo ""
|
|
echo "Public services:"
|
|
echo " - Main Site: https://$CF_DOMAIN"
|
|
echo " - Listmonk: https://listmonk.$CF_DOMAIN"
|
|
echo " - Documentation: https://docs.$CF_DOMAIN"
|
|
echo " - n8n: https://n8n.$CF_DOMAIN"
|
|
echo " - NocoDB: https://db.$CF_DOMAIN"
|
|
echo " - Gitea: https://git.$CF_DOMAIN"
|
|
echo " - Map: https://map.$CF_DOMAIN"
|
|
echo " - Mini QR: https://qr.$CF_DOMAIN"
|
|
echo ""
|
|
echo "Protected services (requires login with $ADMIN_EMAIL):"
|
|
echo " - Dashboard: https://homepage.$CF_DOMAIN"
|
|
echo " - Code Server: https://code.$CF_DOMAIN"
|
|
echo ""
|
|
echo "Cloudflared service status: sudo systemctl status $SERVICE_NAME"
|
|
echo "View tunnel logs: sudo journalctl -u $SERVICE_NAME -f"
|
|
echo "" |