- Bump DESK_MOLONI version to 3.0.1 across module - Normalize hooks to after_client_* and instantiate PerfexHooks safely - Fix OAuthController view path and API client class name - Add missing admin views for webhook config/logs; adjust view loading - Harden client portal routes and admin routes mapping - Make Dashboard/Logs/Queue tolerant to optional model methods - Align log details query with existing schema; avoid broken joins This makes the module operational in Perfex (admin + client), reduces 404s, and avoids fatal errors due to inconsistent tables/methods.
426 lines
12 KiB
Bash
426 lines
12 KiB
Bash
#!/bin/bash
|
|
# Desk-Moloni v3.0 Cron Job Setup Script
|
|
#
|
|
# Sets up automated cron jobs for queue processing and maintenance tasks.
|
|
# Handles different environments and user permissions.
|
|
|
|
set -euo pipefail
|
|
|
|
# Script configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
MODULE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
CLI_DIR="$MODULE_DIR/cli"
|
|
LOG_DIR="$MODULE_DIR/logs"
|
|
LOCK_DIR="$MODULE_DIR/locks"
|
|
|
|
# Default configuration
|
|
DEFAULT_QUEUE_INTERVAL="*/1" # Every minute
|
|
DEFAULT_MAINTENANCE_HOUR="2" # 2 AM
|
|
DEFAULT_LOG_RETENTION_DAYS="30"
|
|
DEFAULT_USER=""
|
|
|
|
# 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"
|
|
}
|
|
|
|
# Help function
|
|
show_help() {
|
|
cat << EOF
|
|
Desk-Moloni v3.0 Cron Setup Script
|
|
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Options:
|
|
-h, --help Show this help message
|
|
-u, --user USER User to run cron jobs as (default: current user)
|
|
-i, --interval INTERVAL Queue processor interval (default: */1 for every minute)
|
|
-m, --maintenance HOUR Maintenance hour (default: 2 for 2 AM)
|
|
-r, --retention DAYS Log retention days (default: 30)
|
|
-d, --dry-run Show what would be done without making changes
|
|
-v, --verbose Verbose output
|
|
--uninstall Remove all cron jobs
|
|
--status Show current cron job status
|
|
|
|
Examples:
|
|
$0 # Setup with defaults
|
|
$0 -u www-data -i "*/5" # Run as www-data every 5 minutes
|
|
$0 --dry-run # Preview changes
|
|
$0 --uninstall # Remove all cron jobs
|
|
$0 --status # Show current status
|
|
|
|
Cron Jobs Created:
|
|
1. Queue Processor: Processes synchronization queue
|
|
2. Daily Maintenance: Cleanup logs, update mappings
|
|
3. Health Check: Monitor system health
|
|
4. Token Refresh: Refresh OAuth tokens
|
|
|
|
EOF
|
|
}
|
|
|
|
# Parse command line arguments
|
|
QUEUE_INTERVAL="$DEFAULT_QUEUE_INTERVAL"
|
|
MAINTENANCE_HOUR="$DEFAULT_MAINTENANCE_HOUR"
|
|
LOG_RETENTION_DAYS="$DEFAULT_LOG_RETENTION_DAYS"
|
|
CRON_USER="$DEFAULT_USER"
|
|
DRY_RUN=false
|
|
VERBOSE=false
|
|
UNINSTALL=false
|
|
SHOW_STATUS=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
-u|--user)
|
|
CRON_USER="$2"
|
|
shift 2
|
|
;;
|
|
-i|--interval)
|
|
QUEUE_INTERVAL="$2"
|
|
shift 2
|
|
;;
|
|
-m|--maintenance)
|
|
MAINTENANCE_HOUR="$2"
|
|
shift 2
|
|
;;
|
|
-r|--retention)
|
|
LOG_RETENTION_DAYS="$2"
|
|
shift 2
|
|
;;
|
|
-d|--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
-v|--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
--uninstall)
|
|
UNINSTALL=true
|
|
shift
|
|
;;
|
|
--status)
|
|
SHOW_STATUS=true
|
|
shift
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Set default user to current user if not specified
|
|
if [[ -z "$CRON_USER" ]]; then
|
|
CRON_USER=$(whoami)
|
|
fi
|
|
|
|
# Validate user exists
|
|
if ! id "$CRON_USER" &>/dev/null; then
|
|
log_error "User '$CRON_USER' does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if running as root or target user
|
|
CURRENT_USER=$(whoami)
|
|
if [[ "$CURRENT_USER" != "root" && "$CURRENT_USER" != "$CRON_USER" ]]; then
|
|
log_error "Must run as root or target user ($CRON_USER)"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate maintenance hour
|
|
if [[ ! "$MAINTENANCE_HOUR" =~ ^[0-9]+$ ]] || [[ "$MAINTENANCE_HOUR" -lt 0 ]] || [[ "$MAINTENANCE_HOUR" -gt 23 ]]; then
|
|
log_error "Invalid maintenance hour: $MAINTENANCE_HOUR (must be 0-23)"
|
|
exit 1
|
|
fi
|
|
|
|
# Create required directories
|
|
create_directories() {
|
|
local dirs=("$LOG_DIR" "$LOCK_DIR")
|
|
|
|
for dir in "${dirs[@]}"; do
|
|
if [[ ! -d "$dir" ]]; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
log_info "Would create directory: $dir"
|
|
else
|
|
mkdir -p "$dir"
|
|
chown "$CRON_USER:$CRON_USER" "$dir" 2>/dev/null || true
|
|
log_success "Created directory: $dir"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Generate cron job entries
|
|
generate_cron_jobs() {
|
|
cat << EOF
|
|
# Desk-Moloni v3.0 Cron Jobs
|
|
# Generated on $(date)
|
|
# User: $CRON_USER
|
|
|
|
# Queue Processor - Process synchronization queue
|
|
$QUEUE_INTERVAL * * * * /usr/bin/flock -n $LOCK_DIR/queue_processor.lock php $CLI_DIR/queue_processor.php >> $LOG_DIR/queue_processor.log 2>&1
|
|
|
|
# Daily Maintenance - Cleanup and optimization
|
|
0 $MAINTENANCE_HOUR * * * /usr/bin/flock -n $LOCK_DIR/maintenance.lock $SCRIPT_DIR/maintenance.sh >> $LOG_DIR/maintenance.log 2>&1
|
|
|
|
# Health Check - System monitoring
|
|
*/15 * * * * /usr/bin/flock -n $LOCK_DIR/health_check.lock php $CLI_DIR/sync_commands.php health >> $LOG_DIR/health_check.log 2>&1
|
|
|
|
# Token Refresh - OAuth token maintenance
|
|
0 */6 * * * /usr/bin/flock -n $LOCK_DIR/token_refresh.lock $SCRIPT_DIR/token_refresh.sh >> $LOG_DIR/token_refresh.log 2>&1
|
|
|
|
# Log Rotation - Cleanup old logs
|
|
0 1 * * 0 /usr/bin/find $LOG_DIR -name "*.log" -mtime +$LOG_RETENTION_DAYS -delete
|
|
|
|
EOF
|
|
}
|
|
|
|
# Get current cron jobs for the user
|
|
get_current_crontab() {
|
|
if [[ "$CURRENT_USER" == "root" ]]; then
|
|
crontab -u "$CRON_USER" -l 2>/dev/null || true
|
|
else
|
|
crontab -l 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# Remove existing Desk-Moloni cron jobs
|
|
remove_existing_jobs() {
|
|
local current_crontab
|
|
current_crontab=$(get_current_crontab)
|
|
|
|
if [[ -n "$current_crontab" ]]; then
|
|
# Remove lines between Desk-Moloni markers
|
|
local new_crontab
|
|
new_crontab=$(echo "$current_crontab" | sed '/# Desk-Moloni v3.0 Cron Jobs/,/^$/d')
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
log_info "Would remove existing Desk-Moloni cron jobs"
|
|
else
|
|
if [[ "$CURRENT_USER" == "root" ]]; then
|
|
echo "$new_crontab" | crontab -u "$CRON_USER" -
|
|
else
|
|
echo "$new_crontab" | crontab -
|
|
fi
|
|
log_success "Removed existing Desk-Moloni cron jobs"
|
|
fi
|
|
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Install cron jobs
|
|
install_cron_jobs() {
|
|
local current_crontab new_crontab cron_jobs
|
|
current_crontab=$(get_current_crontab)
|
|
cron_jobs=$(generate_cron_jobs)
|
|
|
|
# Combine existing crontab with new jobs
|
|
if [[ -n "$current_crontab" ]]; then
|
|
new_crontab="$current_crontab"$'\n'"$cron_jobs"
|
|
else
|
|
new_crontab="$cron_jobs"
|
|
fi
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
log_info "Would install the following cron jobs:"
|
|
echo "$cron_jobs"
|
|
else
|
|
if [[ "$CURRENT_USER" == "root" ]]; then
|
|
echo "$new_crontab" | crontab -u "$CRON_USER" -
|
|
else
|
|
echo "$new_crontab" | crontab -
|
|
fi
|
|
log_success "Installed Desk-Moloni cron jobs for user: $CRON_USER"
|
|
fi
|
|
}
|
|
|
|
# Show current status
|
|
show_status() {
|
|
log_info "Desk-Moloni Cron Job Status"
|
|
echo "================================"
|
|
|
|
local current_crontab
|
|
current_crontab=$(get_current_crontab)
|
|
|
|
if [[ -n "$current_crontab" ]]; then
|
|
local desk_moloni_jobs
|
|
desk_moloni_jobs=$(echo "$current_crontab" | sed -n '/# Desk-Moloni v3.0 Cron Jobs/,/^$/p')
|
|
|
|
if [[ -n "$desk_moloni_jobs" ]]; then
|
|
log_success "Desk-Moloni cron jobs are installed for user: $CRON_USER"
|
|
echo "$desk_moloni_jobs"
|
|
else
|
|
log_warning "No Desk-Moloni cron jobs found for user: $CRON_USER"
|
|
fi
|
|
else
|
|
log_warning "No crontab found for user: $CRON_USER"
|
|
fi
|
|
|
|
# Check if cron daemon is running
|
|
if systemctl is-active --quiet cron 2>/dev/null || systemctl is-active --quiet crond 2>/dev/null; then
|
|
log_success "Cron daemon is running"
|
|
else
|
|
log_warning "Cron daemon may not be running"
|
|
fi
|
|
|
|
# Check log files
|
|
echo -e "\nLog Files Status:"
|
|
for log_file in "queue_processor.log" "maintenance.log" "health_check.log" "token_refresh.log"; do
|
|
local log_path="$LOG_DIR/$log_file"
|
|
if [[ -f "$log_path" ]]; then
|
|
local size=$(du -h "$log_path" | cut -f1)
|
|
local modified=$(stat -c %y "$log_path" 2>/dev/null | cut -d' ' -f1,2 | cut -d'.' -f1)
|
|
log_info "$log_file: $size (modified: $modified)"
|
|
else
|
|
log_warning "$log_file: Not found"
|
|
fi
|
|
done
|
|
|
|
# Check lock files
|
|
echo -e "\nActive Processes:"
|
|
for lock_file in "$LOCK_DIR"/*.lock; do
|
|
if [[ -f "$lock_file" ]]; then
|
|
local lock_name=$(basename "$lock_file" .lock)
|
|
log_warning "$lock_name: Process may be running (lock file exists)"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Validate PHP and dependencies
|
|
validate_dependencies() {
|
|
log_info "Validating dependencies..."
|
|
|
|
# Check PHP
|
|
if ! command -v php &> /dev/null; then
|
|
log_error "PHP is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
|
|
local php_version
|
|
php_version=$(php -r "echo PHP_VERSION;" 2>/dev/null)
|
|
log_success "PHP version: $php_version"
|
|
|
|
# Check flock
|
|
if ! command -v flock &> /dev/null; then
|
|
log_error "flock is not installed (required for preventing concurrent execution)"
|
|
exit 1
|
|
fi
|
|
|
|
# Check CLI files exist
|
|
local cli_files=("$CLI_DIR/queue_processor.php" "$CLI_DIR/sync_commands.php")
|
|
for cli_file in "${cli_files[@]}"; do
|
|
if [[ ! -f "$cli_file" ]]; then
|
|
log_error "CLI file not found: $cli_file"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
log_success "All dependencies validated"
|
|
}
|
|
|
|
# Test cron job syntax
|
|
test_cron_syntax() {
|
|
log_info "Testing cron job syntax..."
|
|
|
|
local cron_jobs
|
|
cron_jobs=$(generate_cron_jobs)
|
|
|
|
# Basic validation of cron expressions
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^[^#].* ]]; then
|
|
local cron_expr
|
|
cron_expr=$(echo "$line" | cut -d' ' -f1-5)
|
|
|
|
# Very basic validation - just check field count
|
|
local field_count
|
|
field_count=$(echo "$cron_expr" | wc -w)
|
|
|
|
if [[ "$field_count" -ne 5 ]]; then
|
|
log_error "Invalid cron expression: $cron_expr"
|
|
exit 1
|
|
fi
|
|
fi
|
|
done <<< "$cron_jobs"
|
|
|
|
log_success "Cron job syntax validated"
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
log_info "Desk-Moloni v3.0 Cron Setup"
|
|
log_info "User: $CRON_USER"
|
|
log_info "Queue Interval: $QUEUE_INTERVAL"
|
|
log_info "Maintenance Hour: $MAINTENANCE_HOUR"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
log_warning "DRY RUN MODE - No changes will be made"
|
|
fi
|
|
|
|
if [[ "$SHOW_STATUS" == true ]]; then
|
|
show_status
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$UNINSTALL" == true ]]; then
|
|
log_info "Uninstalling Desk-Moloni cron jobs..."
|
|
if remove_existing_jobs; then
|
|
log_success "Cron jobs removed successfully"
|
|
else
|
|
log_warning "No existing cron jobs found"
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
# Installation process
|
|
validate_dependencies
|
|
test_cron_syntax
|
|
create_directories
|
|
|
|
# Remove existing jobs first
|
|
remove_existing_jobs || true
|
|
|
|
# Install new jobs
|
|
install_cron_jobs
|
|
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
log_info ""
|
|
log_success "Cron jobs have been installed successfully!"
|
|
log_info "Monitor logs in: $LOG_DIR"
|
|
log_info "Check status with: $0 --status"
|
|
log_info ""
|
|
log_info "Next steps:"
|
|
log_info "1. Verify cron daemon is running: systemctl status cron"
|
|
log_info "2. Monitor queue processor: tail -f $LOG_DIR/queue_processor.log"
|
|
log_info "3. Check health status: php $CLI_DIR/sync_commands.php health"
|
|
fi
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |