#!/bin/bash # Desk-Moloni v3.0 Installation Script # # Complete installation and setup automation for the Desk-Moloni module. # Handles database setup, permissions, configuration, and initial setup. set -euo pipefail # Script configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MODULE_DIR="$(dirname "$SCRIPT_DIR")" PROJECT_ROOT="$(dirname "$(dirname "$MODULE_DIR")")" # Default configuration DEFAULT_DB_HOST="localhost" DEFAULT_DB_NAME="perfex_crm" DEFAULT_DB_USER="root" DEFAULT_WEB_USER="www-data" DEFAULT_ENVIRONMENT="production" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Installation state INSTALL_LOG="$MODULE_DIR/install.log" BACKUP_DIR="$MODULE_DIR/backups/$(date +%Y%m%d_%H%M%S)" # Logging functions log_info() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" echo -e "${BLUE}$msg${NC}" echo "$msg" >> "$INSTALL_LOG" } log_success() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS] $1" echo -e "${GREEN}$msg${NC}" echo "$msg" >> "$INSTALL_LOG" } log_warning() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $1" echo -e "${YELLOW}$msg${NC}" echo "$msg" >> "$INSTALL_LOG" } log_error() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" echo -e "${RED}$msg${NC}" echo "$msg" >> "$INSTALL_LOG" } log_step() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [STEP] $1" echo -e "${CYAN}=== $1 ===${NC}" echo "$msg" >> "$INSTALL_LOG" } # Help function show_help() { cat << EOF Desk-Moloni v3.0 Installation Script Usage: $0 [OPTIONS] Options: -h, --help Show this help message --db-host HOST Database host (default: $DEFAULT_DB_HOST) --db-name DATABASE Database name (default: $DEFAULT_DB_NAME) --db-user USER Database user (default: $DEFAULT_DB_USER) --db-password PASSWORD Database password (prompted if not provided) --web-user USER Web server user (default: $DEFAULT_WEB_USER) --environment ENV Environment: development|production (default: $DEFAULT_ENVIRONMENT) --skip-database Skip database installation --skip-cron Skip cron job setup --skip-permissions Skip file permission setup --force Force installation (overwrite existing) --dry-run Show what would be done without making changes --uninstall Remove the module and all data --backup Create backup before installation --restore BACKUP_PATH Restore from backup Installation Steps: 1. Pre-installation checks 2. Backup existing data (if --backup) 3. Database schema installation 4. File permission setup 5. Configuration initialization 6. Cron job setup 7. Post-installation verification Examples: $0 # Interactive installation $0 --db-name perfex --db-user admin # Specify database settings $0 --environment development # Development environment $0 --dry-run # Preview installation $0 --uninstall # Remove module $0 --backup # Backup before install EOF } # Parse command line arguments DB_HOST="$DEFAULT_DB_HOST" DB_NAME="$DEFAULT_DB_NAME" DB_USER="$DEFAULT_DB_USER" DB_PASSWORD="" WEB_USER="$DEFAULT_WEB_USER" ENVIRONMENT="$DEFAULT_ENVIRONMENT" SKIP_DATABASE=false SKIP_CRON=false SKIP_PERMISSIONS=false FORCE_INSTALL=false DRY_RUN=false UNINSTALL=false BACKUP_BEFORE_INSTALL=false RESTORE_BACKUP="" while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_help exit 0 ;; --db-host) DB_HOST="$2" shift 2 ;; --db-name) DB_NAME="$2" shift 2 ;; --db-user) DB_USER="$2" shift 2 ;; --db-password) DB_PASSWORD="$2" shift 2 ;; --web-user) WEB_USER="$2" shift 2 ;; --environment) ENVIRONMENT="$2" shift 2 ;; --skip-database) SKIP_DATABASE=true shift ;; --skip-cron) SKIP_CRON=true shift ;; --skip-permissions) SKIP_PERMISSIONS=true shift ;; --force) FORCE_INSTALL=true shift ;; --dry-run) DRY_RUN=true shift ;; --uninstall) UNINSTALL=true shift ;; --backup) BACKUP_BEFORE_INSTALL=true shift ;; --restore) RESTORE_BACKUP="$2" shift 2 ;; *) log_error "Unknown option: $1" show_help exit 1 ;; esac done # Validate environment if [[ ! "$ENVIRONMENT" =~ ^(development|production)$ ]]; then log_error "Invalid environment: $ENVIRONMENT (must be development or production)" exit 1 fi # Create installation log mkdir -p "$(dirname "$INSTALL_LOG")" touch "$INSTALL_LOG" # Pre-installation checks check_requirements() { log_step "Pre-installation Requirements Check" local errors=0 # Check if running as root or with sudo if [[ $EUID -ne 0 ]] && [[ -z "${SUDO_USER:-}" ]]; then log_error "This script must be run as root or with sudo" ((errors++)) fi # Check PHP version if ! command -v php &> /dev/null; then log_error "PHP is not installed" ((errors++)) else local php_version php_version=$(php -r "echo PHP_VERSION_ID;" 2>/dev/null || echo "0") if [[ "$php_version" -lt 80400 ]]; then log_error "PHP 8.4 or higher is required (current: $(php -r "echo PHP_VERSION;"))" ((errors++)) else log_success "PHP version: $(php -r "echo PHP_VERSION;")" fi fi # Check required PHP extensions local required_extensions=("mysqli" "pdo" "pdo_mysql" "json" "curl" "openssl" "mbstring") for ext in "${required_extensions[@]}"; do if ! php -m | grep -q "^$ext$"; then log_error "Required PHP extension missing: $ext" ((errors++)) else log_success "PHP extension available: $ext" fi done # Check MySQL/MariaDB client if ! command -v mysql &> /dev/null; then log_error "MySQL client is not installed" ((errors++)) else log_success "MySQL client available" fi # Check web server user if ! id "$WEB_USER" &>/dev/null; then log_error "Web server user '$WEB_USER' does not exist" ((errors++)) else log_success "Web server user '$WEB_USER' exists" fi # Check Perfex CRM installation local perfex_config="$PROJECT_ROOT/application/config/config.php" if [[ ! -f "$perfex_config" ]]; then log_error "Perfex CRM config not found at: $perfex_config" log_error "Please ensure this script is run from within a Perfex CRM installation" ((errors++)) else log_success "Perfex CRM installation detected" fi if [[ $errors -gt 0 ]]; then log_error "Pre-installation checks failed with $errors errors" exit 1 fi log_success "All requirements satisfied" } # Interactive configuration interactive_config() { if [[ "$DRY_RUN" == true ]] || [[ "$UNINSTALL" == true ]] || [[ -n "$RESTORE_BACKUP" ]]; then return fi log_step "Interactive Configuration" # Database password if [[ -z "$DB_PASSWORD" ]]; then echo -n "Database password for $DB_USER@$DB_HOST: " read -s DB_PASSWORD echo if [[ -z "$DB_PASSWORD" ]]; then log_error "Database password is required" exit 1 fi fi # Confirm settings echo -e "\n${CYAN}Installation Configuration:${NC}" echo "Database Host: $DB_HOST" echo "Database Name: $DB_NAME" echo "Database User: $DB_USER" echo "Web Server User: $WEB_USER" echo "Environment: $ENVIRONMENT" echo "Module Path: $MODULE_DIR" if [[ "$FORCE_INSTALL" == false ]]; then echo -n "Proceed with installation? [y/N]: " read -r confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then log_info "Installation cancelled by user" exit 0 fi fi } # Test database connection test_database_connection() { log_step "Testing Database Connection" if [[ "$SKIP_DATABASE" == true ]]; then log_warning "Skipping database connection test" return fi local mysql_cmd="mysql -h$DB_HOST -u$DB_USER -p$DB_PASSWORD" if [[ "$DRY_RUN" == true ]]; then log_info "Would test database connection to $DB_HOST" return fi # Test connection if ! echo "SELECT 1;" | $mysql_cmd "$DB_NAME" &>/dev/null; then log_error "Failed to connect to database $DB_NAME on $DB_HOST" exit 1 fi log_success "Database connection successful" } # Create backup create_backup() { if [[ "$BACKUP_BEFORE_INSTALL" == false ]] && [[ "$UNINSTALL" == false ]]; then return fi log_step "Creating Backup" if [[ "$DRY_RUN" == true ]]; then log_info "Would create backup in: $BACKUP_DIR" return fi mkdir -p "$BACKUP_DIR" # Backup database tables if [[ "$SKIP_DATABASE" == false ]]; then local tables=( "desk_moloni_config" "desk_moloni_mapping" "desk_moloni_sync_queue" "desk_moloni_sync_log" ) local mysql_cmd="mysql -h$DB_HOST -u$DB_USER -p$DB_PASSWORD" for table in "${tables[@]}"; do if echo "SHOW TABLES LIKE '$table';" | $mysql_cmd "$DB_NAME" | grep -q "$table"; then log_info "Backing up table: $table" mysqldump -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" "$table" > "$BACKUP_DIR/$table.sql" fi done fi # Backup configuration files if [[ -d "$MODULE_DIR/config" ]]; then cp -r "$MODULE_DIR/config" "$BACKUP_DIR/" log_info "Configuration files backed up" fi # Backup logs (recent only) if [[ -d "$MODULE_DIR/logs" ]]; then find "$MODULE_DIR/logs" -name "*.log" -mtime -7 -exec cp {} "$BACKUP_DIR/" \; log_info "Recent log files backed up" fi # Create backup manifest cat > "$BACKUP_DIR/manifest.txt" << EOF Desk-Moloni v3.0 Backup Created: $(date) Version: 3.0.0 Environment: $ENVIRONMENT Database: $DB_NAME Host: $DB_HOST EOF log_success "Backup created: $BACKUP_DIR" } # Install database schema install_database() { if [[ "$SKIP_DATABASE" == true ]]; then log_warning "Skipping database installation" return fi log_step "Installing Database Schema" local mysql_cmd="mysql -h$DB_HOST -u$DB_USER -p$DB_PASSWORD $DB_NAME" local schema_file="$MODULE_DIR/sql/schema.sql" if [[ "$DRY_RUN" == true ]]; then log_info "Would install database schema from: $schema_file" return fi # Create schema file if it doesn't exist if [[ ! -f "$schema_file" ]]; then log_info "Creating database schema file" mkdir -p "$(dirname "$schema_file")" cat > "$schema_file" << 'EOF' -- Desk-Moloni v3.0 Database Schema -- Auto-generated installation schema -- Configuration table CREATE TABLE IF NOT EXISTS desk_moloni_config ( id INT AUTO_INCREMENT PRIMARY KEY, setting_key VARCHAR(255) NOT NULL UNIQUE, setting_value TEXT, encrypted TINYINT(1) DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Entity mapping table CREATE TABLE IF NOT EXISTS desk_moloni_mapping ( id INT AUTO_INCREMENT PRIMARY KEY, entity_type ENUM('client', 'product', 'invoice', 'estimate', 'credit_note') NOT NULL, perfex_id INT NOT NULL, moloni_id INT NOT NULL, sync_direction ENUM('perfex_to_moloni', 'moloni_to_perfex', 'bidirectional') DEFAULT 'bidirectional', last_sync_at TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY unique_perfex_mapping (entity_type, perfex_id), UNIQUE KEY unique_moloni_mapping (entity_type, moloni_id), INDEX idx_entity_perfex (entity_type, perfex_id), INDEX idx_entity_moloni (entity_type, moloni_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Sync queue table CREATE TABLE IF NOT EXISTS desk_moloni_sync_queue ( id INT AUTO_INCREMENT PRIMARY KEY, task_type ENUM('sync_client', 'sync_product', 'sync_invoice', 'sync_estimate', 'sync_credit_note', 'status_update') NOT NULL, entity_type ENUM('client', 'product', 'invoice', 'estimate', 'credit_note') NOT NULL, entity_id INT NOT NULL, priority TINYINT DEFAULT 5, payload JSON, status ENUM('pending', 'processing', 'completed', 'failed', 'retry') DEFAULT 'pending', attempts INT DEFAULT 0, max_attempts INT DEFAULT 3, scheduled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, started_at TIMESTAMP NULL, completed_at TIMESTAMP NULL, error_message TEXT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_status_priority (status, priority, scheduled_at), INDEX idx_entity (entity_type, entity_id), INDEX idx_scheduled (scheduled_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Sync log table CREATE TABLE IF NOT EXISTS desk_moloni_sync_log ( id INT AUTO_INCREMENT PRIMARY KEY, operation_type ENUM('create', 'update', 'delete', 'status_change') NOT NULL, entity_type ENUM('client', 'product', 'invoice', 'estimate', 'credit_note') NOT NULL, perfex_id INT, moloni_id INT, direction ENUM('perfex_to_moloni', 'moloni_to_perfex') NOT NULL, status ENUM('success', 'error', 'warning') NOT NULL, request_data JSON, response_data JSON, error_message TEXT NULL, execution_time_ms INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_entity_status (entity_type, status, created_at), INDEX idx_perfex_entity (perfex_id, entity_type), INDEX idx_moloni_entity (moloni_id, entity_type), INDEX idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Insert default configuration INSERT IGNORE INTO desk_moloni_config (setting_key, setting_value, encrypted) VALUES ('module_version', '3.0.0', 0), ('installation_date', NOW(), 0), ('sync_enabled', '1', 0), ('queue_batch_size', '10', 0), ('max_retry_attempts', '3', 0), ('api_rate_limit', '60', 0); EOF log_success "Database schema file created" fi # Execute schema log_info "Executing database schema..." if $mysql_cmd < "$schema_file"; then log_success "Database schema installed successfully" else log_error "Failed to install database schema" exit 1 fi # Verify tables were created local tables=("desk_moloni_config" "desk_moloni_mapping" "desk_moloni_sync_queue" "desk_moloni_sync_log") for table in "${tables[@]}"; do if echo "SHOW TABLES LIKE '$table';" | $mysql_cmd | grep -q "$table"; then log_success "Table created: $table" else log_error "Failed to create table: $table" exit 1 fi done } # Setup file permissions setup_permissions() { if [[ "$SKIP_PERMISSIONS" == true ]]; then log_warning "Skipping permission setup" return fi log_step "Setting Up File Permissions" local directories=( "$MODULE_DIR/logs" "$MODULE_DIR/locks" "$MODULE_DIR/cache" "$MODULE_DIR/temp" ) if [[ "$DRY_RUN" == true ]]; then log_info "Would create directories and set permissions" return fi # Create directories for dir in "${directories[@]}"; do if [[ ! -d "$dir" ]]; then mkdir -p "$dir" log_info "Created directory: $dir" fi chown -R "$WEB_USER:$WEB_USER" "$dir" chmod 755 "$dir" log_success "Permissions set for: $dir" done # Set permissions on CLI scripts local cli_files=( "$MODULE_DIR/cli/queue_processor.php" "$MODULE_DIR/cli/sync_commands.php" ) for file in "${cli_files[@]}"; do if [[ -f "$file" ]]; then chmod +x "$file" log_success "Made executable: $file" fi done # Set permissions on shell scripts find "$MODULE_DIR/scripts" -name "*.sh" -type f -exec chmod +x {} \; log_success "Made shell scripts executable" } # Initialize configuration initialize_config() { log_step "Initializing Configuration" local config_dir="$MODULE_DIR/config" local config_file="$config_dir/config.php" if [[ "$DRY_RUN" == true ]]; then log_info "Would initialize configuration files" return fi mkdir -p "$config_dir" # Create main configuration file cat > "$config_file" << EOF '$ENVIRONMENT', 'version' => '3.0.0', 'installation_date' => '$(date -u +%Y-%m-%dT%H:%M:%SZ)', // Database configuration 'database' => [ 'host' => '$DB_HOST', 'database' => '$DB_NAME', 'username' => '$DB_USER', // Password stored securely in Perfex config ], // Queue configuration 'queue' => [ 'batch_size' => 10, 'max_attempts' => 3, 'retry_delay' => 300, // 5 minutes 'max_execution_time' => 300, // 5 minutes ], // API configuration 'api' => [ 'rate_limit' => 60, // requests per minute 'timeout' => 30, // seconds 'retry_attempts' => 3, ], // Logging configuration 'logging' => [ 'level' => '$ENVIRONMENT' === 'development' ? 'debug' : 'info', 'retention_days' => 30, 'max_file_size' => '10M', ], // Security configuration 'security' => [ 'encryption_method' => 'AES-256-GCM', 'token_refresh_threshold' => 300, // 5 minutes before expiry ], ]; EOF chown "$WEB_USER:$WEB_USER" "$config_file" chmod 644 "$config_file" log_success "Configuration file created: $config_file" # Create environment-specific config local env_config="$config_dir/config.$ENVIRONMENT.php" cat > "$env_config" << EOF $([ "$ENVIRONMENT" == "development" ] && echo "true" || echo "false"), 'log_level' => '$([ "$ENVIRONMENT" == "development" ] && echo "debug" || echo "info")', 'api_timeout' => $([ "$ENVIRONMENT" == "development" ] && echo "60" || echo "30"), ]; EOF chown "$WEB_USER:$WEB_USER" "$env_config" chmod 644 "$env_config" log_success "Environment configuration created: $env_config" } # Setup cron jobs setup_cron_jobs() { if [[ "$SKIP_CRON" == true ]]; then log_warning "Skipping cron job setup" return fi log_step "Setting Up Cron Jobs" local cron_script="$MODULE_DIR/scripts/setup_cron.sh" if [[ "$DRY_RUN" == true ]]; then log_info "Would setup cron jobs using: $cron_script" return fi if [[ -f "$cron_script" ]]; then chmod +x "$cron_script" # Run cron setup script if "$cron_script" --user "$WEB_USER"; then log_success "Cron jobs installed successfully" else log_error "Failed to install cron jobs" exit 1 fi else log_warning "Cron setup script not found: $cron_script" fi } # Post-installation verification verify_installation() { log_step "Post-Installation Verification" local errors=0 # Verify database tables if [[ "$SKIP_DATABASE" == false ]]; then local mysql_cmd="mysql -h$DB_HOST -u$DB_USER -p$DB_PASSWORD $DB_NAME" local required_tables=("desk_moloni_config" "desk_moloni_mapping" "desk_moloni_sync_queue" "desk_moloni_sync_log") for table in "${required_tables[@]}"; do if echo "SHOW TABLES LIKE '$table';" | $mysql_cmd | grep -q "$table"; then log_success "✓ Table exists: $table" else log_error "✗ Table missing: $table" ((errors++)) fi done fi # Verify file permissions local required_dirs=("$MODULE_DIR/logs" "$MODULE_DIR/locks") for dir in "${required_dirs[@]}"; do if [[ -d "$dir" ]] && [[ -w "$dir" ]]; then log_success "✓ Directory writable: $dir" else log_error "✗ Directory not writable: $dir" ((errors++)) fi done # Verify CLI commands local cli_files=("$MODULE_DIR/cli/queue_processor.php" "$MODULE_DIR/cli/sync_commands.php") for file in "${cli_files[@]}"; do if [[ -f "$file" ]] && [[ -x "$file" ]]; then log_success "✓ CLI command executable: $(basename "$file")" else log_error "✗ CLI command not executable: $(basename "$file")" ((errors++)) fi done # Test basic functionality if [[ "$DRY_RUN" == false ]]; then local health_cmd="php $MODULE_DIR/cli/sync_commands.php health" if $health_cmd &>/dev/null; then log_success "✓ Health check command works" else log_warning "⚠ Health check command failed (may be expected on first run)" fi fi if [[ $errors -eq 0 ]]; then log_success "All verification checks passed" else log_error "Verification failed with $errors errors" exit 1 fi } # Uninstallation uninstall_module() { log_step "Uninstalling Desk-Moloni Module" local confirm="no" if [[ "$FORCE_INSTALL" == false ]]; then echo -e "${RED}WARNING: This will permanently delete all Desk-Moloni data!${NC}" echo -n "Type 'YES' to confirm uninstallation: " read -r confirm else confirm="YES" fi if [[ "$confirm" != "YES" ]]; then log_info "Uninstallation cancelled" exit 0 fi # Create backup before uninstall BACKUP_BEFORE_INSTALL=true create_backup if [[ "$DRY_RUN" == true ]]; then log_info "Would uninstall module and remove all data" return fi # Remove cron jobs local cron_script="$MODULE_DIR/scripts/setup_cron.sh" if [[ -f "$cron_script" ]]; then "$cron_script" --uninstall --user "$WEB_USER" || true log_success "Cron jobs removed" fi # Drop database tables if [[ "$SKIP_DATABASE" == false ]]; then local mysql_cmd="mysql -h$DB_HOST -u$DB_USER -p$DB_PASSWORD $DB_NAME" local tables=("desk_moloni_sync_log" "desk_moloni_sync_queue" "desk_moloni_mapping" "desk_moloni_config") for table in "${tables[@]}"; do echo "DROP TABLE IF EXISTS $table;" | $mysql_cmd log_success "Dropped table: $table" done fi # Remove module files (but preserve backups) local preserve_dirs=("backups") for dir in "$MODULE_DIR"/*; do if [[ -d "$dir" ]]; then local dirname=$(basename "$dir") if [[ ! " ${preserve_dirs[@]} " =~ " ${dirname} " ]]; then rm -rf "$dir" log_success "Removed directory: $dir" fi elif [[ -f "$dir" ]]; then rm -f "$dir" log_success "Removed file: $dir" fi done log_success "Module uninstalled successfully" log_info "Backup preserved at: $BACKUP_DIR" } # Restore from backup restore_from_backup() { log_step "Restoring from Backup" if [[ ! -d "$RESTORE_BACKUP" ]]; then log_error "Backup directory not found: $RESTORE_BACKUP" exit 1 fi local manifest_file="$RESTORE_BACKUP/manifest.txt" if [[ ! -f "$manifest_file" ]]; then log_error "Backup manifest not found: $manifest_file" exit 1 fi log_info "Backup details:" cat "$manifest_file" if [[ "$DRY_RUN" == true ]]; then log_info "Would restore from backup: $RESTORE_BACKUP" return fi echo -n "Proceed with restore? [y/N]: " read -r confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then log_info "Restore cancelled" exit 0 fi # Restore database tables local mysql_cmd="mysql -h$DB_HOST -u$DB_USER -p$DB_PASSWORD $DB_NAME" for sql_file in "$RESTORE_BACKUP"/*.sql; do if [[ -f "$sql_file" ]]; then local table_name=$(basename "$sql_file" .sql) log_info "Restoring table: $table_name" $mysql_cmd < "$sql_file" log_success "Table restored: $table_name" fi done # Restore configuration files if [[ -d "$RESTORE_BACKUP/config" ]]; then cp -r "$RESTORE_BACKUP/config/"* "$MODULE_DIR/config/" 2>/dev/null || true log_success "Configuration files restored" fi log_success "Restore completed successfully" } # Main installation function main_install() { log_step "Starting Desk-Moloni v3.0 Installation" # Installation steps check_requirements interactive_config test_database_connection create_backup install_database setup_permissions initialize_config setup_cron_jobs verify_installation log_step "Installation Completed Successfully" echo -e "\n${GREEN}🎉 Desk-Moloni v3.0 has been installed successfully!${NC}\n" echo "Next steps:" echo "1. Configure OAuth credentials in the Perfex CRM admin panel" echo "2. Test the API connection: php $MODULE_DIR/cli/sync_commands.php test-connection" echo "3. Monitor the queue processor: tail -f $MODULE_DIR/logs/queue_processor.log" echo "4. Check system health: php $MODULE_DIR/cli/sync_commands.php health" echo "" echo "Documentation: See $MODULE_DIR/README.md" echo "Log files: $MODULE_DIR/logs/" echo "Configuration: $MODULE_DIR/config/" echo "" if [[ "$ENVIRONMENT" == "development" ]]; then echo -e "${YELLOW}Development environment detected${NC}" echo "- Debug mode enabled" echo "- Extended API timeouts" echo "- Detailed logging" echo "" fi } # Main execution main() { # Handle special operations if [[ "$UNINSTALL" == true ]]; then uninstall_module exit 0 fi if [[ -n "$RESTORE_BACKUP" ]]; then restore_from_backup exit 0 fi # Normal installation main_install } # Error handling trap 'log_error "Installation failed on line $LINENO. Check $INSTALL_LOG for details."' ERR # Run main function main "$@"