| Server IP : 65.108.144.40 / Your IP : 216.73.217.165 Web Server : Apache/2.4.52 (Ubuntu) System : Linux ubuntu-8gb-hel1-1 5.15.0-173-generic #183-Ubuntu SMP Fri Mar 6 13:29:34 UTC 2026 x86_64 User : dev ( 1000) PHP Version : 8.2.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/dev/webapps/certinia/ |
Upload File : |
#!/bin/bash
# ============================================================================
# Certinia Optimizely CMS - Linux Server Setup Script
# ============================================================================
# This script will:
# 1. Install all required dependencies (Docker, .NET 9, Node.js, Yarn)
# 2. Set up SQL Server 2022 in Docker
# 3. Import the preprod database from .bacpac file
# 4. Configure connection strings
# 5. Install frontend dependencies
# 6. Build the project
# ============================================================================
set -o pipefail
# Configuration
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SQL_CONTAINER_NAME="sqlserver2022"
SQL_SA_PASSWORD="YourStrong@Passw0rd"
SQL_PORT="1433"
DATABASE_NAME="Certinia_Cms_Preprod"
BACPAC_FILE="certinia_prod.bacpac"
SETUP_LOG="$PROJECT_ROOT/setup.log"
MAX_RETRIES=3
# Initialize log
echo "Setup started at $(date)" > "$SETUP_LOG"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ============================================================================
# Helper Functions
# ============================================================================
print_header() {
echo ""
echo -e "${BLUE}============================================================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}============================================================================${NC}"
echo ""
}
print_success() {
echo -e "${GREEN}✅ $1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}❌ $1${NC}"
}
print_info() {
echo -e "${BLUE}ℹ️ $1${NC}"
}
check_command() {
if command -v "$1" &> /dev/null; then
return 0
else
return 1
fi
}
cleanup_on_error() {
print_error "Setup failed. Check $SETUP_LOG for details."
exit 1
}
retry_command() {
local max_attempts=$1
shift
local cmd="$@"
local attempt=1
while [ $attempt -le $max_attempts ]; do
if eval "$cmd"; then
return 0
fi
print_warning "Attempt $attempt/$max_attempts failed. Retrying in 3 seconds..."
attempt=$((attempt + 1))
sleep 3
done
print_error "Command failed after $max_attempts attempts"
return 1
}
check_port_available() {
local port=$1
if ss -tuln | grep -q ":$port " || netstat -tuln 2>/dev/null | grep -q ":$port "; then
print_error "Port $port is already in use"
return 1
fi
return 0
}
validate_docker_running() {
if ! docker info &>/dev/null 2>&1; then
return 1
fi
return 0
}
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
DISTRO_VERSION=$VERSION_ID
else
DISTRO="unknown"
fi
}
# ============================================================================
# Step 0: Detect Linux Distribution
# ============================================================================
print_header "Step 0: Detecting Linux Distribution"
detect_distro
print_info "Detected distribution: $DISTRO $DISTRO_VERSION"
# Check if running as root or with sudo
if [ "$EUID" -ne 0 ]; then
print_warning "This script requires sudo privileges for package installation"
print_info "You may be prompted for your password"
SUDO="sudo"
else
SUDO=""
fi
# ============================================================================
# Step 1: Update System Packages
# ============================================================================
print_header "Step 1: Updating System Packages"
case "$DISTRO" in
ubuntu|debian)
print_info "Updating apt package lists..."
if ! $SUDO apt-get update 2>&1 | tee -a "$SETUP_LOG"; then
print_warning "apt-get update failed, continuing..."
fi
print_success "Package lists updated"
;;
centos|rhel|fedora|rocky|almalinux)
print_info "Updating yum/dnf repositories..."
if check_command dnf; then
PKG_MGR="dnf"
else
PKG_MGR="yum"
fi
if ! $SUDO $PKG_MGR check-update 2>&1 | tee -a "$SETUP_LOG"; then
print_info "Repository check completed"
fi
print_success "Repository updated"
;;
*)
print_warning "Unknown distribution, proceeding with caution..."
;;
esac
# ============================================================================
# Step 2: Install Required System Tools
# ============================================================================
print_header "Step 2: Installing Required System Tools"
REQUIRED_TOOLS="curl wget unzip jq ca-certificates gnupg lsb-release"
case "$DISTRO" in
ubuntu|debian)
print_info "Installing required tools via apt..."
$SUDO apt-get install -y curl wget unzip jq ca-certificates gnupg lsb-release apt-transport-https software-properties-common 2>&1 | tee -a "$SETUP_LOG"
;;
centos|rhel|fedora|rocky|almalinux)
print_info "Installing required tools via $PKG_MGR..."
$SUDO $PKG_MGR install -y curl wget unzip jq ca-certificates gnupg2 2>&1 | tee -a "$SETUP_LOG"
;;
esac
print_success "System tools installed"
# ============================================================================
# Step 3: Install Docker
# ============================================================================
print_header "Step 3: Installing Docker"
if check_command docker; then
print_success "Docker is already installed"
docker --version 2>&1 | tee -a "$SETUP_LOG"
else
print_info "Installing Docker..."
case "$DISTRO" in
ubuntu|debian)
# Remove old versions
$SUDO apt-get remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true
# Add Docker's official GPG key
$SUDO install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/$DISTRO/gpg | $SUDO gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$SUDO chmod a+r /etc/apt/keyrings/docker.gpg
# Set up the repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DISTRO \
$(lsb_release -cs) stable" | \
$SUDO tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
$SUDO apt-get update 2>&1 | tee -a "$SETUP_LOG"
$SUDO apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2>&1 | tee -a "$SETUP_LOG"
;;
centos|rhel|rocky|almalinux)
# Remove old versions
$SUDO $PKG_MGR remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 2>/dev/null || true
# Add Docker repository
$SUDO $PKG_MGR install -y yum-utils 2>&1 | tee -a "$SETUP_LOG"
$SUDO yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Install Docker Engine
$SUDO $PKG_MGR install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2>&1 | tee -a "$SETUP_LOG"
;;
fedora)
# Remove old versions
$SUDO dnf remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine 2>/dev/null || true
# Add Docker repository
$SUDO dnf -y install dnf-plugins-core
$SUDO dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
# Install Docker Engine
$SUDO dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2>&1 | tee -a "$SETUP_LOG"
;;
esac
print_success "Docker installed"
fi
# Start and enable Docker service
print_info "Starting Docker service..."
$SUDO systemctl start docker 2>&1 | tee -a "$SETUP_LOG"
$SUDO systemctl enable docker 2>&1 | tee -a "$SETUP_LOG"
# Add current user to docker group (if not root)
if [ "$EUID" -ne 0 ]; then
print_info "Adding current user to docker group..."
$SUDO usermod -aG docker $USER 2>&1 | tee -a "$SETUP_LOG"
print_warning "You may need to log out and back in for docker group changes to take effect"
print_info "Alternatively, run: newgrp docker"
fi
# Check if Docker is running
if ! validate_docker_running; then
print_error "Docker is not running after installation"
print_info "Please check Docker service status: systemctl status docker"
cleanup_on_error
fi
print_success "Docker is running"
docker --version 2>&1 | tee -a "$SETUP_LOG"
# ============================================================================
# Step 4: Install .NET 9.0 SDK
# ============================================================================
print_header "Step 4: Installing .NET 9.0 SDK"
if check_command dotnet; then
DOTNET_VERSION=$(dotnet --version 2>&1)
print_success ".NET SDK is installed: $DOTNET_VERSION"
# Check if .NET 9.x is installed
if ! dotnet --list-sdks 2>/dev/null | grep -q "^9\."; then
print_warning "Current .NET version is $DOTNET_VERSION, but project requires .NET 9.0"
print_info "Installing .NET 9.0 SDK..."
else
print_success ".NET 9.0 SDK is already installed"
fi
else
print_info "Installing .NET 9.0 SDK..."
fi
# Install .NET 9 (prefer OS package manager; fall back to Microsoft dotnet-install)
install_dotnet_9_via_script() {
print_info "Installing .NET 9.0 SDK via dotnet-install script..."
# Ensure prerequisites
if check_command apt-get; then
$SUDO apt-get update 2>&1 | tee -a "$SETUP_LOG"
$SUDO apt-get install -y ca-certificates curl tar gzip 2>&1 | tee -a "$SETUP_LOG"
elif check_command dnf; then
$SUDO dnf install -y ca-certificates curl tar gzip 2>&1 | tee -a "$SETUP_LOG"
elif check_command yum; then
$SUDO yum install -y ca-certificates curl tar gzip 2>&1 | tee -a "$SETUP_LOG"
fi
$SUDO mkdir -p /usr/local/dotnet 2>&1 | tee -a "$SETUP_LOG"
curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh 2>&1 | tee -a "$SETUP_LOG"
$SUDO chmod +x /tmp/dotnet-install.sh 2>&1 | tee -a "$SETUP_LOG"
# Install SDK into /usr/local/dotnet
$SUDO /tmp/dotnet-install.sh --install-dir /usr/local/dotnet --channel 9.0 --quality release --sdk 2>&1 | tee -a "$SETUP_LOG"
# Make dotnet available system-wide
$SUDO ln -sf /usr/local/dotnet/dotnet /usr/local/bin/dotnet 2>&1 | tee -a "$SETUP_LOG"
}
case "$DISTRO" in
ubuntu|debian)
# Add Microsoft package repository (best effort). Ubuntu/Debian repos may not always contain dotnet-sdk-9.0.
wget https://packages.microsoft.com/config/$DISTRO/$DISTRO_VERSION/packages-microsoft-prod.deb -O packages-microsoft-prod.deb 2>&1 | tee -a "$SETUP_LOG"
$SUDO dpkg -i packages-microsoft-prod.deb 2>&1 | tee -a "$SETUP_LOG"
rm packages-microsoft-prod.deb
$SUDO apt-get update 2>&1 | tee -a "$SETUP_LOG"
# Try apt package first; if unavailable, fall back to dotnet-install.
if ! $SUDO apt-get install -y dotnet-sdk-9.0 2>&1 | tee -a "$SETUP_LOG"; then
install_dotnet_9_via_script
fi
;;
centos|rhel|rocky|almalinux)
# Add Microsoft repository and try package install; fall back if unavailable
$SUDO $PKG_MGR install -y https://packages.microsoft.com/config/rhel/9/packages-microsoft-prod.rpm 2>&1 | tee -a "$SETUP_LOG"
if ! $SUDO $PKG_MGR install -y dotnet-sdk-9.0 2>&1 | tee -a "$SETUP_LOG"; then
install_dotnet_9_via_script
fi
;;
fedora)
# Add Microsoft repository and try package install; fall back if unavailable
$SUDO rpm --import https://packages.microsoft.com/keys/microsoft.asc 2>&1 | tee -a "$SETUP_LOG"
wget https://packages.microsoft.com/config/fedora/39/prod.repo 2>&1 | tee -a "$SETUP_LOG"
$SUDO mv prod.repo /etc/yum.repos.d/microsoft-prod.repo 2>&1 | tee -a "$SETUP_LOG"
$SUDO chown root:root /etc/yum.repos.d/microsoft-prod.repo 2>&1 | tee -a "$SETUP_LOG"
if ! $SUDO dnf install -y dotnet-sdk-9.0 2>&1 | tee -a "$SETUP_LOG"; then
install_dotnet_9_via_script
fi
;;
esac
# Verify .NET 9 is available (after fallback attempt)
if ! check_command dotnet; then
print_error "dotnet command not found after installation attempts"
cleanup_on_error
fi
if ! dotnet --list-sdks 2>/dev/null | grep -q "^9\."; then
print_error ".NET 9.0 SDK not found after installation"
print_info "Available SDKs:"
dotnet --list-sdks 2>&1 | tee -a "$SETUP_LOG"
print_info "If you must proceed with .NET 8, install it via: sudo apt-get install -y dotnet-sdk-8.0"
cleanup_on_error
fi
print_success ".NET 9.0 SDK is ready"
dotnet --version 2>&1 | tee -a "$SETUP_LOG"
# ============================================================================
# Step 5: Install Node.js and Yarn
# ============================================================================
print_header "Step 5: Installing Node.js and Yarn"
if check_command node; then
NODE_VERSION=$(node --version 2>&1)
print_success "Node.js is installed: $NODE_VERSION"
# Check Node version (should be >= 18)
NODE_MAJOR=$(echo $NODE_VERSION | cut -d'.' -f1 | sed 's/v//')
if [ "$NODE_MAJOR" -lt 18 ]; then
print_warning "Node.js version $NODE_VERSION is outdated. Installing latest LTS..."
fi
else
print_info "Installing Node.js..."
fi
# Install Node.js via NodeSource
case "$DISTRO" in
ubuntu|debian)
# Install Node.js 20.x LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | $SUDO -E bash - 2>&1 | tee -a "$SETUP_LOG"
$SUDO apt-get install -y nodejs 2>&1 | tee -a "$SETUP_LOG"
;;
centos|rhel|fedora|rocky|almalinux)
# Install Node.js 20.x LTS
curl -fsSL https://rpm.nodesource.com/setup_20.x | $SUDO bash - 2>&1 | tee -a "$SETUP_LOG"
$SUDO $PKG_MGR install -y nodejs 2>&1 | tee -a "$SETUP_LOG"
;;
esac
# Install Yarn
if check_command yarn; then
YARN_VERSION=$(yarn --version 2>&1)
print_success "Yarn is installed: $YARN_VERSION"
else
print_info "Installing Yarn..."
$SUDO npm install -g yarn 2>&1 | tee -a "$SETUP_LOG"
print_success "Yarn installed"
fi
# Verify installations
node --version 2>&1 | tee -a "$SETUP_LOG"
npm --version 2>&1 | tee -a "$SETUP_LOG"
yarn --version 2>&1 | tee -a "$SETUP_LOG"
# ============================================================================
# Step 6: Set Up SQL Server Container
# ============================================================================
print_header "Step 6: Setting Up SQL Server 2022 Container"
# Check port availability
if ! check_port_available $SQL_PORT; then
print_warning "Port $SQL_PORT is in use. Checking if it's our SQL Server container..."
if ! docker ps -a --format '{{.Names}}' | grep -q "^${SQL_CONTAINER_NAME}$"; then
print_error "Port $SQL_PORT is occupied by another process"
print_info "Please free up port $SQL_PORT or modify SQL_PORT in this script"
cleanup_on_error
fi
fi
# Check if container already exists
if docker ps -a --format '{{.Names}}' | grep -q "^${SQL_CONTAINER_NAME}$"; then
print_info "Container '$SQL_CONTAINER_NAME' already exists"
# Check if it's running
if docker ps --format '{{.Names}}' | grep -q "^${SQL_CONTAINER_NAME}$"; then
print_success "Container is already running"
else
print_info "Starting existing container..."
if ! docker start $SQL_CONTAINER_NAME 2>&1 | tee -a "$SETUP_LOG"; then
print_error "Failed to start existing container"
print_info "Removing old container and creating new one..."
docker rm -f $SQL_CONTAINER_NAME 2>&1 | tee -a "$SETUP_LOG" || true
# Create new container below
else
print_success "Container started"
fi
fi
fi
# Create container if it doesn't exist or was removed
if ! docker ps -a --format '{{.Names}}' | grep -q "^${SQL_CONTAINER_NAME}$"; then
print_info "Creating new SQL Server 2022 container..."
# Pull the image first
print_info "Pulling SQL Server image (this may take a few minutes)..."
if ! retry_command 2 "docker pull mcr.microsoft.com/mssql/server:2022-latest 2>&1 | tee -a '$SETUP_LOG'"; then
print_error "Failed to pull SQL Server image"
cleanup_on_error
fi
# Create and run container
if ! docker run -e "ACCEPT_EULA=Y" \
-e "MSSQL_SA_PASSWORD=$SQL_SA_PASSWORD" \
-e "MSSQL_PID=Developer" \
-p $SQL_PORT:1433 \
--name $SQL_CONTAINER_NAME \
--hostname sql1 \
-d mcr.microsoft.com/mssql/server:2022-latest 2>&1 | tee -a "$SETUP_LOG"; then
print_error "Failed to create SQL Server container"
cleanup_on_error
fi
print_success "SQL Server container created"
fi
# Wait for SQL Server to be ready
print_info "Waiting for SQL Server to be ready (this may take up to 60 seconds)..."
sleep 15
MAX_ATTEMPTS=60
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
if docker exec $SQL_CONTAINER_NAME /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$SQL_SA_PASSWORD" -C \
-Q "SELECT 1" &>/dev/null 2>&1; then
print_success "SQL Server is ready"
break
fi
# Check if container is still running
if ! docker ps --format '{{.Names}}' | grep -q "^${SQL_CONTAINER_NAME}$"; then
print_error "SQL Server container stopped unexpectedly"
print_info "Container logs:"
docker logs $SQL_CONTAINER_NAME 2>&1 | tail -20 | tee -a "$SETUP_LOG"
cleanup_on_error
fi
ATTEMPT=$((ATTEMPT + 1))
echo -n "."
sleep 2
done
echo ""
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
print_error "SQL Server failed to start within timeout"
print_info "Container logs:"
docker logs $SQL_CONTAINER_NAME 2>&1 | tail -20 | tee -a "$SETUP_LOG"
cleanup_on_error
fi
# Verify SQL Server is accessible
print_info "Verifying SQL Server connection..."
if ! docker exec $SQL_CONTAINER_NAME /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$SQL_SA_PASSWORD" -C \
-Q "SELECT @@VERSION" &>/dev/null 2>&1; then
print_error "Cannot connect to SQL Server"
cleanup_on_error
fi
print_success "SQL Server connection verified"
# ============================================================================
# Step 7: Download and Set Up sqlpackage
# ============================================================================
print_header "Step 7: Setting Up sqlpackage Tool"
SQLPACKAGE_DIR="$HOME/.local/sqlpackage"
SQLPACKAGE_BIN="$SQLPACKAGE_DIR/sqlpackage"
if [ -f "$SQLPACKAGE_BIN" ]; then
print_success "sqlpackage is already installed at $SQLPACKAGE_BIN"
if ! "$SQLPACKAGE_BIN" /version &>/dev/null 2>&1; then
print_warning "sqlpackage exists but doesn't work, reinstalling..."
rm -rf "$SQLPACKAGE_DIR"
fi
fi
if [ ! -f "$SQLPACKAGE_BIN" ]; then
print_info "Downloading sqlpackage..."
mkdir -p "$SQLPACKAGE_DIR"
# Determine architecture and OS
ARCH=$(uname -m)
case "$ARCH" in
x86_64)
SQLPACKAGE_URL="https://aka.ms/sqlpackage-linux"
print_info "Detected x64 architecture"
;;
aarch64|arm64)
SQLPACKAGE_URL="https://aka.ms/sqlpackage-linux-arm64"
print_info "Detected ARM64 architecture"
;;
*)
print_error "Unsupported architecture: $ARCH"
cleanup_on_error
;;
esac
print_info "Downloading from $SQLPACKAGE_URL..."
if ! retry_command 2 "curl -L --fail '$SQLPACKAGE_URL' -o '$SQLPACKAGE_DIR/sqlpackage.zip' 2>&1 | tee -a '$SETUP_LOG'"; then
print_error "Failed to download sqlpackage"
cleanup_on_error
fi
print_info "Extracting sqlpackage..."
if ! unzip -o -q "$SQLPACKAGE_DIR/sqlpackage.zip" -d "$SQLPACKAGE_DIR" 2>&1 | tee -a "$SETUP_LOG"; then
print_error "Failed to extract sqlpackage"
rm -f "$SQLPACKAGE_DIR/sqlpackage.zip"
cleanup_on_error
fi
rm -f "$SQLPACKAGE_DIR/sqlpackage.zip"
chmod +x "$SQLPACKAGE_BIN"
# Verify installation
if [ ! -f "$SQLPACKAGE_BIN" ]; then
print_error "sqlpackage binary not found after extraction"
cleanup_on_error
fi
print_success "sqlpackage installed successfully"
fi
# Test sqlpackage
if "$SQLPACKAGE_BIN" /version &>/dev/null 2>&1; then
print_success "sqlpackage is working correctly"
"$SQLPACKAGE_BIN" /version 2>&1 | head -1 | tee -a "$SETUP_LOG"
else
print_warning "sqlpackage may require additional dependencies"
print_info "Installing ICU libraries..."
case "$DISTRO" in
ubuntu|debian)
$SUDO apt-get install -y libicu-dev 2>&1 | tee -a "$SETUP_LOG"
;;
centos|rhel|fedora|rocky|almalinux)
$SUDO $PKG_MGR install -y libicu 2>&1 | tee -a "$SETUP_LOG"
;;
esac
fi
# ============================================================================
# Step 8: Import Database from .bacpac File
# ============================================================================
print_header "Step 8: Importing Database from .bacpac File"
# Check if .bacpac file exists
if [ ! -f "$PROJECT_ROOT/$BACPAC_FILE" ]; then
print_error "Database backup file not found: $BACPAC_FILE"
print_warning "Please place the .bacpac file in the project root directory"
print_info "Expected location: $PROJECT_ROOT/$BACPAC_FILE"
# List .bacpac files that might be available
print_info "Looking for .bacpac files in project root..."
FOUND_BACPACS=$(find "$PROJECT_ROOT" -maxdepth 1 -name "*.bacpac" -type f 2>/dev/null)
if [ -n "$FOUND_BACPACS" ]; then
print_info "Found .bacpac files:"
echo "$FOUND_BACPACS"
print_warning "Update BACPAC_FILE variable in script to use one of these files"
fi
read -p "Do you want to continue without importing the database? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
cleanup_on_error
fi
print_warning "Skipping database import - you'll need to import manually"
else
print_info "Found database backup: $BACPAC_FILE"
FILE_SIZE=$(du -h "$PROJECT_ROOT/$BACPAC_FILE" | cut -f1)
print_info "File size: $FILE_SIZE"
# Check if database already exists
DB_EXISTS=$(docker exec $SQL_CONTAINER_NAME /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$SQL_SA_PASSWORD" -C \
-Q "SELECT name FROM sys.databases WHERE name = N'$DATABASE_NAME'" -h -1 2>/dev/null | tr -d '[:space:]')
if [ "$DB_EXISTS" == "$DATABASE_NAME" ]; then
print_warning "Database '$DATABASE_NAME' already exists"
read -p "Do you want to drop and re-import? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
print_info "Dropping existing database..."
if docker exec $SQL_CONTAINER_NAME /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$SQL_SA_PASSWORD" -C \
-Q "ALTER DATABASE [$DATABASE_NAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [$DATABASE_NAME]" 2>&1 | tee -a "$SETUP_LOG"; then
print_success "Existing database dropped"
else
print_error "Failed to drop database"
cleanup_on_error
fi
else
print_info "Keeping existing database"
fi
fi
# Import database if it doesn't exist
DB_EXISTS=$(docker exec $SQL_CONTAINER_NAME /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$SQL_SA_PASSWORD" -C \
-Q "SELECT name FROM sys.databases WHERE name = N'$DATABASE_NAME'" -h -1 2>/dev/null | tr -d '[:space:]')
if [ "$DB_EXISTS" != "$DATABASE_NAME" ]; then
print_info "Importing database (this may take 5-15 minutes depending on file size)..."
print_warning "Please be patient - large databases take time to import"
if ! "$SQLPACKAGE_BIN" /Action:Import \
/SourceFile:"$PROJECT_ROOT/$BACPAC_FILE" \
/TargetConnectionString:"Server=localhost,$SQL_PORT;Database=$DATABASE_NAME;User Id=sa;Password=$SQL_SA_PASSWORD;TrustServerCertificate=True;Connection Timeout=300;" \
/p:CommandTimeout=600 \
/p:DatabaseMaximumSize=1024 \
/p:Storage=Memory 2>&1 | tee -a "$SETUP_LOG"; then
print_error "Database import failed"
print_info "Check $SETUP_LOG for details"
cleanup_on_error
fi
# Verify import
print_info "Verifying database import..."
DB_EXISTS=$(docker exec $SQL_CONTAINER_NAME /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P "$SQL_SA_PASSWORD" -C \
-Q "SELECT name FROM sys.databases WHERE name = N'$DATABASE_NAME'" -h -1 2>/dev/null | tr -d '[:space:]')
if [ "$DB_EXISTS" == "$DATABASE_NAME" ]; then
print_success "Database imported and verified successfully"
else
print_error "Database import verification failed"
cleanup_on_error
fi
else
print_success "Database already exists and ready to use"
fi
fi
# ============================================================================
# Step 9: Configure Connection Strings
# ============================================================================
print_header "Step 9: Configuring Connection Strings"
APPSETTINGS_FILE="$PROJECT_ROOT/source/Certinia.Web/appsettings.user.json"
APPSETTINGS_DIR="$(dirname "$APPSETTINGS_FILE")"
# Verify directory exists
if [ ! -d "$APPSETTINGS_DIR" ]; then
print_error "Certinia.Web directory not found at: $APPSETTINGS_DIR"
print_info "Please verify the project structure"
cleanup_on_error
fi
if [ -f "$APPSETTINGS_FILE" ]; then
print_success "appsettings.user.json already exists"
print_info "Current connection string will be preserved"
# Validate JSON
if ! python3 -c "import json; json.load(open('$APPSETTINGS_FILE'))" &>/dev/null; then
print_warning "Existing appsettings.user.json has invalid JSON, backing up and recreating..."
mv "$APPSETTINGS_FILE" "$APPSETTINGS_FILE.backup.$(date +%Y%m%d_%H%M%S)"
fi
fi
if [ ! -f "$APPSETTINGS_FILE" ]; then
print_info "Creating appsettings.user.json..."
cat > "$APPSETTINGS_FILE" << EOF
{
"ConnectionStrings": {
"EPiServerDB": "Server=localhost,$SQL_PORT;Database=$DATABASE_NAME;User Id=sa;Password=$SQL_SA_PASSWORD;TrustServerCertificate=True;MultipleActiveResultSets=True;Connection Timeout=120;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"EPiServer": "Information"
}
},
"AllowedHosts": "*"
}
EOF
# Validate the created file
if python3 -c "import json; json.load(open('$APPSETTINGS_FILE'))" &>/dev/null; then
print_success "appsettings.user.json created and validated"
else
print_error "Created appsettings.user.json has invalid JSON"
cleanup_on_error
fi
fi
# ============================================================================
# Step 10: Install Frontend Dependencies
# ============================================================================
print_header "Step 10: Installing Frontend Dependencies"
CLIENT_APP_DIR="$PROJECT_ROOT/source/Certinia.Web/ClientApp"
if [ ! -d "$CLIENT_APP_DIR" ]; then
print_warning "ClientApp directory not found at: $CLIENT_APP_DIR"
print_info "Skipping frontend dependencies installation"
else
cd "$CLIENT_APP_DIR" || {
print_error "Failed to change to ClientApp directory"
cleanup_on_error
}
# Check if package.json exists
if [ ! -f "package.json" ]; then
print_error "package.json not found in ClientApp directory"
cleanup_on_error
fi
print_info "Running yarn install (this may take a few minutes)..."
# Clean install if node_modules already exists
if [ -d "node_modules" ]; then
print_info "Cleaning previous installation..."
rm -rf node_modules 2>&1 | tee -a "$SETUP_LOG" || true
fi
# Try yarn install with retries
if ! retry_command $MAX_RETRIES "yarn install --network-timeout 100000 2>&1 | tee -a '$SETUP_LOG'"; then
print_error "yarn install failed after $MAX_RETRIES attempts"
print_info "Trying with npm as fallback..."
if ! retry_command 2 "npm install 2>&1 | tee -a '$SETUP_LOG'"; then
print_error "Both yarn and npm installation failed"
cleanup_on_error
fi
fi
# Verify installation
if [ ! -d "node_modules" ]; then
print_error "node_modules directory not created"
cleanup_on_error
fi
print_success "Frontend dependencies installed"
cd "$PROJECT_ROOT"
fi
# ============================================================================
# Step 11: Build Frontend Assets
# ============================================================================
print_header "Step 11: Building Frontend Assets"
if [ -d "$CLIENT_APP_DIR" ] && [ -f "$CLIENT_APP_DIR/package.json" ]; then
cd "$CLIENT_APP_DIR" || {
print_error "Failed to change to ClientApp directory"
cleanup_on_error
}
print_info "Running yarn build..."
if ! yarn build 2>&1 | tee -a "$SETUP_LOG"; then
print_warning "yarn build failed, trying npm run build..."
if ! npm run build 2>&1 | tee -a "$SETUP_LOG"; then
print_error "Frontend build failed"
print_warning "Continuing setup despite build failure..."
else
print_success "Frontend assets built with npm"
fi
else
print_success "Frontend assets built"
fi
cd "$PROJECT_ROOT"
else
print_warning "Skipping frontend build - ClientApp not found"
fi
# ============================================================================
# Step 12: Restore .NET Dependencies
# ============================================================================
print_header "Step 12: Restoring .NET Dependencies"
cd "$PROJECT_ROOT"
print_info "Running dotnet restore..."
if ! retry_command 2 "dotnet restore 2>&1 | tee -a '$SETUP_LOG'"; then
print_error "dotnet restore failed"
print_info "Check $SETUP_LOG for details"
cleanup_on_error
fi
print_success ".NET dependencies restored"
# ============================================================================
# Step 13: Build .NET Project
# ============================================================================
print_header "Step 13: Building .NET Project"
print_info "Running dotnet build..."
if ! dotnet build source/Certinia.Web/Certinia.Web.csproj 2>&1 | tee -a "$SETUP_LOG"; then
print_warning "Initial build failed. Attempting with detailed verbosity..."
if ! dotnet build source/Certinia.Web/Certinia.Web.csproj -v detailed 2>&1 | tee -a "$SETUP_LOG"; then
print_error "Build failed"
print_info "Check $SETUP_LOG for detailed error messages"
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
cleanup_on_error
fi
print_warning "Continuing despite build errors..."
else
print_success ".NET project built (detailed mode)"
fi
else
print_success ".NET project built"
fi
# ============================================================================
# Step 14: Configure Systemd Service (Optional)
# ============================================================================
print_header "Step 14: Configure Systemd Service (Optional)"
read -p "Do you want to set up the application as a systemd service for auto-start? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
print_info "Creating systemd service file..."
SERVICE_FILE="/etc/systemd/system/certinia-cms.service"
$SUDO tee "$SERVICE_FILE" > /dev/null << EOF
[Unit]
Description=Certinia Optimizely CMS
After=network.target docker.service
Requires=docker.service
[Service]
Type=notify
WorkingDirectory=$PROJECT_ROOT/source/Certinia.Web
ExecStart=/usr/bin/dotnet run --project $PROJECT_ROOT/source/Certinia.Web/Certinia.Web.csproj
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=certinia-cms
User=$USER
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
EOF
$SUDO systemctl daemon-reload
print_success "Systemd service created"
print_info "You can now use:"
echo " sudo systemctl start certinia-cms # Start the service"
echo " sudo systemctl stop certinia-cms # Stop the service"
echo " sudo systemctl enable certinia-cms # Enable auto-start on boot"
echo " sudo systemctl status certinia-cms # Check service status"
else
print_info "Skipping systemd service setup"
fi
# ============================================================================
# Setup Complete
# ============================================================================
print_header "🎉 Setup Complete!"
echo ""
print_success "All dependencies installed and configured successfully!"
echo ""
echo -e "${BLUE}Database Details:${NC}"
echo " Server: localhost,$SQL_PORT"
echo " Database: $DATABASE_NAME"
echo " Username: sa"
echo " Password: $SQL_SA_PASSWORD"
echo ""
echo -e "${BLUE}Next Steps:${NC}"
echo ""
echo "1. Start the application:"
echo -e " ${GREEN}cd $PROJECT_ROOT${NC}"
echo -e " ${GREEN}./run.sh${NC}"
echo ""
echo "2. Or run components separately:"
echo -e " ${GREEN}# Terminal 1: Backend${NC}"
echo -e " ${GREEN}dotnet run --project source/Certinia.Web/Certinia.Web.csproj${NC}"
echo ""
echo -e " ${GREEN}# Terminal 2: Frontend Watch Mode${NC}"
echo -e " ${GREEN}./watch-frontend.sh${NC}"
echo ""
echo "3. Access the application:"
echo -e " ${GREEN}https://localhost:5000${NC}"
echo ""
echo "4. Access Optimizely CMS Admin:"
echo -e " ${GREEN}https://localhost:5000/episerver/cms${NC}"
echo ""
echo -e "${BLUE}Docker Commands:${NC}"
print_info "SQL Server container management:"
echo -e " ${GREEN}docker stop $SQL_CONTAINER_NAME${NC} # Stop container"
echo -e " ${GREEN}docker start $SQL_CONTAINER_NAME${NC} # Start container"
echo -e " ${GREEN}docker logs $SQL_CONTAINER_NAME${NC} # View logs"
echo ""
print_info "Setup log saved to: $SETUP_LOG"
echo ""
print_success "Happy coding! 🚀"
echo ""