Created comprehensive architecture and quick reference documentation. NEW DOCUMENTS: 1. MULTI_CONTROL_PANEL_ARCHITECTURE.md (6500+ words) Defines MANDATORY patterns for all future development: - Core principles (never hardcode, use abstractions, conditionals) - Standard library usage (system-detect.sh, user-manager.sh) - Path mapping reference (all panels) - Standard code patterns (log discovery, docroot, domain→user) - Module classification (A/B/C/D) - Testing requirements - Code review checklist - Migration guide - Common mistakes to avoid Every developer must follow these patterns! 2. CONTROL_PANEL_QUICK_REFERENCE.md (8000+ words) Fast lookup while coding: - Panel detection methods - Complete file system path mappings - Configuration file locations - CLI tools & API commands - Database prefix patterns (CRITICAL for InterWorx!) - PHP configuration per panel - Email, FTP, security features - WordPress detection patterns - Process ownership - Code snippets for common tasks - Panel-specific quirks/gotchas - Migration implications Covers: cPanel, Plesk, InterWorx, Standalone PURPOSE: These documents establish a STANDARD ARCHITECTURE before completing InterWorx support. All modules will be refactored to follow these patterns, making it trivial to add DirectAdmin, CyberPanel, etc. KEY PATTERNS ESTABLISHED: - Never hardcode paths → use SYS_LOG_DIR, get_user_info() - Wrap API calls → check SYS_CONTROL_PANEL first - Design for extension → case statements for panels - Test on all platforms → cPanel regression required MODULE CLASSIFICATION: - Class A: Panel agnostic (no special handling) - Class B: Needs system detection (SYS_LOG_DIR) - Class C: Needs user/domain management (get_user_info) - Class D: Panel-specific (document limitations) CRITICAL GOTCHAS DOCUMENTED: - InterWorx database prefix uses DOMAIN not USERNAME! - Plesk has no shared hosting (domain-centric) - cPanel addon domains share public_html - InterWorx logs are per-domain in user home NEXT STEPS: 1. Update existing modules to follow patterns 2. Complete InterWorx support systematically 3. Expand Plesk support 4. Add DirectAdmin/CyberPanel This is the foundation for true multi-panel architecture!
15 KiB
Multi-Control-Panel Architecture
Version: 1.0 Date: 2025-11-19 Status: ACTIVE STANDARD
Executive Summary
This document defines the standard architecture for building control panel-agnostic tools in the Server Management Toolkit. All new code and refactored modules MUST follow these patterns to ensure compatibility with cPanel, Plesk, InterWorx, and future control panels.
Supported Control Panels
Current Support Levels:
- cPanel - ✅ Full support (primary platform)
- Plesk - ⚠️ Partial support (needs expansion)
- InterWorx - 🚧 In progress (Phases 1-3 complete)
- Standalone - ✅ Basic support (no control panel)
Future Targets:
- DirectAdmin
- CyberPanel
- Webmin/Virtualmin
Core Principles
1. Never Hardcode Paths
# ❌ BAD - Hardcoded cPanel path
LOG_DIR="/var/log/apache2/domlogs"
docroot="/home/$user/public_html"
# ✅ GOOD - Use system detection
LOG_DIR="${SYS_LOG_DIR}" # From system-detect.sh
eval $(get_user_info "$username") # Returns $HOME_DIR, $PRIMARY_DOMAIN
docroot="${HOME_DIR}/${PRIMARY_DOMAIN}/html" # Constructed dynamically
2. Use Abstraction Libraries
# ❌ BAD - Direct control panel-specific code
user=$(grep -l "DNS.*$domain" /var/cpanel/users/* 2>/dev/null | ...)
# ✅ GOOD - Use user-manager.sh abstraction
eval $(get_user_info "$username")
# Now you have: $USER_EXISTS, $USERNAME, $PRIMARY_DOMAIN, $ALL_DOMAINS, etc.
3. Conditional Panel-Specific Features
# ❌ BAD - Assumes cPanel exists
whmapi1 cphulkd_list_blocks
# ✅ GOOD - Conditional with graceful fallback
if [ "$SYS_CONTROL_PANEL" = "cpanel" ]; then
whmapi1 cphulkd_list_blocks
else
echo "CPHulk is cPanel-specific, skipping..."
fi
4. Design for Extension
# ✅ GOOD - Easy to add new panels
case "$SYS_CONTROL_PANEL" in
cpanel)
# cPanel-specific logic
;;
plesk)
# Plesk-specific logic
;;
interworx)
# InterWorx-specific logic
;;
directadmin)
# Future: DirectAdmin logic
;;
*)
echo "Unsupported control panel: $SYS_CONTROL_PANEL"
return 1
;;
esac
Standard Library Usage
system-detect.sh
Purpose: Runtime detection of control panel, OS, paths, and system resources Exports:
SYS_CONTROL_PANEL- Control panel type (cpanel, plesk, interworx, none)SYS_LOG_DIR- Apache/web server log directorySYS_USER_HOME_BASE- Base directory for user homesSYS_WEB_SERVER- Web server type (apache, nginx, litespeed)SYS_FIREWALL- Firewall type (csf, firewalld, iptables, ufw)SYS_CSF_ACTIVE- CSF availability flag
Usage:
# Automatically sourced when loading common-functions.sh
source "$SCRIPT_DIR/lib/common-functions.sh"
source "$SCRIPT_DIR/lib/system-detect.sh"
# Variables are now available
if [ "$SYS_CONTROL_PANEL" = "cpanel" ]; then
# cPanel-specific code
fi
user-manager.sh
Purpose: Control panel-agnostic user/domain/database management Functions:
get_user_info()
eval $(get_user_info "username")
# Returns (via eval):
# - USER_EXISTS=yes/no
# - USERNAME=username
# - PRIMARY_DOMAIN=example.com
# - ALL_DOMAINS=space-separated list
# - EMAIL=user@example.com
# - HOME_DIR=/home/username
# - DISK_USED=1024M
get_user_domains()
domains=$(get_user_domains "username")
# Returns newline-separated list of domains
get_user_databases()
databases=$(get_user_databases "username")
# Returns newline-separated list of databases
# Note: InterWorx uses domain prefix, not username!
Critical Note: Database prefixes differ!
- cPanel:
username_dbname - InterWorx:
first8charsOfDomain_dbname - Plesk:
dbname(user-scoped)
Path Mapping Reference
| Resource | cPanel | Plesk | InterWorx | Standalone |
|---|---|---|---|---|
| User Home | /home/user |
/var/www/vhosts/domain |
/home/user |
/home/user |
| Document Root | /home/user/public_html |
/var/www/vhosts/domain/httpdocs |
/home/user/domain.com/html |
/var/www/html |
| Access Logs | /var/log/apache2/domlogs/domain |
/var/www/vhosts/system/domain/logs |
/home/user/var/domain/logs/access_log |
/var/log/httpd/access_log |
| Error Logs | /var/log/apache2/domlogs/domain-error_log |
/var/www/vhosts/system/domain/logs/error_log |
/home/user/var/domain/logs/error_log |
/var/log/httpd/error_log |
| PHP Error Logs | /home/user/public_html/error_log |
/var/www/vhosts/domain/httpdocs/error_log |
/home/user/domain/html/error_log |
N/A |
| User Config | /var/cpanel/users/user |
Plesk DB | listaccounts.pex or vhost configs | N/A |
| Domain Config | /var/cpanel/userdata/user/domain |
Plesk DB | /etc/httpd/conf.d/vhost_domain.conf |
N/A |
| Mail Dir | /home/user/mail |
/var/qmail/mailnames/domain |
/home/user/var/domain/mail |
/var/mail |
| Cron Jobs | /var/spool/cron/user |
/var/spool/cron/user |
/var/spool/cron/user |
/var/spool/cron/user |
| SSL Certs | /etc/letsencrypt/live/domain |
/etc/letsencrypt/live/domain |
/etc/letsencrypt/live/domain |
/etc/letsencrypt/live/domain |
| Database Prefix | username_ |
No prefix | first8chars_ |
N/A |
Standard Patterns
Pattern 1: Log File Discovery
discover_logs() {
local log_files=()
if [ "$SYS_CONTROL_PANEL" = "interworx" ]; then
# InterWorx: Per-domain logs in user home
while IFS= read -r logfile; do
[ -f "$logfile" ] && log_files+=("$logfile")
done < <(find /home/*/var/*/logs -type f -name "access_log" 2>/dev/null)
elif [ "$SYS_CONTROL_PANEL" = "plesk" ]; then
# Plesk: Centralized per-domain logs
while IFS= read -r logfile; do
[ -f "$logfile" ] && log_files+=("$logfile")
done < <(find /var/www/vhosts/system/*/logs -type f -name "access_log" 2>/dev/null)
elif [ "$SYS_CONTROL_PANEL" = "cpanel" ] && [ -n "$SYS_LOG_DIR" ]; then
# cPanel: Centralized domlogs
while IFS= read -r logfile; do
[ -f "$logfile" ] && log_files+=("$logfile")
done < <(find "$SYS_LOG_DIR" -type f ! -name "*-bytes_log" ! -name "*error_log" 2>/dev/null)
else
# Standalone: Main access log only
[ -f "/var/log/httpd/access_log" ] && log_files+=("/var/log/httpd/access_log")
[ -f "/var/log/apache2/access.log" ] && log_files+=("/var/log/apache2/access.log")
fi
echo "${log_files[@]}"
}
Pattern 2: Document Root Discovery
get_docroot() {
local domain="$1"
local username="$2" # Optional, helps with InterWorx
case "$SYS_CONTROL_PANEL" in
cpanel)
# cPanel: public_html
echo "/home/${username}/public_html"
;;
plesk)
# Plesk: httpdocs
echo "/var/www/vhosts/${domain}/httpdocs"
;;
interworx)
# InterWorx: domain.com/html
echo "/home/${username}/${domain}/html"
;;
*)
# Standalone: Assume standard
echo "/var/www/html"
;;
esac
}
Pattern 3: Domain→User Mapping
get_user_for_domain() {
local domain="$1"
case "$SYS_CONTROL_PANEL" in
cpanel)
# cPanel: /etc/userdatadomains
grep "^${domain}:" /etc/userdatadomains 2>/dev/null | cut -d: -f2 | awk -F'==' '{print $1}'
;;
plesk)
# Plesk: Use CLI
plesk bin subscription --info "$domain" 2>/dev/null | grep "Owner" | awk '{print $2}'
;;
interworx)
# InterWorx: Parse vhost configs
grep -l "ServerName ${domain}" /etc/httpd/conf.d/vhost_*.conf 2>/dev/null | head -1 | \
xargs grep "SuexecUserGroup" 2>/dev/null | awk '{print $2}'
;;
*)
echo "Unsupported"
return 1
;;
esac
}
Pattern 4: Control Panel API Calls
# Always wrap API calls in control panel checks
ban_ip_via_panel() {
local ip="$1"
case "$SYS_CONTROL_PANEL" in
cpanel)
if command -v whmapi1 &>/dev/null; then
whmapi1 cphulkd_blacklist ip="$ip"
else
echo "cPanel WHM API not available"
return 1
fi
;;
plesk)
if command -v plesk &>/dev/null; then
plesk bin ip --ban "$ip"
else
echo "Plesk CLI not available"
return 1
fi
;;
interworx)
# InterWorx has no built-in IP ban API
# Fall back to firewall
echo "InterWorx has no native IP ban, using firewall"
return 2
;;
*)
echo "No control panel API available"
return 1
;;
esac
}
Testing Requirements
Every Module Must Be Tested On:
- ✅ cPanel - Regression testing (ensure no breakage)
- ✅ Standalone - No control panel environment
- ⚠️ Plesk - If Plesk support claimed
- ⚠️ InterWorx - If InterWorx support claimed
Test Checklist:
- Module loads without errors on all platforms
- Paths are correctly detected
- User/domain functions work correctly
- Graceful degradation when panel-specific features unavailable
- Error messages are helpful and mention control panel
- No hardcoded paths remain
- API calls are wrapped in panel checks
Code Review Checklist
Before committing any module changes, verify:
- No hardcoded paths (
/var/cpanel,/usr/local/cpanel,/var/log/apache2/domlogs,public_html) - Uses
SYS_LOG_DIRfrom system-detect.sh - Uses
get_user_info()/get_user_domains()from user-manager.sh - API calls wrapped in
if [ "$SYS_CONTROL_PANEL" = "cpanel" ] - Document root constructed dynamically
- Error messages include control panel info for debugging
- Tested with
bash -n script.sh(syntax check) - Added to INTERWORX_COMPATIBILITY_AUDIT.md if applicable
Module Classification
Class A: Control Panel Agnostic
Can work on any system
- hardware-health-check.sh
- tail-secure-log.sh
- tail-mail-log.sh
- firewall-activity-monitor.sh
- ssh-attack-monitor.sh
Requirements: None special
Class B: Requires System Detection
Needs paths but no user/domain management
- network-bandwidth-analyzer.sh
- tail-apache-access.sh
- tail-apache-error.sh
- web-traffic-monitor.sh
Requirements:
- Source system-detect.sh
- Use
SYS_LOG_DIR
Class C: Requires User/Domain Management
Needs to map users/domains
- bot-analyzer.sh
- website-error-analyzer.sh
- 500-error-tracker.sh
- malware-scanner.sh
- wordpress-cron-manager.sh
Requirements:
- Source system-detect.sh
- Source user-manager.sh
- Use
get_user_info()/get_user_domains()
Class D: Control Panel-Specific
Only works on specific panels
- enable-cphulk.sh (cPanel only)
- acronis-* (cPanel specific)
Requirements:
- Check
SYS_CONTROL_PANELat startup - Exit gracefully if wrong panel
- Document panel requirement in help text
Migration Guide
Converting Existing Module to Multi-Panel:
- Add Library Imports
# At top of script, after shebang
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$SCRIPT_DIR/lib/common-functions.sh"
source "$SCRIPT_DIR/lib/system-detect.sh"
source "$SCRIPT_DIR/lib/user-manager.sh" # If Class C
- Replace Hardcoded Paths
# Find all hardcoded paths
grep -n "/var/cpanel\|/usr/local/cpanel\|public_html\|domlogs" yourscript.sh
# Replace with system variables or functions
- Wrap API Calls
# Find all API calls
grep -n "whmapi\|uapi\|cpapi" yourscript.sh
# Wrap in conditional checks
- Add Control Panel Cases
# For panel-specific logic, use case statements
case "$SYS_CONTROL_PANEL" in
cpanel) ... ;;
plesk) ... ;;
interworx) ... ;;
*) ... ;;
esac
- Test Syntax
bash -n yourscript.sh
- Update Documentation
- Add to INTERWORX_COMPATIBILITY_AUDIT.md
- Note supported panels in script header
- Update help text
Common Mistakes to Avoid
❌ Mistake #1: Using Command Existence as Panel Detection
# BAD - Command might exist on other panels
if command -v whmapi1 &>/dev/null; then
# Assume cPanel
fi
Fix: Always check SYS_CONTROL_PANEL
# GOOD
if [ "$SYS_CONTROL_PANEL" = "cpanel" ] && command -v whmapi1 &>/dev/null; then
# Definitely cPanel and API available
fi
❌ Mistake #2: Assuming File Locations
# BAD - Only works on cPanel
domains=$(grep "^DNS" /var/cpanel/users/$user | awk '{print $2}')
Fix: Use abstraction
# GOOD
domains=$(get_user_domains "$user")
❌ Mistake #3: Hardcoding Document Root Structure
# BAD - Assumes cPanel's public_html
find /home/*/public_html -name "wp-config.php"
Fix: Use dynamic paths
# GOOD
case "$SYS_CONTROL_PANEL" in
cpanel) find /home/*/public_html -name "wp-config.php" ;;
interworx) find /home/*/*/html -name "wp-config.php" ;;
plesk) find /var/www/vhosts/*/httpdocs -name "wp-config.php" ;;
esac
❌ Mistake #4: Silent Failures
# BAD - Fails silently on non-cPanel
whmapi1 some_command
Fix: Check and report
# GOOD
if [ "$SYS_CONTROL_PANEL" = "cpanel" ]; then
whmapi1 some_command
else
print_warning "This feature requires cPanel (current: $SYS_CONTROL_PANEL)"
return 1
fi
Future Enhancements
Planned Improvements:
- Panel-specific feature matrix - Document what features work on which panels
- Automated testing framework - Docker containers for each panel
- Panel capability detection - Runtime feature detection
- Plugin architecture - Easy addition of new panel support
Control Panel Support Roadmap:
- Phase 1-3: InterWorx (IN PROGRESS)
- Phase 4: Plesk expansion (fill gaps)
- Phase 5: DirectAdmin
- Phase 6: CyberPanel
Questions and Support
Q: What if a feature is impossible on a specific panel? A: Document it, detect it, and fail gracefully with a clear message.
Q: Should I support all panels in every module? A: No. Class D modules can be panel-specific. Just document it.
Q: What about backward compatibility? A: Always maintain cPanel compatibility. It's the primary platform.
Q: How do I test without access to all panels? A: At minimum: syntax check + cPanel regression. InterWorx/Plesk testing is nice-to-have.
Version History
- 1.0 (2025-11-19) - Initial standard established
- Defined core principles
- Created pattern library
- Established testing requirements
- Documented common mistakes