2026-01-26 11:15:56 +01:00
|
|
|
#!/bin/bash
|
|
|
|
|
# Monaco USA Portal - Production Setup Script
|
|
|
|
|
# This script prepares the deployment environment by:
|
|
|
|
|
# 1. Generating missing secrets in .env
|
|
|
|
|
# 2. Generating kong.yml from template with actual API keys
|
|
|
|
|
# 3. Validating the configuration
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
echo "========================================"
|
|
|
|
|
echo "Monaco USA Portal - Production Setup"
|
|
|
|
|
echo "========================================"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Colors for output
|
|
|
|
|
RED='\033[0;31m'
|
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
YELLOW='\033[1;33m'
|
|
|
|
|
NC='\033[0m' # No Color
|
|
|
|
|
|
2026-01-26 11:25:47 +01:00
|
|
|
# Function to generate random string (alphanumeric only to avoid sed issues)
|
2026-01-26 11:15:56 +01:00
|
|
|
generate_secret() {
|
|
|
|
|
local length=${1:-32}
|
2026-01-26 11:25:47 +01:00
|
|
|
openssl rand -hex $length | head -c $length
|
2026-01-26 11:15:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Function to generate JWT token
|
|
|
|
|
generate_jwt() {
|
|
|
|
|
local role=$1
|
|
|
|
|
local secret=$2
|
|
|
|
|
|
|
|
|
|
# JWT Header (base64url encoded)
|
|
|
|
|
local header='{"alg":"HS256","typ":"JWT"}'
|
2026-01-26 11:25:47 +01:00
|
|
|
local header_b64=$(echo -n "$header" | base64 | tr '+/' '-_' | tr -d '=' | tr -d '\n')
|
2026-01-26 11:15:56 +01:00
|
|
|
|
|
|
|
|
# JWT Payload - 100 years expiry
|
|
|
|
|
local exp=$(($(date +%s) + 3153600000))
|
|
|
|
|
local payload="{\"role\":\"$role\",\"iss\":\"supabase\",\"iat\":$(date +%s),\"exp\":$exp}"
|
2026-01-26 11:25:47 +01:00
|
|
|
local payload_b64=$(echo -n "$payload" | base64 | tr '+/' '-_' | tr -d '=' | tr -d '\n')
|
2026-01-26 11:15:56 +01:00
|
|
|
|
|
|
|
|
# Create signature
|
2026-01-26 11:25:47 +01:00
|
|
|
local signature=$(echo -n "${header_b64}.${payload_b64}" | openssl dgst -sha256 -hmac "$secret" -binary | base64 | tr '+/' '-_' | tr -d '=' | tr -d '\n')
|
2026-01-26 11:15:56 +01:00
|
|
|
|
|
|
|
|
echo "${header_b64}.${payload_b64}.${signature}"
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-26 11:25:47 +01:00
|
|
|
# Function to update a variable in .env file using awk (more robust than sed)
|
|
|
|
|
update_env_var() {
|
|
|
|
|
local var_name=$1
|
|
|
|
|
local var_value=$2
|
|
|
|
|
local env_file=".env"
|
|
|
|
|
|
|
|
|
|
# Create temp file
|
|
|
|
|
local tmp_file=$(mktemp)
|
|
|
|
|
|
|
|
|
|
# Use awk to replace the line
|
|
|
|
|
awk -v name="$var_name" -v value="$var_value" '
|
|
|
|
|
BEGIN { FS="="; OFS="=" }
|
|
|
|
|
$1 == name { print name, value; next }
|
|
|
|
|
{ print }
|
|
|
|
|
' "$env_file" > "$tmp_file"
|
|
|
|
|
|
|
|
|
|
# Move temp file to .env
|
|
|
|
|
mv "$tmp_file" "$env_file"
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-26 11:15:56 +01:00
|
|
|
# Check if .env exists
|
|
|
|
|
if [ ! -f .env ]; then
|
|
|
|
|
if [ -f .env.example ]; then
|
|
|
|
|
echo -e "${YELLOW}No .env file found. Creating from .env.example...${NC}"
|
|
|
|
|
cp .env.example .env
|
|
|
|
|
echo -e "${GREEN}Created .env from template.${NC}"
|
|
|
|
|
else
|
|
|
|
|
echo -e "${RED}Error: No .env or .env.example file found.${NC}"
|
|
|
|
|
echo "Please create a .env file with your configuration."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-26 11:25:47 +01:00
|
|
|
# Load environment (handle errors gracefully)
|
|
|
|
|
set +e
|
2026-01-26 11:15:56 +01:00
|
|
|
set -a
|
2026-01-26 11:25:47 +01:00
|
|
|
source .env 2>/dev/null
|
2026-01-26 11:15:56 +01:00
|
|
|
set +a
|
2026-01-26 11:25:47 +01:00
|
|
|
set -e
|
2026-01-26 11:15:56 +01:00
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Checking and generating secrets..."
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Track if we made changes
|
|
|
|
|
CHANGES_MADE=false
|
|
|
|
|
|
|
|
|
|
# Generate POSTGRES_PASSWORD if not set or is placeholder
|
|
|
|
|
if [ -z "$POSTGRES_PASSWORD" ] || [[ "$POSTGRES_PASSWORD" == *"CHANGE_ME"* ]] || [[ "$POSTGRES_PASSWORD" == *"change-this"* ]]; then
|
|
|
|
|
NEW_POSTGRES_PASSWORD=$(generate_secret 32)
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "POSTGRES_PASSWORD" "$NEW_POSTGRES_PASSWORD"
|
2026-01-26 11:15:56 +01:00
|
|
|
POSTGRES_PASSWORD=$NEW_POSTGRES_PASSWORD
|
|
|
|
|
echo -e "${GREEN}[Generated]${NC} POSTGRES_PASSWORD"
|
|
|
|
|
CHANGES_MADE=true
|
|
|
|
|
else
|
|
|
|
|
echo -e "[OK] POSTGRES_PASSWORD is set"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Generate JWT_SECRET if not set or is placeholder
|
|
|
|
|
if [ -z "$JWT_SECRET" ] || [[ "$JWT_SECRET" == *"CHANGE_ME"* ]] || [[ "$JWT_SECRET" == *"generate"* ]]; then
|
|
|
|
|
NEW_JWT_SECRET=$(generate_secret 32)
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "JWT_SECRET" "$NEW_JWT_SECRET"
|
2026-01-26 11:15:56 +01:00
|
|
|
JWT_SECRET=$NEW_JWT_SECRET
|
|
|
|
|
echo -e "${GREEN}[Generated]${NC} JWT_SECRET"
|
|
|
|
|
CHANGES_MADE=true
|
|
|
|
|
else
|
|
|
|
|
echo -e "[OK] JWT_SECRET is set"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Generate SECRET_KEY_BASE if not set or is placeholder
|
|
|
|
|
if [ -z "$SECRET_KEY_BASE" ] || [[ "$SECRET_KEY_BASE" == *"CHANGE_ME"* ]] || [[ "$SECRET_KEY_BASE" == *"generate"* ]]; then
|
|
|
|
|
NEW_SECRET_KEY_BASE=$(generate_secret 64)
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "SECRET_KEY_BASE" "$NEW_SECRET_KEY_BASE"
|
2026-01-26 11:15:56 +01:00
|
|
|
SECRET_KEY_BASE=$NEW_SECRET_KEY_BASE
|
|
|
|
|
echo -e "${GREEN}[Generated]${NC} SECRET_KEY_BASE"
|
|
|
|
|
CHANGES_MADE=true
|
|
|
|
|
else
|
|
|
|
|
echo -e "[OK] SECRET_KEY_BASE is set"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Generate ANON_KEY if not set or is placeholder
|
|
|
|
|
if [ -z "$ANON_KEY" ] || [[ "$ANON_KEY" == *"CHANGE_ME"* ]] || [[ "$ANON_KEY" == *"your-"* ]]; then
|
|
|
|
|
NEW_ANON_KEY=$(generate_jwt "anon" "$JWT_SECRET")
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "ANON_KEY" "$NEW_ANON_KEY"
|
2026-01-26 11:15:56 +01:00
|
|
|
ANON_KEY=$NEW_ANON_KEY
|
|
|
|
|
echo -e "${GREEN}[Generated]${NC} ANON_KEY (JWT with role=anon)"
|
|
|
|
|
CHANGES_MADE=true
|
|
|
|
|
else
|
|
|
|
|
echo -e "[OK] ANON_KEY is set"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Generate SERVICE_ROLE_KEY if not set or is placeholder
|
|
|
|
|
if [ -z "$SERVICE_ROLE_KEY" ] || [[ "$SERVICE_ROLE_KEY" == *"CHANGE_ME"* ]] || [[ "$SERVICE_ROLE_KEY" == *"your-"* ]]; then
|
|
|
|
|
NEW_SERVICE_ROLE_KEY=$(generate_jwt "service_role" "$JWT_SECRET")
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "SERVICE_ROLE_KEY" "$NEW_SERVICE_ROLE_KEY"
|
2026-01-26 11:15:56 +01:00
|
|
|
SERVICE_ROLE_KEY=$NEW_SERVICE_ROLE_KEY
|
|
|
|
|
echo -e "${GREEN}[Generated]${NC} SERVICE_ROLE_KEY (JWT with role=service_role)"
|
|
|
|
|
CHANGES_MADE=true
|
|
|
|
|
else
|
|
|
|
|
echo -e "[OK] SERVICE_ROLE_KEY is set"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Also update PUBLIC_SUPABASE_ANON_KEY and SUPABASE_SERVICE_ROLE_KEY if they exist
|
|
|
|
|
if grep -q "^PUBLIC_SUPABASE_ANON_KEY=" .env; then
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "PUBLIC_SUPABASE_ANON_KEY" "\${ANON_KEY}"
|
2026-01-26 11:15:56 +01:00
|
|
|
fi
|
|
|
|
|
if grep -q "^SUPABASE_SERVICE_ROLE_KEY=" .env; then
|
2026-01-26 11:25:47 +01:00
|
|
|
update_env_var "SUPABASE_SERVICE_ROLE_KEY" "\${SERVICE_ROLE_KEY}"
|
2026-01-26 11:15:56 +01:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Reload environment after changes
|
2026-01-26 11:25:47 +01:00
|
|
|
set +e
|
2026-01-26 11:15:56 +01:00
|
|
|
set -a
|
2026-01-26 11:25:47 +01:00
|
|
|
source .env 2>/dev/null
|
2026-01-26 11:15:56 +01:00
|
|
|
set +a
|
2026-01-26 11:25:47 +01:00
|
|
|
set -e
|
2026-01-26 11:15:56 +01:00
|
|
|
|
|
|
|
|
# Validate required variables
|
|
|
|
|
echo "Validating required variables..."
|
|
|
|
|
REQUIRED_VARS=(
|
2026-01-26 11:34:25 +01:00
|
|
|
"PORTAL_DOMAIN"
|
|
|
|
|
"API_DOMAIN"
|
2026-01-26 11:15:56 +01:00
|
|
|
"POSTGRES_USER"
|
|
|
|
|
"POSTGRES_PASSWORD"
|
|
|
|
|
"POSTGRES_DB"
|
|
|
|
|
"JWT_SECRET"
|
|
|
|
|
"ANON_KEY"
|
|
|
|
|
"SERVICE_ROLE_KEY"
|
|
|
|
|
"SECRET_KEY_BASE"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
MISSING_VARS=()
|
|
|
|
|
for var in "${REQUIRED_VARS[@]}"; do
|
|
|
|
|
if [ -z "${!var}" ]; then
|
|
|
|
|
MISSING_VARS+=("$var")
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if [ ${#MISSING_VARS[@]} -ne 0 ]; then
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${RED}Error: The following required variables are not set in .env:${NC}"
|
|
|
|
|
for var in "${MISSING_VARS[@]}"; do
|
|
|
|
|
echo " - $var"
|
|
|
|
|
done
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Please edit .env and set these values, then run this script again."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo -e "${GREEN}All required variables are set.${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Check for optional but recommended variables
|
|
|
|
|
OPTIONAL_VARS=(
|
|
|
|
|
"SMTP_HOST"
|
|
|
|
|
"SMTP_USER"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
echo "Checking optional variables..."
|
|
|
|
|
for var in "${OPTIONAL_VARS[@]}"; do
|
|
|
|
|
if [ -z "${!var}" ]; then
|
|
|
|
|
echo -e "${YELLOW}[Warning]${NC} $var is not set (optional)"
|
|
|
|
|
else
|
|
|
|
|
echo -e "[OK] $var is set"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Generate kong.yml from template
|
|
|
|
|
echo "Generating kong.yml from template..."
|
|
|
|
|
|
|
|
|
|
if [ ! -f kong.yml.template ]; then
|
|
|
|
|
echo -e "${RED}Error: kong.yml.template not found.${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-26 11:25:47 +01:00
|
|
|
# Use awk to replace placeholders (more robust than sed for complex strings)
|
|
|
|
|
awk -v anon_key="$ANON_KEY" -v service_key="$SERVICE_ROLE_KEY" '
|
|
|
|
|
{
|
|
|
|
|
gsub(/__ANON_KEY__/, anon_key)
|
|
|
|
|
gsub(/__SERVICE_ROLE_KEY__/, service_key)
|
|
|
|
|
print
|
|
|
|
|
}
|
|
|
|
|
' kong.yml.template > kong.yml
|
2026-01-26 11:15:56 +01:00
|
|
|
|
|
|
|
|
echo -e "${GREEN}Generated kong.yml with API keys.${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Summary
|
|
|
|
|
echo "========================================"
|
|
|
|
|
echo "Setup Complete!"
|
|
|
|
|
echo "========================================"
|
|
|
|
|
echo ""
|
|
|
|
|
if [ "$CHANGES_MADE" = true ]; then
|
|
|
|
|
echo -e "${YELLOW}IMPORTANT: New secrets were generated and saved to .env${NC}"
|
|
|
|
|
echo "Make sure to backup your .env file securely!"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
echo "Next steps:"
|
|
|
|
|
echo " 1. Review .env and configure any remaining settings"
|
|
|
|
|
echo " 2. Start the services: docker compose up -d"
|
|
|
|
|
echo " 3. Wait for all containers to be healthy: docker compose ps"
|
2026-01-26 11:34:25 +01:00
|
|
|
echo " 4. Access the portal at: https://${PORTAL_DOMAIN:-portal.monacousa.org}"
|
2026-01-26 11:15:56 +01:00
|
|
|
echo " 5. Create your admin account on first visit"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Useful commands:"
|
|
|
|
|
echo " docker compose ps - Check container status"
|
|
|
|
|
echo " docker compose logs -f - Follow all logs"
|
|
|
|
|
echo " docker compose logs db - Check database logs"
|
|
|
|
|
echo " docker compose down - Stop all services"
|
|
|
|
|
echo ""
|