250 lines
11 KiB
Bash
250 lines
11 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# Deploy Backend API Containers for DBIS Core Banking System
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||
|
|
|
||
|
|
# Source utilities
|
||
|
|
source "$PROJECT_ROOT/dbis_core/scripts/utils/common.sh"
|
||
|
|
source "$PROJECT_ROOT/dbis_core/scripts/utils/dbis-core-utils.sh" 2>/dev/null || true
|
||
|
|
source "$PROJECT_ROOT/smom-dbis-138-proxmox/lib/container-utils.sh" 2>/dev/null || true
|
||
|
|
|
||
|
|
# Load configuration
|
||
|
|
load_config
|
||
|
|
|
||
|
|
log_info "========================================="
|
||
|
|
log_info "DBIS Core - API Deployment"
|
||
|
|
log_info "========================================="
|
||
|
|
log_info ""
|
||
|
|
|
||
|
|
check_root
|
||
|
|
if ! command_exists pct; then
|
||
|
|
error_exit "This script must be run on Proxmox host (pct command not found)"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Ensure OS template exists
|
||
|
|
ensure_os_template "${DBIS_CONTAINER_OS_TEMPLATE:-${CONTAINER_OS_TEMPLATE:-local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst}}" || {
|
||
|
|
error_exit "OS template not available. Please download it first."
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to create API container
|
||
|
|
create_api_container() {
|
||
|
|
local vmid="$1"
|
||
|
|
local hostname="$2"
|
||
|
|
local ip_address="$3"
|
||
|
|
local instance_name="${4:-primary}"
|
||
|
|
|
||
|
|
log_info "Creating API container: $hostname (VMID: $vmid, IP: $ip_address)"
|
||
|
|
|
||
|
|
if container_exists "$vmid"; then
|
||
|
|
log_warn "Container $vmid already exists, skipping creation"
|
||
|
|
else
|
||
|
|
log_info "Creating container $vmid..."
|
||
|
|
pct create "$vmid" \
|
||
|
|
"${DBIS_CONTAINER_OS_TEMPLATE:-${CONTAINER_OS_TEMPLATE:-local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst}}" \
|
||
|
|
--storage "${PROXMOX_STORAGE:-local-lvm}" \
|
||
|
|
--hostname "$hostname" \
|
||
|
|
--memory "${DBIS_API_MEMORY:-8192}" \
|
||
|
|
--cores "${DBIS_API_CORES:-4}" \
|
||
|
|
--rootfs "${PROXMOX_STORAGE:-local-lvm}:${DBIS_API_DISK:-100}" \
|
||
|
|
--net0 "bridge=${DBIS_NETWORK_BRIDGE:-vmbr0},name=eth0,ip=${ip_address}/24,gw=192.168.11.1,type=veth" \
|
||
|
|
--unprivileged "${DBIS_CONTAINER_UNPRIVILEGED:-1}" \
|
||
|
|
--swap "${DBIS_API_SWAP:-1024}" \
|
||
|
|
--onboot "${DBIS_CONTAINER_ONBOOT:-1}" \
|
||
|
|
--timezone "${DBIS_CONTAINER_TIMEZONE:-America/Los_Angeles}" \
|
||
|
|
--features nesting=1,keyctl=1
|
||
|
|
|
||
|
|
log_success "Container $vmid created"
|
||
|
|
fi
|
||
|
|
|
||
|
|
wait_for_container "$vmid"
|
||
|
|
|
||
|
|
# Configure container
|
||
|
|
log_info "Configuring container $vmid..."
|
||
|
|
pct set "$vmid" --features nesting=1,keyctl=1
|
||
|
|
|
||
|
|
# Start container and wait for readiness
|
||
|
|
if ! start_container_and_wait "$vmid"; then
|
||
|
|
log_error "Failed to start container $vmid"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Verify container is ready
|
||
|
|
if ! verify_container_ready "$vmid"; then
|
||
|
|
log_error "Container $vmid is not ready for file operations"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Configure locale
|
||
|
|
pct exec "$vmid" -- bash -c "export LC_ALL=C; export LANG=C; echo 'export LC_ALL=C' >> /root/.bashrc; echo 'export LANG=C' >> /root/.bashrc; echo 'export LC_ALL=C' >> /etc/environment; echo 'export LANG=C' >> /etc/environment" 2>/dev/null || true
|
||
|
|
|
||
|
|
# Update system
|
||
|
|
log_info "Updating system packages..."
|
||
|
|
pct exec "$vmid" -- bash -c "export DEBIAN_FRONTEND=noninteractive; rm -f /var/lib/apt/lists/lock /var/cache/apt/archives/lock /var/lib/dpkg/lock* 2>/dev/null || true; apt-get update -qq && apt-get upgrade -y -qq" 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
|
||
|
|
# Remove conflicting Node.js packages FIRST (before Node.js installation)
|
||
|
|
log_info "Removing conflicting Node.js packages..."
|
||
|
|
pct exec "$vmid" -- bash -c "export DEBIAN_FRONTEND=noninteractive; rm -f /var/lib/apt/lists/lock /var/cache/apt/archives/lock /var/lib/dpkg/lock* 2>/dev/null || true; dpkg -r --force-depends nodejs libnode72 nodejs-doc 2>/dev/null || true; apt-get remove -y -qq nodejs libnode72 nodejs-doc 2>/dev/null || true; apt-get autoremove -y -qq 2>/dev/null || true; apt-get update -qq" 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
|
||
|
|
# Install curl (required for Node.js installation) - check first if already installed
|
||
|
|
if pct exec "$vmid" -- command -v curl >/dev/null 2>&1; then
|
||
|
|
log_info "Curl already installed"
|
||
|
|
else
|
||
|
|
log_info "Installing curl..."
|
||
|
|
pct exec "$vmid" -- bash -c "export DEBIAN_FRONTEND=noninteractive; rm -f /var/lib/apt/lists/lock /var/cache/apt/archives/lock /var/lib/dpkg/lock* 2>/dev/null || true; dpkg --configure -a 2>/dev/null || true; apt-get install -y -qq curl" 2>&1 | grep -vE "(perl: warning|locale:)" || {
|
||
|
|
if ! pct exec "$vmid" -- command -v curl >/dev/null 2>&1; then
|
||
|
|
log_error "Failed to install curl"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Install Node.js using nvm (Node Version Manager) to avoid conflicts
|
||
|
|
log_info "Installing Node.js ${DBIS_NODE_VERSION:-18} using nvm..."
|
||
|
|
pct exec "$vmid" -- bash -c "export NVM_DIR=\"/root/.nvm\"; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash && [ -s \"\$NVM_DIR/nvm.sh\" ] && . \"\$NVM_DIR/nvm.sh\" && nvm install ${DBIS_NODE_VERSION:-18} && nvm use ${DBIS_NODE_VERSION:-18} && nvm alias default ${DBIS_NODE_VERSION:-18}" 2>&1 | grep -vE "(perl: warning|locale:)" || {
|
||
|
|
log_warn "nvm installation may have issues, checking Node.js..."
|
||
|
|
if ! pct exec "$vmid" -- bash -c "source /root/.nvm/nvm.sh 2>/dev/null && node --version" 2>/dev/null; then
|
||
|
|
log_error "Failed to install Node.js via nvm"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create symlink for node and npm in /usr/local/bin for system-wide access
|
||
|
|
pct exec "$vmid" -- bash -c "source /root/.nvm/nvm.sh 2>/dev/null && ln -sf \$(nvm which node) /usr/local/bin/node && ln -sf \$(nvm which npm) /usr/local/bin/npm 2>/dev/null || true"
|
||
|
|
|
||
|
|
# Install PM2 globally
|
||
|
|
log_info "Installing PM2 process manager..."
|
||
|
|
pct exec "$vmid" -- bash -c "npm install -g pm2" 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
|
||
|
|
# Create application directory
|
||
|
|
log_info "Setting up application directory..."
|
||
|
|
pct exec "$vmid" -- bash -c "mkdir -p ${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core}" 2>/dev/null || true
|
||
|
|
|
||
|
|
# Copy dbis_core repository to container
|
||
|
|
log_info "Copying DBIS Core repository to container..."
|
||
|
|
if [[ -d "$PROJECT_ROOT/dbis_core" ]]; then
|
||
|
|
# Use tar to copy files (pct push doesn't support recursive)
|
||
|
|
log_info "Pushing repository files to container..."
|
||
|
|
local temp_tar="/tmp/dbis_core_$$.tar.gz"
|
||
|
|
tar czf "$temp_tar" -C "$PROJECT_ROOT" dbis_core 2>/dev/null
|
||
|
|
if [[ -f "$temp_tar" ]]; then
|
||
|
|
pct push "$vmid" "$temp_tar" /tmp/dbis_core.tar.gz 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
pct exec "$vmid" -- bash -c "cd /opt && tar xzf /tmp/dbis_core.tar.gz && mv dbis_core dbis-core 2>/dev/null && rm -f /tmp/dbis_core.tar.gz" 2>&1 | grep -vE "(perl: warning|locale:)" || {
|
||
|
|
log_warn "Failed to extract repository, will clone instead"
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt} && git clone https://github.com/Order-of-Hospitallers/dbis_core.git dbis-core 2>/dev/null || true" || true
|
||
|
|
}
|
||
|
|
rm -f "$temp_tar" 2>/dev/null || true
|
||
|
|
else
|
||
|
|
log_warn "Failed to create tar archive, will clone instead"
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt} && git clone https://github.com/Order-of-Hospitallers/dbis_core.git dbis-core 2>/dev/null || true" || true
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_warn "Local repository not found, will need to clone from git"
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt} && git clone https://github.com/Order-of-Hospitallers/dbis_core.git dbis-core 2>/dev/null || true" || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Install dependencies
|
||
|
|
log_info "Installing npm dependencies..."
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core} && npm ci 2>&1 | tail -20" 2>&1 | grep -vE "(perl: warning|locale:)" || {
|
||
|
|
log_warn "npm ci failed, trying npm install"
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core} && npm install 2>&1 | tail -20" 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
}
|
||
|
|
|
||
|
|
# Generate Prisma client
|
||
|
|
log_info "Generating Prisma client..."
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core} && npx prisma generate 2>&1 | tail -10" 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
|
||
|
|
# Build TypeScript
|
||
|
|
log_info "Building TypeScript..."
|
||
|
|
pct exec "$vmid" -- bash -c "cd ${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core} && npm run build 2>&1 | tail -20" 2>&1 | grep -vE "(perl: warning|locale:)" || true
|
||
|
|
|
||
|
|
# Create environment file
|
||
|
|
log_info "Creating environment configuration..."
|
||
|
|
local db_host="${DBIS_POSTGRES_PRIMARY_IP:-192.168.11.100}"
|
||
|
|
local db_name="${DBIS_DB_NAME:-dbis_core}"
|
||
|
|
local db_user="${DBIS_DB_USER:-dbis}"
|
||
|
|
local db_password="${DBIS_DB_PASSWORD:-}"
|
||
|
|
local redis_host="${DBIS_REDIS_IP:-192.168.11.120}"
|
||
|
|
local jwt_secret="${JWT_SECRET:-$(generate_jwt_secret)}"
|
||
|
|
|
||
|
|
pct exec "$vmid" -- bash -c "cat > ${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core}/.env <<EOF
|
||
|
|
DATABASE_URL=postgresql://${db_user}:${db_password}@${db_host}:5432/${db_name}
|
||
|
|
JWT_SECRET=${jwt_secret}
|
||
|
|
ALLOWED_ORIGINS=http://${DBIS_FRONTEND_IP:-192.168.11.130},https://${DBIS_FRONTEND_IP:-192.168.11.130}
|
||
|
|
NODE_ENV=production
|
||
|
|
LOG_LEVEL=info
|
||
|
|
HSM_ENABLED=false
|
||
|
|
REDIS_URL=redis://${redis_host}:6379
|
||
|
|
PORT=${DBIS_API_PORT:-3000}
|
||
|
|
EOF
|
||
|
|
" 2>/dev/null || true
|
||
|
|
|
||
|
|
# Create systemd service
|
||
|
|
log_info "Creating systemd service..."
|
||
|
|
pct exec "$vmid" -- bash -c "cat > /etc/systemd/system/dbis-api.service <<EOF
|
||
|
|
[Unit]
|
||
|
|
Description=DBIS Core API Server
|
||
|
|
After=network.target
|
||
|
|
|
||
|
|
[Service]
|
||
|
|
Type=simple
|
||
|
|
User=root
|
||
|
|
WorkingDirectory=${DBIS_CORE_PROJECT_ROOT:-/opt/dbis-core}
|
||
|
|
Environment=NODE_ENV=production
|
||
|
|
ExecStart=/usr/bin/node dist/index.js
|
||
|
|
Restart=always
|
||
|
|
RestartSec=10
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
EOF
|
||
|
|
" 2>/dev/null || true
|
||
|
|
|
||
|
|
# Enable and start service
|
||
|
|
log_info "Starting API service..."
|
||
|
|
pct exec "$vmid" -- systemctl daemon-reload 2>/dev/null || true
|
||
|
|
pct exec "$vmid" -- systemctl enable dbis-api 2>/dev/null || true
|
||
|
|
pct exec "$vmid" -- systemctl start dbis-api 2>/dev/null || true
|
||
|
|
|
||
|
|
# Wait for service to be ready
|
||
|
|
log_info "Waiting for API service to be ready..."
|
||
|
|
sleep 5
|
||
|
|
|
||
|
|
# Configure firewall
|
||
|
|
if pct exec "$vmid" -- command -v ufw >/dev/null 2>&1; then
|
||
|
|
log_info "Configuring firewall..."
|
||
|
|
pct exec "$vmid" -- bash -c "ufw allow ${DBIS_API_PORT:-3000}/tcp comment 'DBIS API'" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
log_success "API container $hostname (VMID: $vmid) deployed successfully"
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
# Deploy API Primary
|
||
|
|
log_info "Deploying API Primary..."
|
||
|
|
create_api_container \
|
||
|
|
"${VMID_DBIS_API_PRIMARY:-10150}" \
|
||
|
|
"dbis-api-primary" \
|
||
|
|
"${DBIS_API_PRIMARY_IP:-192.168.11.150}" \
|
||
|
|
"primary"
|
||
|
|
|
||
|
|
# Deploy API Secondary (if HA enabled)
|
||
|
|
if [[ "${DBIS_ENABLE_HA:-true}" == "true" ]] && [[ "${DBIS_API_COUNT:-2}" -ge 2 ]]; then
|
||
|
|
log_info "Deploying API Secondary..."
|
||
|
|
create_api_container \
|
||
|
|
"${VMID_DBIS_API_SECONDARY:-10151}" \
|
||
|
|
"dbis-api-secondary" \
|
||
|
|
"${DBIS_API_SECONDARY_IP:-192.168.11.151}" \
|
||
|
|
"secondary"
|
||
|
|
fi
|
||
|
|
|
||
|
|
log_success "API deployment completed!"
|
||
|
|
log_info ""
|
||
|
|
log_info "Next steps:"
|
||
|
|
log_info "1. Run database migrations: ./scripts/deployment/configure-database.sh"
|
||
|
|
log_info "2. Deploy Frontend: ./scripts/deployment/deploy-frontend.sh"
|
||
|
|
log_info "3. Check API status: ./scripts/management/status.sh"
|
||
|
|
|