almost add installer with bash script
This commit is contained in:
parent
79b2aae2da
commit
2284544f41
14
README.md
14
README.md
|
@ -13,6 +13,20 @@
|
|||
* افزونههای PHP: php-Intl، php-mbstring، php-http، php-raphf، php-zip، php-gd
|
||||
* composer
|
||||
|
||||
## نصب سریع در سرورهای اوبونتو 24.04 به بالا
|
||||
|
||||
برای نصب سریع حسابیکس در سرورهای اوبونتو 24.04 به بالا، میتوانید از اسکریپت نصب خودکار استفاده کنید. این اسکریپت تمام پیشنیازها را نصب کرده و حسابیکس را به صورت خودکار راهاندازی میکند:
|
||||
|
||||
```bash
|
||||
cd /tmp && curl -sSL https://shell.hesabix.ir/installer.sh | tr -d '\r' > installer.sh && chmod +x installer.sh && sudo bash installer.sh
|
||||
```
|
||||
|
||||
پس از اجرای این دستور، اسکریپت نصب به صورت خودکار:
|
||||
- تمام پیشنیازهای مورد نیاز را نصب میکند
|
||||
- پایگاه داده را پیکربندی میکند
|
||||
- حسابیکس را نصب و راهاندازی میکند
|
||||
- تنظیمات امنیتی اولیه را اعمال میکند
|
||||
|
||||
## نصب
|
||||
|
||||
* پروژه را در پوشه وب سرور کپی یا کلون کنید. اگر از کنترل پنلهای اشتراکی مانند cpanel یا directadmin استفاده میکنید، فایلها را در پوشه اصلی کپی کنید و پوشه public_html بازنویسی خواهد شد.
|
||||
|
|
930
install.sh
Executable file
930
install.sh
Executable file
|
@ -0,0 +1,930 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Hesabix Installation Script
|
||||
# Version: 2.6
|
||||
# Modernized and optimized for Ubuntu 24.04 LTS
|
||||
# Last Updated: April 28, 2025
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
# Colors for better UI
|
||||
declare -r RED='\e[31m'
|
||||
declare -r GREEN='\e[32m'
|
||||
declare -r YELLOW='\e[33m'
|
||||
declare -r BLUE='\e[34m'
|
||||
declare -r NC='\e[0m'
|
||||
declare -r BOLD='\e[1m'
|
||||
declare -r UNDERLINE='\e[4m'
|
||||
|
||||
# ASCII Art
|
||||
print_logo() {
|
||||
echo -e "${BOLD}${BLUE}"
|
||||
echo " _ _ ___ ___ _ ___ ___ __ __"
|
||||
echo " | || | | __| / __| /_\ | _ ) |_ _| \ \/ /"
|
||||
echo " | __ | | _| \__ \ / _ \ | _ \ | | > < "
|
||||
echo " |_||_| |___| |___/ /_/ \_\ |___/ |___| /_/\_\\"
|
||||
echo -e "${NC}"
|
||||
}
|
||||
|
||||
# Print header
|
||||
print_header() {
|
||||
clear
|
||||
print_logo
|
||||
echo -e "${BOLD}${BLUE}=================================================${NC}"
|
||||
echo -e "${BOLD}${BLUE} Hesabix Installation Script ${NC}"
|
||||
echo -e "${BOLD}${BLUE}=================================================${NC}"
|
||||
echo -e "${YELLOW}Hesabix is a powerful open-source accounting software${NC}"
|
||||
echo -e "${YELLOW}developed with ❤ by Babak Alizadeh (alizadeh.babak)${NC}"
|
||||
echo -e "${YELLOW}License: GNU GPL v3${NC}"
|
||||
echo -e "${YELLOW}Website: ${UNDERLINE}https://hesabix.ir${NC}"
|
||||
echo -e "${YELLOW}Support us: ${UNDERLINE}https://hesabix.ir/page/sponsors${NC} ❤"
|
||||
echo -e "${BOLD}${BLUE}=================================================${NC}\n"
|
||||
}
|
||||
|
||||
# Show the header immediately
|
||||
print_header
|
||||
|
||||
# Configuration
|
||||
declare -r LOG_FILE="/var/log/hesabix_install.log"
|
||||
declare -r LOG_LEVEL="DEBUG" # Options: DEBUG, INFO, WARNING, ERROR
|
||||
declare -r REQUIRED_DISK_SPACE_MB=2000
|
||||
declare -r REQUIRED_MEMORY_MB=1024
|
||||
declare -r MIN_PHP_VERSION="8.2"
|
||||
declare -r NODE_VERSION="20"
|
||||
declare -r COMPOSER_TIMEOUT=600
|
||||
declare -r NPM_TIMEOUT=600
|
||||
|
||||
# Global variables
|
||||
declare SEND_TELEMETRY=false
|
||||
declare domain=""
|
||||
declare apache_user="www-data"
|
||||
|
||||
# Function to log messages in JSON format
|
||||
log_message() {
|
||||
local level="$1"
|
||||
local message="$2"
|
||||
local timestamp
|
||||
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Create JSON log entry
|
||||
local log_entry
|
||||
log_entry=$(printf '{"timestamp":"%s","level":"%s","message":"%s"}\n' "$timestamp" "$level" "$message")
|
||||
|
||||
# Write to log file
|
||||
echo "$log_entry" >> "$LOG_FILE"
|
||||
|
||||
# Display to console based on level
|
||||
case "$level" in
|
||||
"INFO")
|
||||
echo -e "${GREEN}[INFO] $message${NC}"
|
||||
;;
|
||||
"WARNING")
|
||||
echo -e "${YELLOW}[WARNING] $message${NC}"
|
||||
;;
|
||||
"ERROR")
|
||||
echo -e "${RED}[ERROR] $message${NC}"
|
||||
;;
|
||||
"DEBUG")
|
||||
[[ "$LOG_LEVEL" == "DEBUG" ]] && echo -e "${BLUE}[DEBUG] $message${NC}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to initialize logging
|
||||
init_logging() {
|
||||
mkdir -p "$(dirname "$LOG_FILE")" || {
|
||||
echo -e "${RED}Error: Cannot create log directory${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
touch "$LOG_FILE" || {
|
||||
echo -e "${RED}Error: Cannot create log file${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
chmod 644 "$LOG_FILE"
|
||||
|
||||
log_message "INFO" "Hesabix installation script started"
|
||||
log_message "INFO" "Log file: $LOG_FILE"
|
||||
log_message "INFO" "To view logs: tail -f $LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to handle errors
|
||||
handle_error() {
|
||||
local message="$1"
|
||||
local exit_code="${2:-1}"
|
||||
|
||||
log_message "ERROR" "$message"
|
||||
log_message "ERROR" "Installation failed. Check logs: $LOG_FILE"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# Function to check if command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Function to check internet connectivity
|
||||
check_internet() {
|
||||
log_message "INFO" "Checking internet connectivity..."
|
||||
if ! ping -c 1 8.8.8.8 >/dev/null 2>&1; then
|
||||
handle_error "No internet connection detected. Please ensure the server has internet access."
|
||||
fi
|
||||
log_message "INFO" "Internet connectivity verified"
|
||||
}
|
||||
|
||||
# Function to validate system requirements
|
||||
check_system_requirements() {
|
||||
log_message "INFO" "Checking system requirements..."
|
||||
|
||||
# Check root privileges
|
||||
[[ "$EUID" -ne 0 ]] && handle_error "This script must be run as root"
|
||||
|
||||
# Check OS compatibility
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
log_message "INFO" "Detected OS: $NAME $VERSION"
|
||||
|
||||
[[ ! "$ID" =~ ^(ubuntu|debian)$ ]] && handle_error "Only Ubuntu and Debian are supported"
|
||||
else
|
||||
handle_error "Cannot determine OS type"
|
||||
fi
|
||||
|
||||
# Check disk space
|
||||
local available_space
|
||||
available_space=$(df -m / | awk 'NR==2 {print $4}')
|
||||
log_message "DEBUG" "Required disk space: ${REQUIRED_DISK_SPACE_MB}MB"
|
||||
log_message "DEBUG" "Available disk space: ${available_space}MB"
|
||||
|
||||
[[ "$available_space" -lt "$REQUIRED_DISK_SPACE_MB" ]] && \
|
||||
handle_error "Insufficient disk space. Required: ${REQUIRED_DISK_SPACE_MB}MB, Available: ${available_space}MB"
|
||||
|
||||
# Check memory
|
||||
local total_memory
|
||||
total_memory=$(free -m | awk '/^Mem:/{print $2}')
|
||||
log_message "DEBUG" "Required memory: ${REQUIRED_MEMORY_MB}MB"
|
||||
log_message "DEBUG" "Total memory: ${total_memory}MB"
|
||||
|
||||
[[ "$total_memory" -lt "$REQUIRED_MEMORY_MB" ]] && \
|
||||
log_message "WARNING" "Low memory detected. Performance may be affected"
|
||||
|
||||
log_message "INFO" "System requirements check completed"
|
||||
}
|
||||
|
||||
# Function to update package lists
|
||||
update_packages() {
|
||||
log_message "INFO" "Updating package lists..."
|
||||
apt-get update -y || handle_error "Failed to update package lists"
|
||||
}
|
||||
|
||||
# Function to install required tools
|
||||
install_tools() {
|
||||
local tools=("curl" "coreutils" "git" "unzip")
|
||||
|
||||
for tool in "${tools[@]}"; do
|
||||
if ! command_exists "$tool"; then
|
||||
log_message "INFO" "Installing $tool..."
|
||||
apt-get install -y "$tool" || handle_error "Failed to install $tool"
|
||||
log_message "INFO" "$tool installed successfully"
|
||||
else
|
||||
log_message "INFO" "$tool is already installed"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Function to get installed PHP version
|
||||
get_php_version() {
|
||||
local version
|
||||
version=$(php -v | head -n 1 | awk '{print $2}' | cut -d'.' -f1,2)
|
||||
echo "$version"
|
||||
}
|
||||
|
||||
# Function to get all installed PHP versions
|
||||
get_installed_php_versions() {
|
||||
local versions=()
|
||||
for version_dir in /etc/php/*; do
|
||||
if [[ -d "$version_dir" && "$version_dir" =~ /etc/php/[0-9]+\.[0-9]+$ ]]; then
|
||||
versions+=("$(basename "$version_dir")")
|
||||
fi
|
||||
done
|
||||
echo "${versions[@]}"
|
||||
}
|
||||
|
||||
# Function to check if all required extensions are installed for all PHP versions
|
||||
check_required_extensions() {
|
||||
local missing_extensions=()
|
||||
local installed_versions
|
||||
installed_versions=($(get_installed_php_versions))
|
||||
|
||||
for version in "${installed_versions[@]}"; do
|
||||
log_message "INFO" "Checking required extensions for PHP $version..."
|
||||
for ext in raphf http dom xml gd curl simplexml xmlwriter zip; do
|
||||
if ! php"$version" -m | grep -q "$ext"; then
|
||||
missing_extensions+=("php${version}-${ext}")
|
||||
log_message "WARNING" "Extension $ext is missing for PHP $version"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ ${#missing_extensions[@]} -gt 0 ]]; then
|
||||
echo -e "\nWarning: The following extensions are missing:"
|
||||
printf '%s\n' "${missing_extensions[@]}"
|
||||
echo -e "\nDo you want to install them now?"
|
||||
read -p "Install missing extensions? (y/n) [y]: " response
|
||||
|
||||
if [[ "$response" =~ ^[Yy]$ ]] || [[ -z "$response" ]]; then
|
||||
log_message "INFO" "Installing missing extensions..."
|
||||
|
||||
# First try to install from default repositories
|
||||
if ! apt-get install -y "${missing_extensions[@]}"; then
|
||||
log_message "WARNING" "Failed to install from default repositories. Trying custom repositories..."
|
||||
|
||||
# Add custom repository
|
||||
LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php || log_message "WARNING" "PPA already exists"
|
||||
update_packages
|
||||
|
||||
# Try installing again
|
||||
apt-get install -y "${missing_extensions[@]}" || {
|
||||
log_message "ERROR" "Failed to install missing extensions even with custom repository"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Enable extensions after installation
|
||||
for version in "${installed_versions[@]}"; do
|
||||
for ext in raphf http dom xml gd curl simplexml xmlwriter zip; do
|
||||
if ! php"$version" -m | grep -q "$ext"; then
|
||||
log_message "INFO" "Enabling $ext for PHP $version..."
|
||||
phpenmod -v "$version" "$ext" || log_message "WARNING" "Failed to enable $ext for CLI in version $version"
|
||||
phpenmod -v "$version" -s apache2 "$ext" || log_message "WARNING" "Failed to enable $ext for Apache in version $version"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Verify extensions are now installed
|
||||
local still_missing=()
|
||||
for version in "${installed_versions[@]}"; do
|
||||
for ext in raphf http dom xml gd curl simplexml xmlwriter zip; do
|
||||
if ! php"$version" -m | grep -q "$ext"; then
|
||||
still_missing+=("php${version}-${ext}")
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ ${#still_missing[@]} -gt 0 ]]; then
|
||||
echo -e "\nWarning: The following extensions are still missing after installation:"
|
||||
printf '%s\n' "${still_missing[@]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Restart Apache
|
||||
systemctl restart apache2 || log_message "WARNING" "Failed to restart Apache"
|
||||
else
|
||||
log_message "WARNING" "User chose not to install missing extensions"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to verify CLI extensions
|
||||
verify_cli_extensions() {
|
||||
local version="$1"
|
||||
log_message "INFO" "Verifying CLI extensions for PHP $version..."
|
||||
|
||||
# Get the list of enabled extensions from CLI
|
||||
local enabled_extensions
|
||||
enabled_extensions=$(php"$version" -m)
|
||||
|
||||
# Check each required extension
|
||||
for ext in raphf http dom xml gd curl simplexml xmlwriter zip intl mbstring mysql bcmath; do
|
||||
if ! echo "$enabled_extensions" | grep -q "^$ext$"; then
|
||||
log_message "ERROR" "Extension $ext is not enabled in CLI for PHP $version"
|
||||
log_message "INFO" "Attempting to enable $ext for CLI..."
|
||||
|
||||
# Try to enable the extension
|
||||
phpenmod -v "$version" "$ext" || {
|
||||
log_message "ERROR" "Failed to enable $ext for CLI in version $version"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Verify again after enabling
|
||||
enabled_extensions=$(php"$version" -m)
|
||||
if ! echo "$enabled_extensions" | grep -q "^$ext$"; then
|
||||
log_message "ERROR" "Extension $ext is still not enabled in CLI for PHP $version"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to install PHP and extensions
|
||||
install_php() {
|
||||
log_message "INFO" "Checking PHP installation..."
|
||||
|
||||
# Check if php-cli is installed
|
||||
if ! command_exists php; then
|
||||
log_message "INFO" "Installing php-cli..."
|
||||
apt-get install -y php-cli || handle_error "Failed to install php-cli"
|
||||
fi
|
||||
|
||||
# Get current PHP version
|
||||
local current_version
|
||||
current_version=$(get_php_version)
|
||||
log_message "DEBUG" "Current PHP version: $current_version"
|
||||
|
||||
# Check if PHP version meets minimum requirement
|
||||
if [[ "$(echo "$current_version >= $MIN_PHP_VERSION" | bc -l)" -ne 1 ]]; then
|
||||
log_message "INFO" "Installing PHP $MIN_PHP_VERSION or higher..."
|
||||
|
||||
# First try to install from default Ubuntu repositories
|
||||
log_message "INFO" "Attempting to install PHP from default Ubuntu repositories..."
|
||||
apt-get install -y software-properties-common
|
||||
|
||||
local php_packages=(
|
||||
"php${MIN_PHP_VERSION}"
|
||||
"php${MIN_PHP_VERSION}-cli"
|
||||
"libapache2-mod-php${MIN_PHP_VERSION}"
|
||||
"php${MIN_PHP_VERSION}-intl"
|
||||
"php${MIN_PHP_VERSION}-mbstring"
|
||||
"php${MIN_PHP_VERSION}-zip"
|
||||
"php${MIN_PHP_VERSION}-gd"
|
||||
"php${MIN_PHP_VERSION}-mysql"
|
||||
"php${MIN_PHP_VERSION}-curl"
|
||||
"php${MIN_PHP_VERSION}-xml"
|
||||
"php${MIN_PHP_VERSION}-bcmath"
|
||||
"php${MIN_PHP_VERSION}-raphf"
|
||||
"php${MIN_PHP_VERSION}-http"
|
||||
"php${MIN_PHP_VERSION}-dom"
|
||||
"php${MIN_PHP_VERSION}-simplexml"
|
||||
"php${MIN_PHP_VERSION}-xmlwriter"
|
||||
)
|
||||
|
||||
# Try installing from default repositories
|
||||
if ! apt-get install -y "${php_packages[@]}"; then
|
||||
log_message "WARNING" "Failed to install PHP from default repositories. Trying custom repositories..."
|
||||
|
||||
# Add custom repository if default installation fails
|
||||
LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php || log_message "WARNING" "PPA already exists"
|
||||
update_packages
|
||||
|
||||
# Try installing again with custom repository
|
||||
apt-get install -y "${php_packages[@]}" || handle_error "Failed to install PHP packages"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get all installed PHP versions
|
||||
local installed_versions
|
||||
installed_versions=($(get_installed_php_versions))
|
||||
log_message "INFO" "Found installed PHP versions: ${installed_versions[*]}"
|
||||
|
||||
# Install and configure extensions for all PHP versions
|
||||
for version in "${installed_versions[@]}"; do
|
||||
log_message "INFO" "Configuring PHP $version..."
|
||||
|
||||
# Install all required extensions for this version
|
||||
local required_extensions=(
|
||||
"php${version}-raphf"
|
||||
"php${version}-http"
|
||||
"php${version}-dom"
|
||||
"php${version}-xml"
|
||||
"php${version}-gd"
|
||||
"php${version}-curl"
|
||||
"php${version}-simplexml"
|
||||
"php${version}-xmlwriter"
|
||||
"php${version}-zip"
|
||||
"php${version}-intl"
|
||||
"php${version}-mbstring"
|
||||
"php${version}-mysql"
|
||||
"php${version}-bcmath"
|
||||
)
|
||||
|
||||
# Install extensions
|
||||
log_message "INFO" "Installing required extensions for PHP $version..."
|
||||
apt-get install -y "${required_extensions[@]}" || {
|
||||
log_message "WARNING" "Failed to install some extensions for PHP $version. Trying custom repository..."
|
||||
|
||||
# Add custom repository if needed
|
||||
LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php || log_message "WARNING" "PPA already exists"
|
||||
update_packages
|
||||
|
||||
# Try installing again
|
||||
apt-get install -y "${required_extensions[@]}" || handle_error "Failed to install PHP extensions for version $version"
|
||||
}
|
||||
|
||||
# Enable all extensions
|
||||
for ext in raphf http dom xml gd curl simplexml xmlwriter zip intl mbstring mysql bcmath; do
|
||||
log_message "INFO" "Enabling $ext for PHP $version..."
|
||||
phpenmod -v "$version" "$ext" || log_message "WARNING" "Failed to enable $ext for CLI in version $version"
|
||||
phpenmod -v "$version" -s apache2 "$ext" || log_message "WARNING" "Failed to enable $ext for Apache in version $version"
|
||||
done
|
||||
|
||||
# Create PHP configuration for both CLI and Apache
|
||||
for sapi in cli apache2; do
|
||||
if [[ -d "/etc/php/${version}/${sapi}/conf.d" ]]; then
|
||||
cat > "/etc/php/${version}/${sapi}/conf.d/99-hesabix.ini" << EOF
|
||||
extension=raphf.so
|
||||
extension=http.so
|
||||
extension=dom.so
|
||||
extension=xml.so
|
||||
extension=gd.so
|
||||
extension=curl.so
|
||||
extension=simplexml.so
|
||||
extension=xmlwriter.so
|
||||
extension=zip.so
|
||||
extension=intl.so
|
||||
extension=mbstring.so
|
||||
extension=mysql.so
|
||||
extension=bcmath.so
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
|
||||
# Enable PHP module for Apache
|
||||
if [[ -d "/etc/php/${version}/apache2" ]]; then
|
||||
a2enmod "php${version}" || handle_error "Failed to enable PHP module for version $version"
|
||||
fi
|
||||
|
||||
# Verify extensions are loaded
|
||||
log_message "INFO" "Verifying extensions for PHP $version..."
|
||||
for ext in raphf http dom xml gd curl simplexml xmlwriter zip intl mbstring mysql bcmath; do
|
||||
if ! php"$version" -m | grep -q "$ext"; then
|
||||
log_message "ERROR" "Extension $ext is not loaded for PHP $version"
|
||||
handle_error "Failed to load extension $ext for PHP $version"
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify CLI extensions specifically
|
||||
if ! verify_cli_extensions "$version"; then
|
||||
log_message "ERROR" "Failed to verify CLI extensions for PHP $version"
|
||||
handle_error "CLI extensions verification failed for PHP $version"
|
||||
fi
|
||||
done
|
||||
|
||||
# Restart Apache if any PHP version was configured
|
||||
if [[ ${#installed_versions[@]} -gt 0 ]]; then
|
||||
systemctl restart apache2 || handle_error "Failed to restart Apache"
|
||||
fi
|
||||
|
||||
log_message "INFO" "PHP installation and configuration completed for all versions"
|
||||
}
|
||||
|
||||
# Function to install MySQL
|
||||
install_mysql() {
|
||||
log_message "INFO" "Checking MySQL installation..."
|
||||
|
||||
if command_exists mysql; then
|
||||
local mysql_version
|
||||
mysql_version=$(mysql -V | grep -oP '\d+\.\d+\.\d+' | head -1)
|
||||
log_message "INFO" "MySQL/MariaDB version $mysql_version is installed"
|
||||
else
|
||||
log_message "INFO" "Installing MySQL..."
|
||||
apt-get install -y mysql-server || handle_error "Failed to install MySQL"
|
||||
systemctl enable mysql || handle_error "Failed to enable MySQL"
|
||||
systemctl start mysql || handle_error "Failed to start MySQL"
|
||||
log_message "INFO" "MySQL installed successfully"
|
||||
fi
|
||||
|
||||
# Verify MySQL service
|
||||
if ! systemctl is-active --quiet mysql; then
|
||||
handle_error "MySQL service is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to install Node.js
|
||||
install_nodejs() {
|
||||
log_message "INFO" "Checking Node.js installation..."
|
||||
|
||||
if command_exists node; then
|
||||
local node_version
|
||||
node_version=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||
if [[ "$node_version" -ge "$NODE_VERSION" ]]; then
|
||||
log_message "INFO" "Node.js version $(node -v) is installed"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
log_message "INFO" "Installing Node.js $NODE_VERSION..."
|
||||
check_internet
|
||||
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - || handle_error "Failed to setup Node.js repository"
|
||||
apt-get install -y nodejs || handle_error "Failed to install Node.js"
|
||||
|
||||
# Verify npm installation
|
||||
if ! command_exists npm; then
|
||||
log_message "INFO" "Installing npm..."
|
||||
apt-get install -y npm || handle_error "Failed to install npm"
|
||||
fi
|
||||
|
||||
log_message "INFO" "Node.js and npm installed successfully"
|
||||
}
|
||||
|
||||
# Function to install Apache
|
||||
install_apache() {
|
||||
log_message "INFO" "Checking Apache installation..."
|
||||
|
||||
if command_exists apache2; then
|
||||
log_message "INFO" "Apache is installed"
|
||||
else
|
||||
log_message "INFO" "Installing Apache..."
|
||||
apt-get install -y apache2 || handle_error "Failed to install Apache"
|
||||
systemctl enable apache2 || handle_error "Failed to enable Apache"
|
||||
systemctl start apache2 || handle_error "Failed to start Apache"
|
||||
log_message "INFO" "Apache installed successfully"
|
||||
fi
|
||||
|
||||
# Verify Apache service
|
||||
if ! systemctl is-active --quiet apache2; then
|
||||
handle_error "Apache service is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to install Composer
|
||||
install_composer() {
|
||||
log_message "INFO" "Checking Composer installation..."
|
||||
|
||||
if command_exists composer; then
|
||||
log_message "INFO" "Composer is already installed"
|
||||
return
|
||||
fi
|
||||
|
||||
log_message "INFO" "Installing Composer..."
|
||||
check_internet
|
||||
local composer_setup="/tmp/composer-setup.php"
|
||||
|
||||
# Set environment variables for Composer
|
||||
export COMPOSER_ALLOW_SUPERUSER=1
|
||||
export COMPOSER_HOME=/root/.config/composer
|
||||
export COMPOSER_NO_INTERACTION=1
|
||||
|
||||
php -r "copy('https://getcomposer.org/installer', '$composer_setup');" || handle_error "Failed to download Composer installer"
|
||||
php "$composer_setup" --install-dir=/usr/local/bin --filename=composer || handle_error "Failed to install Composer"
|
||||
rm -f "$composer_setup"
|
||||
|
||||
# Configure Composer to work with root user
|
||||
mkdir -p /root/.config/composer
|
||||
cat > /root/.config/composer/config.json << EOF
|
||||
{
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": true,
|
||||
"symfony/flex": true,
|
||||
"symfony/runtime": true
|
||||
},
|
||||
"sort-packages": true,
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"no-interaction": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
command_exists composer || handle_error "Composer installation verification failed"
|
||||
log_message "INFO" "Composer installed successfully"
|
||||
}
|
||||
|
||||
# Function to setup SSL with Let's Encrypt
|
||||
setup_ssl() {
|
||||
local domain="$1"
|
||||
|
||||
log_message "INFO" "Setting up SSL for $domain..."
|
||||
|
||||
check_internet
|
||||
if ! command_exists certbot; then
|
||||
log_message "INFO" "Installing Certbot..."
|
||||
apt-get install -y certbot python3-certbot-apache || handle_error "Failed to install Certbot"
|
||||
fi
|
||||
|
||||
certbot --apache -d "$domain" -d "www.$domain" --non-interactive --agree-tos --email "admin@$domain" || {
|
||||
log_message "WARNING" "Failed to setup SSL automatically. Please configure SSL manually."
|
||||
}
|
||||
|
||||
log_message "INFO" "SSL setup completed"
|
||||
}
|
||||
|
||||
# Function to validate domain
|
||||
validate_domain() {
|
||||
local domain="$1"
|
||||
if [[ "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
handle_error "Invalid domain format: $domain. Must be like example.com"
|
||||
}
|
||||
|
||||
# Function to get domain input
|
||||
get_domain() {
|
||||
local domain
|
||||
while true; do
|
||||
read -p "Please enter the domain for Hesabix installation (e.g., example.com): " domain
|
||||
if validate_domain "$domain"; then
|
||||
echo "$domain"
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Function to setup domain
|
||||
setup_domain() {
|
||||
local domain="$1"
|
||||
local domain_path="/var/www/html/$domain/public_html"
|
||||
|
||||
log_message "INFO" "Setting up domain: $domain"
|
||||
|
||||
# Create domain directory
|
||||
mkdir -p "$domain_path" || handle_error "Failed to create domain directory"
|
||||
chown -R "$apache_user:$apache_user" "$domain_path"
|
||||
chmod -R 755 "$domain_path"
|
||||
|
||||
# Enable Apache rewrite module
|
||||
a2enmod rewrite || handle_error "Failed to enable Apache rewrite module"
|
||||
|
||||
# Create Apache virtual host
|
||||
local config_file="/etc/apache2/sites-available/$domain.conf"
|
||||
cat > "$config_file" << EOF
|
||||
<VirtualHost *:80>
|
||||
ServerName $domain
|
||||
ServerAlias www.$domain
|
||||
DocumentRoot $domain_path
|
||||
|
||||
<Directory $domain_path>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog \${APACHE_LOG_DIR}/$domain-error.log
|
||||
CustomLog \${APACHE_LOG_DIR}/$domain-access.log combined
|
||||
</VirtualHost>
|
||||
EOF
|
||||
|
||||
apache2ctl configtest || handle_error "Apache configuration test failed"
|
||||
a2ensite "$domain.conf" || handle_error "Failed to enable Apache site"
|
||||
systemctl restart apache2 || handle_error "Failed to restart Apache"
|
||||
|
||||
log_message "INFO" "Domain setup completed"
|
||||
}
|
||||
|
||||
# Function to setup database
|
||||
setup_database() {
|
||||
local domain="$1"
|
||||
local db_name="hesabix_$(echo "$domain" | tr '.-' '_')"
|
||||
local db_user="hesabix_user"
|
||||
local db_password
|
||||
db_password=$(openssl rand -base64 12)
|
||||
local domain_path="/var/www/html/$domain"
|
||||
|
||||
log_message "INFO" "Setting up database: $db_name"
|
||||
|
||||
# Verify MySQL is running
|
||||
if ! systemctl is-active --quiet mysql; then
|
||||
handle_error "MySQL service is not running"
|
||||
fi
|
||||
|
||||
# Create database
|
||||
mysql -e "CREATE DATABASE IF NOT EXISTS \`$db_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" || \
|
||||
handle_error "Failed to create database"
|
||||
|
||||
# Create user
|
||||
mysql -e "CREATE USER IF NOT EXISTS '$db_user'@'localhost' IDENTIFIED BY '$db_password';" || \
|
||||
handle_error "Failed to create database user"
|
||||
|
||||
# Grant privileges
|
||||
mysql -e "GRANT ALL PRIVILEGES ON \`$db_name\`.* TO '$db_user'@'localhost';" || \
|
||||
handle_error "Failed to grant database privileges"
|
||||
mysql -e "FLUSH PRIVILEGES;" || handle_error "Failed to flush privileges"
|
||||
|
||||
# Update environment configuration
|
||||
local env_file="$domain_path/hesabixCore/.env.local.php"
|
||||
cat > "$env_file" << EOF
|
||||
<?php
|
||||
return [
|
||||
'APP_ENV' => 'prod',
|
||||
'APP_SECRET' => '$(openssl rand -hex 16)',
|
||||
'DATABASE_URL' => 'mysql://$db_user:$db_password@127.0.0.1:3306/$db_name?serverVersion=8.0.32&charset=utf8mb4',
|
||||
'MESSENGER_TRANSPORT_DSN' => 'doctrine://default?auto_setup=0',
|
||||
'MAILER_DSN' => 'null://null',
|
||||
'CORS_ALLOW_ORIGIN' => '*',
|
||||
'LOCK_DSN' => 'flock',
|
||||
];
|
||||
EOF
|
||||
|
||||
# Set proper permissions
|
||||
chown "$apache_user:$apache_user" "$env_file"
|
||||
chmod 644 "$env_file"
|
||||
|
||||
log_message "INFO" "Database setup completed"
|
||||
}
|
||||
|
||||
# Function to install software
|
||||
install_software() {
|
||||
local domain="$1"
|
||||
local domain_path="/var/www/html/$domain"
|
||||
local current_version
|
||||
current_version=$(get_php_version)
|
||||
|
||||
log_message "INFO" "Installing software for domain: $domain"
|
||||
|
||||
# Ensure domain directory exists
|
||||
if [[ ! -d "$domain_path" ]]; then
|
||||
handle_error "Domain directory $domain_path does not exist"
|
||||
fi
|
||||
|
||||
cd "$domain_path" || handle_error "Failed to change to domain directory: $domain_path"
|
||||
|
||||
# Check internet connectivity
|
||||
check_internet
|
||||
|
||||
# Check required extensions before proceeding
|
||||
if ! check_required_extensions; then
|
||||
echo -e "\nWarning: Some required extensions are missing or could not be installed."
|
||||
echo -e "Do you want to continue with the installation anyway?"
|
||||
echo -e "Note: This might cause issues with some features."
|
||||
read -p "Continue? (y/n) [n]: " response
|
||||
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
log_message "ERROR" "User chose not to continue with missing extensions"
|
||||
handle_error "Installation aborted by user"
|
||||
fi
|
||||
|
||||
# If user continues, add --ignore-platform-req flags
|
||||
local composer_flags="--ignore-platform-req=ext-simplexml --ignore-platform-req=ext-xmlwriter --ignore-platform-req=ext-zip"
|
||||
else
|
||||
local composer_flags=""
|
||||
fi
|
||||
|
||||
# Initialize git repository
|
||||
log_message "INFO" "Initializing git repository in $domain_path..."
|
||||
git init || handle_error "Failed to initialize git repository"
|
||||
|
||||
# Add remote repository
|
||||
git remote add origin https://github.com/morrning/hesabixCore.git || \
|
||||
handle_error "Failed to add remote repository"
|
||||
|
||||
# Fetch and checkout the repository
|
||||
log_message "INFO" "Fetching repository contents..."
|
||||
git fetch origin || handle_error "Failed to fetch repository"
|
||||
git checkout -b main origin/main || handle_error "Failed to checkout repository"
|
||||
|
||||
# Verify repository path
|
||||
if [[ ! -d "$domain_path" ]]; then
|
||||
handle_error "Repository directory $domain_path was not created"
|
||||
fi
|
||||
|
||||
cd "$domain_path" || handle_error "Failed to change to domain directory: $domain_path"
|
||||
|
||||
# Verify composer.json exists in hesabixCore directory
|
||||
if [[ ! -f "hesabixCore/composer.json" ]]; then
|
||||
handle_error "composer.json file not found in $domain_path/hesabixCore"
|
||||
fi
|
||||
|
||||
# Configure Composer
|
||||
export COMPOSER_ALLOW_SUPERUSER=1
|
||||
export COMPOSER_HOME=/root/.config/composer
|
||||
export COMPOSER_NO_INTERACTION=1
|
||||
|
||||
# Install dependencies
|
||||
log_message "INFO" "Installing Composer dependencies..."
|
||||
cd "hesabixCore" || handle_error "Failed to change to hesabixCore directory"
|
||||
|
||||
# Try to install dependencies
|
||||
if ! timeout "$COMPOSER_TIMEOUT" composer install --no-interaction --optimize-autoloader $composer_flags; then
|
||||
log_message "ERROR" "Failed to install Composer dependencies"
|
||||
handle_error "Failed to install Composer dependencies"
|
||||
fi
|
||||
|
||||
# Generate environment
|
||||
log_message "INFO" "Generating environment file..."
|
||||
timeout 300 composer dump-env prod --no-interaction || \
|
||||
handle_error "Failed to generate environment file"
|
||||
|
||||
# Verify environment file
|
||||
if [[ ! -f ".env.local.php" ]]; then
|
||||
handle_error "Environment file (.env.local.php) was not generated"
|
||||
fi
|
||||
|
||||
log_message "INFO" "Software installation completed"
|
||||
}
|
||||
|
||||
# Function to setup web UI
|
||||
setup_web_ui() {
|
||||
local domain="$1"
|
||||
local domain_path="/var/www/html/$domain"
|
||||
local webui_path="$domain_path/webUI"
|
||||
|
||||
log_message "INFO" "Setting up web UI..."
|
||||
|
||||
# Check if webUI directory exists
|
||||
if [[ ! -d "$webui_path" ]]; then
|
||||
handle_error "Web UI directory ($webui_path) does not exist"
|
||||
fi
|
||||
|
||||
cd "$webui_path" || handle_error "Failed to change to webUI directory"
|
||||
|
||||
# Install dependencies
|
||||
log_message "INFO" "Installing web UI dependencies..."
|
||||
timeout "$NPM_TIMEOUT" npm install || handle_error "Failed to install web UI dependencies"
|
||||
|
||||
# Build web UI
|
||||
log_message "INFO" "Building web UI..."
|
||||
timeout "$NPM_TIMEOUT" npm run build-only || handle_error "Failed to build web UI"
|
||||
|
||||
log_message "INFO" "Web UI setup completed"
|
||||
}
|
||||
|
||||
# Function to set Apache ownership
|
||||
set_apache_ownership() {
|
||||
local domain="$1"
|
||||
local domain_path="/var/www/html/$domain"
|
||||
|
||||
log_message "INFO" "Setting Apache ownership..."
|
||||
|
||||
chown -R "$apache_user:$apache_user" "$domain_path" || \
|
||||
handle_error "Failed to set Apache ownership"
|
||||
|
||||
find "$domain_path" -type d -exec chmod 755 {} \;
|
||||
find "$domain_path" -type f -exec chmod 644 {} \;
|
||||
|
||||
log_message "INFO" "Apache ownership set"
|
||||
}
|
||||
|
||||
# Function to show installation summary
|
||||
show_installation_summary() {
|
||||
local domain="$1"
|
||||
local domain_path="/var/www/html/$domain"
|
||||
|
||||
log_message "INFO" "Showing installation summary..."
|
||||
|
||||
echo -e "\n${BOLD}${BLUE}=================================================${NC}"
|
||||
echo -e "${BOLD}${BLUE} Hesabix Installation Summary ${NC}"
|
||||
echo -e "${BOLD}${BLUE}=================================================${NC}"
|
||||
echo -e "\n${YELLOW}Installation Details:${NC}"
|
||||
echo -e "Domain: $domain"
|
||||
echo -e "Installation Path: $domain_path"
|
||||
echo -e "Web Server: Apache"
|
||||
echo -e "PHP Version: $(php -v | head -n 1 | awk '{print $2}')"
|
||||
echo -e "Node.js Version: $(node -v)"
|
||||
|
||||
echo -e "\n${YELLOW}Next Steps:${NC}"
|
||||
echo -e "1. Configure domain DNS to point to this server"
|
||||
echo -e "2. Access the web interface: https://$domain"
|
||||
echo -e "3. Register the first user (system administrator)"
|
||||
|
||||
echo -e "\n${YELLOW}Support:${NC}"
|
||||
echo -e "• Developer: Babak Alizadeh (alizadeh.babak)"
|
||||
echo -e "• License: GNU GPL v3"
|
||||
echo -e "• Website: ${UNDERLINE}https://hesabix.ir${NC}"
|
||||
echo -e "• Support us: ${UNDERLINE}https://hesabix.ir/page/sponsors${NC} ❤"
|
||||
|
||||
echo -e "\n${GREEN}Installation completed successfully!${NC}"
|
||||
echo -e "${BOLD}${BLUE}=================================================${NC}"
|
||||
}
|
||||
|
||||
# Function to display telemetry consent
|
||||
display_telemetry_consent() {
|
||||
echo -e "\n${RED}================================================="
|
||||
echo -e "${RED} Anonymous Data Collection "
|
||||
echo -e "${RED}================================================="
|
||||
echo -e "${BLUE}To improve Hesabix, we would like to collect anonymous data:"
|
||||
echo -e "${BLUE}• System information (OS, PHP, MySQL versions)"
|
||||
echo -e "${BLUE}• Installation path and domain"
|
||||
echo -e "${BLUE}• Installation date"
|
||||
|
||||
read -p "Do you agree? (y/n) [n]: " response
|
||||
[[ "$response" =~ ^[Yy]$ ]] && SEND_TELEMETRY=true
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
# Show header first
|
||||
print_header
|
||||
|
||||
# Wait for user to see the header
|
||||
sleep 2
|
||||
|
||||
# Then start logging
|
||||
init_logging
|
||||
check_system_requirements
|
||||
install_tools
|
||||
display_telemetry_consent
|
||||
update_packages
|
||||
install_php
|
||||
install_mysql
|
||||
install_nodejs
|
||||
install_apache
|
||||
install_composer
|
||||
|
||||
domain=$(get_domain)
|
||||
setup_domain "$domain"
|
||||
install_software "$domain"
|
||||
setup_database "$domain"
|
||||
setup_web_ui "$domain"
|
||||
set_apache_ownership "$domain"
|
||||
setup_ssl "$domain"
|
||||
show_installation_summary "$domain"
|
||||
}
|
||||
|
||||
# Execute main with error handling
|
||||
main || handle_error "Installation failed"
|
Loading…
Reference in a new issue