446 lines
14 KiB
Bash
446 lines
14 KiB
Bash
#!/bin/bash
|
|
|
|
# PDF Sort and Merge Application - Ubuntu Installation Script
|
|
# This script installs and configures the Flask web application for PDF processing
|
|
|
|
set -e # Exit on any error
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Check if running as root
|
|
if [[ $EUID -eq 0 ]]; then
|
|
log_error "This script should not be run as root. Please run as a regular user with sudo privileges."
|
|
exit 1
|
|
fi
|
|
|
|
# Check Ubuntu version
|
|
UBUNTU_VERSION=$(lsb_release -rs)
|
|
log_info "Detected Ubuntu version: $UBUNTU_VERSION"
|
|
|
|
# Update system
|
|
log_info "Updating system packages..."
|
|
sudo apt update && sudo apt upgrade -y
|
|
|
|
# Install Python 3.9+ and pip
|
|
log_info "Installing Python and pip..."
|
|
sudo apt install -y python3 python3-pip python3-venv python3-dev
|
|
|
|
# Install system dependencies for PDF processing
|
|
log_info "Installing system dependencies..."
|
|
sudo apt install -y build-essential libpoppler-cpp-dev pkg-config
|
|
|
|
# Install Node.js and npm (for potential frontend assets)
|
|
log_info "Installing Node.js and npm..."
|
|
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
|
sudo apt install -y nodejs
|
|
|
|
# Create application directory
|
|
APP_DIR="$HOME/pdf-sort-merge"
|
|
log_info "Creating application directory: $APP_DIR"
|
|
mkdir -p "$APP_DIR"
|
|
cd "$APP_DIR"
|
|
|
|
# Clone or download the application
|
|
REPO_URL="https://gitlab.com/trbc-dev/eoy-pdf-sortandmerge.git"
|
|
log_info "Cloning application repository..."
|
|
if command -v git &> /dev/null; then
|
|
sudo apt install -y git
|
|
if [[ -d ".git" ]]; then
|
|
log_info "Repository already exists, pulling latest changes..."
|
|
git pull origin web-app
|
|
git checkout web-app
|
|
else
|
|
log_info "Cloning fresh repository..."
|
|
git clone "$REPO_URL" .
|
|
git checkout web-app
|
|
fi
|
|
else
|
|
log_warning "Git not available. Please manually download and extract the application files to $APP_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Create Python virtual environment
|
|
log_info "Creating Python virtual environment..."
|
|
python3 -m venv venv
|
|
source venv/bin/activate
|
|
|
|
# Install Python dependencies
|
|
log_info "Installing Python dependencies..."
|
|
pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
|
|
# Create .env file with Azure AD configuration
|
|
log_info "Configuring Azure AD authentication..."
|
|
echo ""
|
|
echo "=================================================================="
|
|
echo "AZURE AD CONFIGURATION REQUIRED"
|
|
echo "=================================================================="
|
|
echo "You need to provide your Azure AD app registration details."
|
|
echo "If you don't have them, follow these steps:"
|
|
echo "1. Go to https://portal.azure.com"
|
|
echo "2. Navigate to Azure Active Directory > App registrations"
|
|
echo "3. Create a new registration or use existing one"
|
|
echo "4. Note the Application (client) ID and Directory (tenant) ID"
|
|
echo "5. Create a client secret under Certificates & secrets"
|
|
echo "6. Add redirect URI: http://your-domain-name/auth/callback (will be configured automatically)"
|
|
echo "=================================================================="
|
|
echo ""
|
|
|
|
# Prompt for Azure AD credentials
|
|
read -p "Enter Azure AD Tenant ID (Directory ID): " AZURE_TENANT_ID
|
|
read -p "Enter Azure AD Client ID (Application ID): " AZURE_CLIENT_ID
|
|
read -s -p "Enter Azure AD Client Secret: " AZURE_CLIENT_SECRET
|
|
echo ""
|
|
read -p "Enter DNS domain name (e.g., eoypdf.trbc.org) [localhost]: " DNS_DOMAIN
|
|
DNS_DOMAIN=${DNS_DOMAIN:-localhost}
|
|
read -p "Enter server IP/domain (for redirect URI) [$DNS_DOMAIN]: " SERVER_HOST
|
|
SERVER_HOST=${SERVER_HOST:-$DNS_DOMAIN}
|
|
|
|
# Determine if we should use HTTPS
|
|
USE_HTTPS=false
|
|
GENERATE_SELF_SIGNED=false
|
|
if [[ "$DNS_DOMAIN" != "localhost" && "$DNS_DOMAIN" != "127.0.0.1" ]]; then
|
|
read -p "Do you have SSL certificates for $DNS_DOMAIN? (y/n) [n]: " HAS_SSL
|
|
if [[ "${HAS_SSL,,}" == "y" || "${HAS_SSL,,}" == "yes" ]]; then
|
|
USE_HTTPS=true
|
|
else
|
|
read -p "Would you like to generate self-signed SSL certificates? (y/n) [y]: " GEN_SELF
|
|
GEN_SELF=${GEN_SELF:-y}
|
|
if [[ "${GEN_SELF,,}" == "y" || "${GEN_SELF,,}" == "yes" ]]; then
|
|
USE_HTTPS=true
|
|
GENERATE_SELF_SIGNED=true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Create .env file with real values
|
|
cat > .env << EOF
|
|
# Microsoft Entra (Azure AD) Configuration
|
|
# Get these values from your Azure AD app registration in the Azure portal
|
|
|
|
# Azure AD Tenant ID (Directory ID)
|
|
AZURE_TENANT_ID=$AZURE_TENANT_ID
|
|
|
|
# Azure AD Client ID (Application ID)
|
|
AZURE_CLIENT_ID=$AZURE_CLIENT_ID
|
|
|
|
# Azure AD Client Secret
|
|
AZURE_CLIENT_SECRET=$AZURE_CLIENT_SECRET
|
|
|
|
# Azure AD Authority URL (usually https://login.microsoftonline.com/{tenant_id})
|
|
AZURE_AUTHORITY=https://login.microsoftonline.com/$AZURE_TENANT_ID
|
|
|
|
# Redirect URI (must match what's configured in Azure AD app registration)
|
|
AZURE_REDIRECT_URI=http${USE_HTTPS:+s}://$SERVER_HOST/auth/callback
|
|
|
|
# Scopes to request (openid, profile, email are common)
|
|
AZURE_SCOPES=openid profile email
|
|
|
|
# Flask Configuration
|
|
SECRET_KEY=$(openssl rand -hex 32)
|
|
EOF
|
|
|
|
log_success "Azure AD configuration saved to .env file"
|
|
|
|
# Generate self-signed SSL certificates if requested
|
|
if [[ "$GENERATE_SELF_SIGNED" == "true" ]]; then
|
|
log_info "Generating self-signed SSL certificates..."
|
|
sudo mkdir -p /etc/ssl/certs /etc/ssl/private
|
|
|
|
# Generate self-signed certificate
|
|
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
|
-keyout /etc/ssl/private/pdf-sort-merge.key \
|
|
-out /etc/ssl/certs/pdf-sort-merge.crt \
|
|
-subj "/C=US/ST=State/L=City/O=Organization/CN=$DNS_DOMAIN" \
|
|
-addext "subjectAltName=DNS:$DNS_DOMAIN"
|
|
|
|
log_success "Self-signed SSL certificates generated"
|
|
fi
|
|
|
|
# Install and configure nginx
|
|
log_info "Installing and configuring nginx..."
|
|
sudo apt install -y nginx
|
|
|
|
# Create nginx configuration
|
|
NGINX_CONF="/etc/nginx/sites-available/pdf-sort-merge"
|
|
if [[ "$USE_HTTPS" == "true" ]]; then
|
|
# HTTPS configuration with SSL
|
|
sudo tee "$NGINX_CONF" > /dev/null << EOF
|
|
server {
|
|
listen 80;
|
|
server_name $DNS_DOMAIN;
|
|
return 301 https://\$server_name\$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name $DNS_DOMAIN;
|
|
|
|
ssl_certificate /etc/ssl/certs/pdf-sort-merge.crt;
|
|
ssl_certificate_key /etc/ssl/private/pdf-sort-merge.key;
|
|
|
|
# SSL security settings
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
|
|
# Security headers
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
|
|
# Proxy to Flask app
|
|
location / {
|
|
proxy_pass http://127.0.0.1:5000;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
|
|
# WebSocket support (if needed)
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# Timeout settings
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
# Static files
|
|
location /static/ {
|
|
alias $APP_DIR/static/;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
# HTTP-only configuration
|
|
sudo tee "$NGINX_CONF" > /dev/null << EOF
|
|
server {
|
|
listen 80;
|
|
server_name $DNS_DOMAIN;
|
|
|
|
# Security headers
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
|
|
|
# Proxy to Flask app
|
|
location / {
|
|
proxy_pass http://127.0.0.1:5000;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
|
|
# WebSocket support (if needed)
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# Timeout settings
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
# Static files
|
|
location /static/ {
|
|
alias $APP_DIR/static/;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# Enable the site
|
|
sudo ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/
|
|
sudo rm -f /etc/nginx/sites-enabled/default
|
|
|
|
# Test nginx configuration
|
|
log_info "Testing nginx configuration..."
|
|
if sudo nginx -t; then
|
|
log_success "Nginx configuration is valid"
|
|
else
|
|
log_error "Nginx configuration has errors"
|
|
exit 1
|
|
fi
|
|
|
|
# Restart nginx
|
|
sudo systemctl restart nginx
|
|
sudo systemctl enable nginx
|
|
|
|
log_success "Nginx configured to serve $DNS_DOMAIN on port 80"
|
|
|
|
# Create systemd service file
|
|
log_info "Creating systemd service..."
|
|
SERVICE_FILE="/etc/systemd/system/pdf-sort-merge.service"
|
|
sudo tee "$SERVICE_FILE" > /dev/null << EOF
|
|
[Unit]
|
|
Description=PDF Sort and Merge Flask Application
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$USER
|
|
WorkingDirectory=$APP_DIR
|
|
Environment=PATH=$APP_DIR/venv/bin
|
|
ExecStart=$APP_DIR/venv/bin/python $APP_DIR/app.py
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# Set proper permissions
|
|
sudo chmod 644 "$SERVICE_FILE"
|
|
|
|
# Create log directory
|
|
log_info "Creating log directory..."
|
|
sudo mkdir -p /var/log/pdf-sort-merge
|
|
sudo chown $USER:$USER /var/log/pdf-sort-merge
|
|
|
|
# Create application data directories
|
|
log_info "Creating application data directories..."
|
|
mkdir -p results static templates
|
|
|
|
# Create Flask session directory for server-side sessions
|
|
log_info "Creating Flask session directory..."
|
|
sudo mkdir -p /tmp/flask_sessions
|
|
sudo chown $USER:$USER /tmp/flask_sessions
|
|
|
|
# Set up firewall (optional)
|
|
log_info "Configuring firewall..."
|
|
sudo ufw allow 80/tcp || log_warning "Failed to configure firewall for port 80 - you may need to do this manually"
|
|
if [[ "$USE_HTTPS" == "true" ]]; then
|
|
sudo ufw allow 443/tcp || log_warning "Failed to configure firewall for port 443 - you may need to do this manually"
|
|
fi
|
|
sudo ufw allow ssh/tcp || log_warning "Failed to configure firewall for SSH - you may need to do this manually"
|
|
|
|
# Enable and start service
|
|
log_info "Enabling and starting service..."
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable pdf-sort-merge
|
|
sudo systemctl start pdf-sort-merge
|
|
|
|
# Check service status
|
|
sleep 3
|
|
if sudo systemctl is-active --quiet pdf-sort-merge; then
|
|
log_success "Service started successfully!"
|
|
else
|
|
log_error "Service failed to start. Check logs with: sudo journalctl -u pdf-sort-merge -f"
|
|
fi
|
|
|
|
# Create management script
|
|
log_info "Creating management script..."
|
|
cat > manage.sh << 'EOF'
|
|
#!/bin/bash
|
|
|
|
case "$1" in
|
|
start)
|
|
sudo systemctl start pdf-sort-merge
|
|
echo "Service started"
|
|
;;
|
|
stop)
|
|
sudo systemctl stop pdf-sort-merge
|
|
echo "Service stopped"
|
|
;;
|
|
restart)
|
|
sudo systemctl restart pdf-sort-merge
|
|
echo "Service restarted"
|
|
;;
|
|
status)
|
|
sudo systemctl status pdf-sort-merge
|
|
;;
|
|
logs)
|
|
sudo journalctl -u pdf-sort-merge -f
|
|
;;
|
|
update)
|
|
cd /home/$USER/pdf-sort-merge
|
|
source venv/bin/activate
|
|
git pull
|
|
pip install -r requirements.txt
|
|
sudo systemctl restart pdf-sort-merge
|
|
echo "Application updated"
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {start|stop|restart|status|logs|update}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
EOF
|
|
|
|
chmod +x manage.sh
|
|
|
|
# Final instructions
|
|
log_success "Installation completed!"
|
|
echo ""
|
|
echo "=================================================================="
|
|
echo "NEXT STEPS:"
|
|
echo "1. Point your DNS for $DNS_DOMAIN to this server's IP address"
|
|
echo ""
|
|
if [[ "$GENERATE_SELF_SIGNED" == "true" ]]; then
|
|
echo "2. IMPORTANT: Self-signed certificates are now active!"
|
|
echo " - Browsers will show a security warning - click 'Advanced' and 'Proceed'"
|
|
echo " - For production, consider getting a proper SSL certificate from Let's Encrypt"
|
|
echo ""
|
|
fi
|
|
echo "3. Update your Azure AD app registration redirect URI to:"
|
|
if [[ "$USE_HTTPS" == "true" ]]; then
|
|
echo " https://$SERVER_HOST/auth/callback"
|
|
else
|
|
echo " http://$SERVER_HOST/auth/callback"
|
|
fi
|
|
echo ""
|
|
echo "4. Access the application at:"
|
|
if [[ "$USE_HTTPS" == "true" ]]; then
|
|
echo " https://$DNS_DOMAIN"
|
|
else
|
|
echo " http://$DNS_DOMAIN"
|
|
fi
|
|
echo ""
|
|
echo "MANAGEMENT COMMANDS:"
|
|
echo " Start service: sudo systemctl start pdf-sort-merge"
|
|
echo " Stop service: sudo systemctl stop pdf-sort-merge"
|
|
echo " Restart nginx: sudo systemctl restart nginx"
|
|
echo " Check status: sudo systemctl status pdf-sort-merge"
|
|
echo " View logs: sudo journalctl -u pdf-sort-merge -f"
|
|
echo " Update app: ./manage.sh update"
|
|
echo "=================================================================="
|
|
|
|
# Check if .env needs configuration
|
|
if grep -q "your-tenant-id-here" .env; then
|
|
log_warning "IMPORTANT: Configure your .env file before using the application!"
|
|
fi |