#!/bin/bash # Fix MockAPI to use PORT from .env file dynamically # This updates the systemd service to read HOST and PORT from .env at runtime set -e echo "=== Fix: Make MockAPI use PORT from .env dynamically ===" echo APP_DIR="/opt/mockapi" ENV_FILE="${APP_DIR}/.env" SERVICE_FILE="/etc/systemd/system/mockapi.service" WRAPPER_SCRIPT="${APP_DIR}/start_mockapi.sh" # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' print_status() { echo -e "${GREEN}[+]${NC} $1" } print_warning() { echo -e "${YELLOW}[!]${NC} $1" } print_error() { echo -e "${RED}[!]${NC} $1" } # Check if running as root if [[ $EUID -ne 0 ]]; then print_error "This script requires sudo privileges." print_warning "Run with: sudo bash $0" exit 1 fi # Step 1: Check if files exist if [[ ! -f "$ENV_FILE" ]]; then print_error ".env file not found: $ENV_FILE" exit 1 fi if [[ ! -f "$SERVICE_FILE" ]]; then print_error "Service file not found: $SERVICE_FILE" exit 1 fi # Step 2: Ensure PORT exists in .env print_status "Checking .env file for PORT variable..." if ! grep -q "^PORT=" "$ENV_FILE"; then print_warning "PORT not found in .env. Adding PORT=8000..." echo "PORT=8000" >> "$ENV_FILE" print_status "Added PORT=8000 to .env" else PORT_VALUE=$(grep "^PORT=" "$ENV_FILE" | cut -d'=' -f2) print_status "Found PORT in .env: $PORT_VALUE" # Validate PORT is numeric if [[ ! "$PORT_VALUE" =~ ^[0-9]+$ ]]; then print_error "PORT is not numeric: '$PORT_VALUE'. Fixing to 8000." sed -i "s/^PORT=.*/PORT=8000/" "$ENV_FILE" print_status "Updated PORT to 8000" fi fi # Step 3: Ensure HOST exists in .env if ! grep -q "^HOST=" "$ENV_FILE"; then print_warning "HOST not found in .env. Adding HOST=0.0.0.0..." echo "HOST=0.0.0.0" >> "$ENV_FILE" fi # Step 4: Ensure OAUTH2_ISSUER matches port CURRENT_PORT=$(grep "^PORT=" "$ENV_FILE" | cut -d'=' -f2) EXPECTED_ISSUER="http://localhost:${CURRENT_PORT}" if grep -q "^OAUTH2_ISSUER=" "$ENV_FILE"; then CURRENT_ISSUER=$(grep "^OAUTH2_ISSUER=" "$ENV_FILE" | cut -d'=' -f2) if [[ "$CURRENT_ISSUER" != "$EXPECTED_ISSUER" ]]; then print_status "Updating OAUTH2_ISSUER to match port..." sed -i "s|^OAUTH2_ISSUER=.*|OAUTH2_ISSUER=$EXPECTED_ISSUER|" "$ENV_FILE" fi else print_warning "OAUTH2_ISSUER not found. Adding: $EXPECTED_ISSUER" echo "OAUTH2_ISSUER=$EXPECTED_ISSUER" >> "$ENV_FILE" fi # Step 5: Create wrapper script print_status "Creating wrapper script: $WRAPPER_SCRIPT" cat > "$WRAPPER_SCRIPT" << 'EOF' #!/bin/bash # Wrapper script to start MockAPI with environment variables from .env # Source the .env file if [[ -f "/opt/mockapi/.env" ]]; then set -a # Automatically export all variables source "/opt/mockapi/.env" set +a else echo "ERROR: .env file not found at /opt/mockapi/.env" >&2 exit 1 fi # Validate PORT is numeric if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then echo "ERROR: PORT must be numeric, got: '$PORT'" >&2 exit 1 fi # Set defaults if not defined HOST=${HOST:-0.0.0.0} PORT=${PORT:-8000} # Debug info (only if DEBUG is true) if [[ "${DEBUG:-false}" == "true" ]] || [[ "${DEBUG:-false}" == "True" ]]; then echo "Starting MockAPI with HOST=$HOST, PORT=$PORT" echo "Using virtual environment: /opt/mockapi/venv" fi # Execute waitress with variables from .env exec /opt/mockapi/venv/bin/waitress-serve --host="$HOST" --port="$PORT" wsgi:wsgi_app EOF chmod +x "$WRAPPER_SCRIPT" print_status "Wrapper script created and made executable" # Step 6: Backup current service file print_status "Backing up current service file..." BACKUP_FILE="${SERVICE_FILE}.backup.$(date +%s)" cp "$SERVICE_FILE" "$BACKUP_FILE" print_status "Backup created: $BACKUP_FILE" # Step 7: Extract current settings from service file CURRENT_USER=$(grep "^User=" "$SERVICE_FILE" | cut -d'=' -f2) CURRENT_GROUP=$(grep "^Group=" "$SERVICE_FILE" | cut -d'=' -f2) CURRENT_VENV=$(grep 'Environment="PATH=' "$SERVICE_FILE" | cut -d'=' -f2 | cut -d':' -f1 | sed 's/"//g' || echo "/opt/mockapi/venv") print_status "Current service settings:" print_status " User: $CURRENT_USER" print_status " Group: $CURRENT_GROUP" print_status " Venv: $CURRENT_VENV" # Step 8: Create updated service file using wrapper script print_status "Updating service file to use wrapper script..." cat > "$SERVICE_FILE" << EOF [Unit] Description=Mock API Service After=network.target Wants=network.target [Service] Type=simple User=$CURRENT_USER Group=$CURRENT_GROUP WorkingDirectory=$APP_DIR Environment="PATH=$CURRENT_VENV/bin" Environment="PYTHONPATH=$APP_DIR" EnvironmentFile=$APP_DIR/.env # Use wrapper script that sources .env and expands variables ExecStart=$WRAPPER_SCRIPT Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=mockapi # Security hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ReadWritePaths=$APP_DIR [Install] WantedBy=multi-user.target EOF print_status "Service file updated to use wrapper script" # Step 9: Alternative approach - direct variable expansion (simpler) print_status "Also creating alternative simpler fix..." # Create a second service file alternative without wrapper script ALT_SERVICE="${SERVICE_FILE}.alternative" cat > "$ALT_SERVICE" << 'EOF' [Unit] Description=Mock API Service (Alternative - direct expansion) After=network.target Wants=network.target [Service] Type=simple User=$CURRENT_USER Group=$CURRENT_GROUP WorkingDirectory=$APP_DIR Environment="PATH=$CURRENT_VENV/bin" Environment="PYTHONPATH=$APP_DIR" EnvironmentFile=$APP_DIR/.env # Systemd expands variables from EnvironmentFile in ExecStart ExecStart=$CURRENT_VENV/bin/waitress-serve --host=$HOST --port=$PORT wsgi:wsgi_app Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=mockapi # Security hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ReadWritePaths=$APP_DIR [Install] WantedBy=multi-user.target EOF # Replace $CURRENT_USER, $CURRENT_GROUP, etc. in the alternative file sed -i "s|\\\$CURRENT_USER|$CURRENT_USER|g" "$ALT_SERVICE" sed -i "s|\\\$CURRENT_GROUP|$CURRENT_GROUP|g" "$ALT_SERVICE" sed -i "s|\\\$APP_DIR|$APP_DIR|g" "$ALT_SERVICE" sed -i "s|\\\$CURRENT_VENV|$CURRENT_VENV|g" "$ALT_SERVICE" print_status "Alternative service file created: $ALT_SERVICE" print_status "Note: The alternative uses direct systemd variable expansion" print_status " The main fix uses a wrapper script (more reliable)" # Step 10: Test which approach to use print_status "Testing variable expansion..." # Try the direct expansion approach first print_status "Trying direct expansion approach first..." # Update service file to use direct expansion (cleaner) cat > "$SERVICE_FILE" << EOF [Unit] Description=Mock API Service After=network.target Wants=network.target [Service] Type=simple User=$CURRENT_USER Group=$CURRENT_GROUP WorkingDirectory=$APP_DIR Environment="PATH=$CURRENT_VENV/bin" Environment="PYTHONPATH=$APP_DIR" EnvironmentFile=$APP_DIR/.env # Systemd expands variables from EnvironmentFile in ExecStart ExecStart=$CURRENT_VENV/bin/waitress-serve --host=\$HOST --port=\$PORT wsgi:wsgi_app Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=mockapi # Security hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ReadWritePaths=$APP_DIR [Install] WantedBy=multi-user.target EOF print_status "Updated service file with direct variable expansion" print_warning "Note: Using escaped \$HOST and \$PORT to prevent HERE-doc expansion" print_warning "Systemd should expand these from EnvironmentFile" # Step 11: Reload and restart print_status "Reloading systemd daemon..." systemctl daemon-reload print_status "Restarting MockAPI service..." if systemctl restart mockapi; then print_status "Service restart initiated..." sleep 3 if systemctl is-active --quiet mockapi; then print_status "✓ Service is running!" # Test with current port CURRENT_PORT=$(grep "^PORT=" "$ENV_FILE" | cut -d'=' -f2) print_status "Testing health endpoint on port $CURRENT_PORT..." if curl -f -s --max-time 10 http://localhost:$CURRENT_PORT/health > /dev/null 2>&1; then print_status "✓ Health check passed!" echo echo "=== SUCCESS: MockAPI now uses PORT from .env dynamically ===" echo echo "You can now change the port by editing:" echo " $ENV_FILE" echo "And updating the PORT variable." echo echo "Then restart the service:" echo " sudo systemctl restart mockapi" echo echo "Current configuration:" echo " PORT: $CURRENT_PORT" echo " Access: http://localhost:$CURRENT_PORT" echo " Health: http://localhost:$CURRENT_PORT/health" echo " Admin: http://localhost:$CURRENT_PORT/admin/login" else print_warning "Service running but health check failed." print_warning "Checking service logs..." journalctl -u mockapi -n 20 --no-pager fi else print_error "Service failed to start with direct expansion." print_status "Trying wrapper script approach..." # Use wrapper script approach cat > "$SERVICE_FILE" << EOF [Unit] Description=Mock API Service After=network.target Wants=network.target [Service] Type=simple User=$CURRENT_USER Group=$CURRENT_GROUP WorkingDirectory=$APP_DIR Environment="PATH=$CURRENT_VENV/bin" EnvironmentFile=$APP_DIR/.env # Use wrapper script ExecStart=$WRAPPER_SCRIPT Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=mockapi [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl restart mockapi sleep 3 if systemctl is-active --quiet mockapi; then print_status "✓ Service running with wrapper script!" else print_error "Both approaches failed. Check logs:" journalctl -u mockapi -n 30 --no-pager print_status "Restoring backup..." cp "$BACKUP_FILE" "$SERVICE_FILE" systemctl daemon-reload systemctl restart mockapi fi fi else print_error "Failed to restart service." systemctl status mockapi --no-pager -l fi echo print_status "=== Fix completed ===" echo "Backup files:" echo " Service file: $BACKUP_FILE" echo " Alternative: $ALT_SERVICE" echo " Wrapper script: $WRAPPER_SCRIPT" echo echo "To change port:" echo " 1. Edit $ENV_FILE and update PORT value" echo " 2. Run: sudo systemctl restart mockapi"