diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..f287f01
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,15 @@
+# Database settings
+MYSQL_ROOT_PASSWORD=change_this_password
+MYSQL_DATABASE=hesabix_db
+MYSQL_USER=hesabix_user
+MYSQL_PASSWORD=change_this_password
+
+# Application settings
+APP_ENV=prod
+APP_SECRET=change_this_secret
+DATABASE_URL=mysql://hesabix_user:change_this_password@db:3306/hesabix_db
+
+# phpMyAdmin settings
+PMA_HOST=db
+PMA_USER=hesabix_user
+PMA_PASSWORD=change_this_password
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d316c39..8e8b56d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,45 @@
+# Environment files
+.env
+.env.local
+.env.*.local
+
+# Dependency directories
+/vendor/
+/node_modules/
+/webUI/node_modules/
+
+# Log files
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Temporary files
+/tmp/
+/temp/
+.DS_Store
+Thumbs.db
+
+# IDE files
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# Compiled files
+/webUI/dist/
+/webUI/build/
+
+# Database files
+*.sql
+*.sqlite
+*.sqlite3
+
+# Docker files
+.docker/
+docker-compose.override.yml
hesabixArchive/
hesabixBackup/
backup/
-.idea/
public_html/u/*
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..a82753d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,56 @@
+# Use PHP 8.3 with Apache as base image
+FROM php:8.3-apache
+
+# Install required packages
+RUN apt-get update && apt-get install -y \
+ git \
+ curl \
+ libpng-dev \
+ libonig-dev \
+ libxml2-dev \
+ libzip-dev \
+ zip \
+ unzip \
+ nodejs \
+ npm
+
+# Install PHP extensions
+RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
+
+# Install Composer
+COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+
+# Set working directory
+WORKDIR /var/www/html
+
+# Copy project files
+COPY hesabixCore/ /var/www/html/hesabixCore/
+COPY webUI/ /var/www/html/webUI/
+COPY public_html/ /var/www/html/public_html/
+
+# Set permissions
+RUN chown -R www-data:www-data /var/www/html \
+ && chmod -R 755 /var/www/html
+
+# Enable Apache modules
+RUN a2enmod rewrite
+
+# Copy Apache configuration
+COPY docker/apache.conf /etc/apache2/sites-available/000-default.conf
+
+# Install PHP dependencies
+WORKDIR /var/www/html/hesabixCore
+RUN composer install --no-interaction --optimize-autoloader
+
+# Install Node.js dependencies and build web UI
+WORKDIR /var/www/html/webUI
+RUN npm install && npm run build-only
+
+# Return to main directory
+WORKDIR /var/www/html
+
+# Expose ports
+EXPOSE 80
+
+# Run command
+CMD ["apache2-foreground"]
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..25a7de5
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,80 @@
+version: '3.8'
+
+services:
+ # Web service
+ web:
+ build: .
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ - ./hesabixCore:/var/www/html/hesabixCore
+ - ./webUI:/var/www/html/webUI
+ - ./public_html:/var/www/html/public_html
+ - ./hesabixBackup:/var/www/html/hesabixBackup
+ - ./ssl:/etc/ssl/private
+ depends_on:
+ - db
+ environment:
+ - DATABASE_URL=mysql://hesabix_user:hesabix_password@db:3306/hesabix_db
+ - APP_ENV=prod
+ - APP_SECRET=${APP_SECRET:-your-secret-key}
+ - DOMAIN=${DOMAIN:-localhost}
+ networks:
+ - hesabix-network
+ restart: unless-stopped
+
+ # Database service
+ db:
+ image: mysql:8.0
+ environment:
+ - MYSQL_ROOT_PASSWORD=root_password
+ - MYSQL_DATABASE=hesabix_db
+ - MYSQL_USER=hesabix_user
+ - MYSQL_PASSWORD=hesabix_password
+ volumes:
+ - mysql_data:/var/lib/mysql
+ - ./hesabixBackup:/docker-entrypoint-initdb.d
+ networks:
+ - hesabix-network
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot_password"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
+ restart: unless-stopped
+
+ # phpMyAdmin service
+ phpmyadmin:
+ image: phpmyadmin/phpmyadmin
+ ports:
+ - "8080:80"
+ environment:
+ - PMA_HOST=db
+ - PMA_USER=hesabix_user
+ - PMA_PASSWORD=hesabix_password
+ depends_on:
+ - db
+ networks:
+ - hesabix-network
+ restart: unless-stopped
+
+ # Certbot service for SSL
+ certbot:
+ image: certbot/certbot
+ volumes:
+ - ./ssl:/etc/letsencrypt
+ - ./public_html:/var/www/html
+ depends_on:
+ - web
+ entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
+ networks:
+ - hesabix-network
+ restart: unless-stopped
+
+networks:
+ hesabix-network:
+ driver: bridge
+
+volumes:
+ mysql_data:
\ No newline at end of file
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..d7a5373
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,68 @@
+# راهنمای استقرار Hesabix با Docker
+
+این راهنما به شما کمک میکند تا Hesabix را با استفاده از Docker مستقر کنید.
+
+## پیشنیازها
+
+- Docker
+- Docker Compose
+
+## مراحل نصب
+
+1. ابتدا مخزن را کلون کنید:
+```bash
+git clone https://github.com/your-username/hesabix.git
+cd hesabix
+```
+
+2. فایلهای تنظیمات را کپی کنید:
+```bash
+cp .env.example .env
+```
+
+3. مقادیر مورد نظر خود را در فایل `.env` تنظیم کنید.
+
+4. ساخت و اجرای کانتینرها:
+```bash
+docker-compose up -d
+```
+
+## دسترسی به سرویسها
+
+- وبسایت: http://localhost
+- phpMyAdmin: http://localhost:8080
+
+## اطلاعات ورود به دیتابیس
+
+- نام کاربری: hesabix_user
+- رمز عبور: hesabix_password (یا مقدار تعیین شده در فایل .env)
+- نام دیتابیس: hesabix_db
+- هاست: db
+- پورت: 3306
+
+## دستورات مفید
+
+- مشاهده لاگها:
+```bash
+docker-compose logs -f
+```
+
+- توقف سرویسها:
+```bash
+docker-compose down
+```
+
+- راهاندازی مجدد سرویسها:
+```bash
+docker-compose restart
+```
+
+## نکات امنیتی
+
+1. حتماً رمزهای عبور پیشفرض را در فایل `.env` تغییر دهید.
+2. از SSL/TLS برای اتصالات استفاده کنید.
+3. فایلهای حساس را در `.gitignore` قرار دهید.
+
+## پشتیبانی
+
+برای گزارش مشکلات یا درخواست کمک، لطفاً یک issue در مخزن GitHub ایجاد کنید.
\ No newline at end of file
diff --git a/docker/apache.conf b/docker/apache.conf
new file mode 100644
index 0000000..49f28bb
--- /dev/null
+++ b/docker/apache.conf
@@ -0,0 +1,13 @@
+
+ ServerAdmin webmaster@localhost
+ DocumentRoot /var/www/html/public_html
+
+
+ Options Indexes FollowSymLinks
+ AllowOverride All
+ Require all granted
+
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
\ No newline at end of file
diff --git a/docker/check-requirements.sh b/docker/check-requirements.sh
new file mode 100644
index 0000000..6caa7b5
--- /dev/null
+++ b/docker/check-requirements.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# Colors for better display
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+# Required values
+REQUIRED_DISK_SPACE_MB=2000
+REQUIRED_MEMORY_MB=1024
+
+# Function to display messages
+print_message() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+# Function to display errors
+print_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Function to display warnings
+print_warning() {
+ echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+# Check root privileges
+if [ "$EUID" -ne 0 ]; then
+ print_error "This script must be run as root"
+ exit 1
+fi
+
+# Check Docker installation
+if ! command -v docker &> /dev/null; then
+ print_error "Docker is not installed. Please install Docker first."
+ exit 1
+fi
+
+# Check Docker Compose installation
+if ! command -v docker-compose &> /dev/null; then
+ print_error "Docker Compose is not installed. Please install Docker Compose first."
+ exit 1
+fi
+
+# Check disk space
+available_space=$(df -m / | awk 'NR==2 {print $4}')
+if [ "$available_space" -lt "$REQUIRED_DISK_SPACE_MB" ]; then
+ print_error "Insufficient disk space. Required: ${REQUIRED_DISK_SPACE_MB}MB, Available: ${available_space}MB"
+ exit 1
+fi
+
+# Check memory
+total_memory=$(free -m | awk '/^Mem:/{print $2}')
+if [ "$total_memory" -lt "$REQUIRED_MEMORY_MB" ]; then
+ print_warning "Low memory detected. Performance may be affected."
+fi
+
+# Check internet connectivity
+print_message "Checking internet connectivity..."
+if ! ping -c 1 8.8.8.8 >/dev/null 2>&1; then
+ print_error "No internet connection detected. Please ensure the server has internet access."
+ exit 1
+fi
+
+print_message "System requirements check completed successfully."
\ No newline at end of file
diff --git a/docker/init-db.sh b/docker/init-db.sh
new file mode 100644
index 0000000..b54204f
--- /dev/null
+++ b/docker/init-db.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# Colors for better display
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+# Function to display messages
+print_message() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+# Function to display errors
+print_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Function to display warnings
+print_warning() {
+ echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+# Wait for MySQL to be ready
+print_message "Waiting for MySQL to be ready..."
+while ! docker-compose exec -T db mysqladmin ping -h localhost -u root -proot_password --silent; do
+ sleep 1
+done
+
+# Import initial database structure
+print_message "Importing initial database structure..."
+if [ -f "hesabixBackup/databasefiles/hesabix-db-default.sql" ]; then
+ docker-compose exec -T db mysql -u root -proot_password hesabix_db < hesabixBackup/databasefiles/hesabix-db-default.sql
+ if [ $? -eq 0 ]; then
+ print_message "Initial database structure imported successfully."
+ else
+ print_error "Failed to import initial database structure."
+ exit 1
+ fi
+else
+ print_error "Initial database file not found at hesabixBackup/databasefiles/hesabix-db-default.sql"
+ exit 1
+fi
+
+# Update database schema
+print_message "Updating database schema..."
+docker-compose exec -T web php /var/www/html/hesabixCore/bin/console doctrine:schema:update --force
+
+if [ $? -eq 0 ]; then
+ print_message "Database schema updated successfully."
+else
+ print_error "Failed to update database schema."
+ exit 1
+fi
+
+print_message "Database initialization completed successfully."
\ No newline at end of file
diff --git a/docker/setup.sh b/docker/setup.sh
new file mode 100644
index 0000000..39a24af
--- /dev/null
+++ b/docker/setup.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+# Colors for better display
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+# Function to display messages
+print_message() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+# Function to display errors
+print_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Function to display warnings
+print_warning() {
+ echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+# Check system requirements
+print_message "Checking system requirements..."
+chmod +x docker/check-requirements.sh
+./docker/check-requirements.sh
+
+if [ $? -ne 0 ]; then
+ print_error "System requirements check failed."
+ exit 1
+fi
+
+# Check if Docker is installed
+if ! command -v docker &> /dev/null; then
+ print_error "Docker is not installed. Please install Docker first."
+ exit 1
+fi
+
+# Check if Docker Compose is installed
+if ! command -v docker-compose &> /dev/null; then
+ print_error "Docker Compose is not installed. Please install Docker Compose first."
+ exit 1
+fi
+
+# Copy .env.example to .env if it doesn't exist
+if [ ! -f .env ]; then
+ print_message "Copying .env.example to .env"
+ cp .env.example .env
+ print_warning "Please configure values in the .env file."
+ exit 0
+fi
+
+# Create SSL directory if it doesn't exist
+if [ ! -d "ssl" ]; then
+ print_message "Creating SSL directory..."
+ mkdir -p ssl
+fi
+
+# Build and start containers
+print_message "Building and starting containers..."
+docker-compose up -d
+
+# Check container status
+if [ $? -eq 0 ]; then
+ print_message "Containers started successfully."
+else
+ print_error "Error starting containers."
+ exit 1
+fi
+
+# Initialize database
+print_message "Initializing database..."
+chmod +x docker/init-db.sh
+./docker/init-db.sh
+
+if [ $? -eq 0 ]; then
+ print_message "Database initialized successfully."
+else
+ print_error "Error initializing database."
+ exit 1
+fi
+
+# Setup SSL if domain is provided
+if [ -n "$DOMAIN" ] && [ "$DOMAIN" != "localhost" ]; then
+ print_message "Setting up SSL for $DOMAIN..."
+ docker-compose run --rm certbot certonly --webroot --webroot-path /var/www/html -d $DOMAIN -d www.$DOMAIN --email admin@$DOMAIN --agree-tos --non-interactive
+
+ if [ $? -eq 0 ]; then
+ print_message "SSL setup completed successfully."
+ else
+ print_warning "SSL setup failed. Continuing without SSL."
+ fi
+fi
+
+print_message "Installation completed successfully."
+print_message "Website: http://localhost"
+if [ -n "$DOMAIN" ] && [ "$DOMAIN" != "localhost" ]; then
+ print_message "Secure Website: https://$DOMAIN"
+fi
+print_message "phpMyAdmin: http://localhost:8080"
+
+# Display logs
+print_message "Displaying logs (press Ctrl+C to exit)..."
+docker-compose logs -f
\ No newline at end of file
diff --git a/install.sh b/install.sh
index 7d2a7f9..2c0fa9a 100755
--- a/install.sh
+++ b/install.sh
@@ -50,7 +50,6 @@ 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
@@ -182,6 +181,9 @@ update_packages() {
install_tools() {
local tools=("curl" "coreutils" "git" "unzip")
+ # Update package lists first
+ update_packages
+
for tool in "${tools[@]}"; do
if ! command_exists "$tool"; then
log_message "INFO" "Installing $tool..."
@@ -213,77 +215,25 @@ get_installed_php_versions() {
# Function to check if all required extensions are installed for all PHP versions
check_required_extensions() {
- local missing_extensions=()
+ local missing_packages=()
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"
+ log_message "INFO" "Checking required packages for PHP $version..."
+ for pkg in 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; do
+ if ! dpkg -l | grep -q "^ii $pkg "; then
+ missing_packages+=("$pkg")
+ log_message "WARNING" "Package $pkg is missing"
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
+ if [[ ${#missing_packages[@]} -gt 0 ]]; then
+ echo -e "\nWarning: The following packages are missing:"
+ printf '%s\n' "${missing_packages[@]}"
+ echo -e "\nThese packages will need to be installed manually after the installation is complete."
+ return 0
fi
return 0
@@ -291,29 +241,28 @@ check_required_extensions() {
# Function to verify CLI extensions
verify_cli_extensions() {
- local version="$1"
- log_message "INFO" "Verifying CLI extensions for PHP $version..."
+ log_message "INFO" "Verifying CLI extensions for PHP..."
# Get the list of enabled extensions from CLI
local enabled_extensions
- enabled_extensions=$(php"$version" -m)
+ enabled_extensions=$(php -m)
# Check each required extension
- for ext in raphf http dom xml gd curl simplexml xmlwriter zip intl mbstring mysql bcmath; do
+ for ext in iconv 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 "ERROR" "Extension $ext is not enabled in CLI"
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"
+ phpenmod "$ext" || {
+ log_message "ERROR" "Failed to enable $ext for CLI"
return 1
}
# Verify again after enabling
- enabled_extensions=$(php"$version" -m)
+ enabled_extensions=$(php -m)
if ! echo "$enabled_extensions" | grep -q "^$ext$"; then
- log_message "ERROR" "Extension $ext is still not enabled in CLI for PHP $version"
+ log_message "ERROR" "Extension $ext is still not enabled in CLI"
return 1
fi
fi
@@ -326,157 +275,67 @@ verify_cli_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
+ # Update package lists first
+ update_packages
- # Get current PHP version
- local current_version
- current_version=$(get_php_version)
- log_message "DEBUG" "Current PHP version: $current_version"
+ # Remove any existing PHP packages first
+ apt-get remove --purge php* -y
+ apt-get autoremove -y
+ apt-get clean
- # 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
+ # Install PHP and required extensions
+ log_message "INFO" "Installing PHP and required extensions..."
+ local php_packages=(
+ "php"
+ "php-cli"
+ "libapache2-mod-php"
+ "php-intl"
+ "php-mbstring"
+ "php-zip"
+ "php-gd"
+ "php-mysql"
+ "php-curl"
+ "php-xml"
+ "php-bcmath"
+ "php-raphf"
+ "php-http"
+ "php-dom"
+ "php-simplexml"
+ "php-xmlwriter"
+ "php-iconv"
+ )
- # 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"
+ # Install each package individually to handle dependencies
+ for pkg in "${php_packages[@]}"; do
+ log_message "INFO" "Installing $pkg..."
+ if ! apt-get install -y "$pkg"; then
+ log_message "WARNING" "Failed to install $pkg, continuing with other packages..."
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
+ # Install specific PHP 8.3 packages
+ for pkg in php8.3-dom php8.3-simplexml php8.3-xmlwriter; do
+ if ! apt-get install -y "$pkg"; then
+ log_message "WARNING" "Failed to install $pkg, continuing with other packages..."
+ fi
+ done
- log_message "INFO" "PHP installation and configuration completed for all versions"
+ # Enable PHP module for Apache
+ a2enmod php8.3 || a2enmod php8.2 || a2enmod php8.1 || a2enmod php8.0 || a2enmod php7.4 || log_message "WARNING" "Failed to enable PHP module"
+
+ # Restart Apache
+ systemctl restart apache2 || log_message "WARNING" "Failed to restart Apache"
+
+ log_message "INFO" "PHP installation and configuration completed"
}
# Function to install MySQL
install_mysql() {
log_message "INFO" "Checking MySQL installation..."
+ # Update package lists first
+ update_packages
+
if command_exists mysql; then
local mysql_version
mysql_version=$(mysql -V | grep -oP '\d+\.\d+\.\d+' | head -1)
@@ -499,6 +358,9 @@ install_mysql() {
install_nodejs() {
log_message "INFO" "Checking Node.js installation..."
+ # Update package lists first
+ update_packages
+
if command_exists node; then
local node_version
node_version=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
@@ -526,6 +388,9 @@ install_nodejs() {
install_apache() {
log_message "INFO" "Checking Apache installation..."
+ # Update package lists first
+ update_packages
+
if command_exists apache2; then
log_message "INFO" "Apache is installed"
else
@@ -630,9 +495,32 @@ get_domain() {
setup_domain() {
local domain="$1"
local domain_path="/var/www/html/$domain/public_html"
+ local config_file="/etc/apache2/sites-available/$domain.conf"
log_message "INFO" "Setting up domain: $domain"
+ # Remove existing virtual host if it exists
+ if [[ -f "$config_file" ]]; then
+ log_message "INFO" "Removing existing virtual host configuration..."
+ a2dissite "$domain.conf" >/dev/null 2>&1
+ rm -f "$config_file"
+ fi
+
+ # Check for existing domain directory
+ if [[ -d "/var/www/html/$domain" ]]; then
+ echo -e "\n${YELLOW}Warning: The directory /var/www/html/$domain already exists.${NC}"
+ echo -e "${YELLOW}All its contents will be deleted.${NC}"
+ read -p "Do you want to continue? (y/n) [n]: " response
+
+ if [[ ! "$response" =~ ^[Yy]$ ]]; then
+ log_message "ERROR" "User chose not to delete existing directory"
+ handle_error "Installation aborted by user"
+ fi
+
+ log_message "INFO" "Removing existing domain directory..."
+ rm -rf "/var/www/html/$domain"
+ fi
+
# Create domain directory
mkdir -p "$domain_path" || handle_error "Failed to create domain directory"
chown -R "$apache_user:$apache_user" "$domain_path"
@@ -642,7 +530,6 @@ setup_domain() {
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
ServerName $domain
@@ -670,51 +557,95 @@ EOF
# Function to setup database
setup_database() {
local domain="$1"
- local db_name="hesabix_$(echo "$domain" | tr '.-' '_')"
+ local base_db_name="hesabix_$(echo "$domain" | tr '.-' '_')"
+ local db_name="$base_db_name"
local db_user="hesabix_user"
local db_password
db_password=$(openssl rand -base64 12)
local domain_path="/var/www/html/$domain"
+ local counter=1
- log_message "INFO" "Setting up database: $db_name"
+ log_message "INFO" "Setting up database..."
# Verify MySQL is running
if ! systemctl is-active --quiet mysql; then
handle_error "MySQL service is not running"
fi
+ # Check if database exists and create new name if needed
+ while mysql -e "SHOW DATABASES LIKE '$db_name'" | grep -q "$db_name"; do
+ db_name="${base_db_name}_${counter}"
+ counter=$((counter + 1))
+ done
+
+ if [[ "$db_name" != "$base_db_name" ]]; then
+ log_message "WARNING" "Database $base_db_name already exists, using $db_name instead"
+ fi
+
+ # Drop existing user if exists
+ mysql -e "DROP USER IF EXISTS '$db_user'@'localhost';" || \
+ log_message "WARNING" "Failed to drop existing user"
+
# 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';" || \
+ # Create user with proper permissions
+ mysql -e "CREATE USER '$db_user'@'localhost' IDENTIFIED BY '$db_password';" || \
handle_error "Failed to create database user"
- # Grant privileges
+ # Grant all privileges and make sure they are applied
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"
+ # Verify user can connect
+ if ! mysql -u"$db_user" -p"$db_password" -e "SELECT 1;" >/dev/null 2>&1; then
+ handle_error "Failed to verify database user access"
+ fi
+
+ # Import default database structure
+ log_message "INFO" "Importing default database structure..."
+ if [[ -f "$domain_path/hesabixBackup/databasefiles/hesabix-db-default.sql" ]]; then
+ mysql -u"$db_user" -p"$db_password" "$db_name" < "$domain_path/hesabixBackup/databasefiles/hesabix-db-default.sql" || \
+ log_message "WARNING" "Failed to import default database structure"
+ else
+ log_message "WARNING" "Default database structure file not found"
+ fi
+
# Update environment configuration
local env_file="$domain_path/hesabixCore/.env.local.php"
cat > "$env_file" << EOF
'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',
-];
+
+// This file was generated by running "composer dump-env dev"
+
+return array (
+ 'APP_ENV' => 'prod',
+ 'SYMFONY_DOTENV_PATH' => './.env',
+ '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"
+ # Update database schema
+ log_message "INFO" "Updating database schema..."
+ cd "$domain_path/hesabixCore" || handle_error "Failed to change to hesabixCore directory"
+
+ # Set environment variables for doctrine command
+ export DATABASE_URL="mysql://$db_user:$db_password@127.0.0.1:3306/$db_name?serverVersion=8.0.32&charset=utf8mb4"
+
+ php bin/console doctrine:schema:update --force || \
+ log_message "WARNING" "Failed to update database schema"
+
log_message "INFO" "Database setup completed"
}
@@ -755,18 +686,37 @@ install_software() {
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"
+ # Initialize git repository if not already initialized
+ if [[ ! -d ".git" ]]; then
+ log_message "INFO" "Initializing git repository in $domain_path..."
+ git init || handle_error "Failed to initialize git repository"
+ fi
- # Add remote repository
- git remote add origin https://github.com/morrning/hesabixCore.git || \
- handle_error "Failed to add remote repository"
+ # Check if remote origin exists
+ if ! git remote get-url origin >/dev/null 2>&1; then
+ # Add remote repository if it doesn't exist
+ git remote add origin https://github.com/morrning/hesabixCore.git || \
+ handle_error "Failed to add remote repository"
+ else
+ # Update remote URL if it exists
+ git remote set-url origin https://github.com/morrning/hesabixCore.git || \
+ handle_error "Failed to update remote repository"
+ fi
# 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"
+
+ # Check if master branch exists
+ if git show-ref --verify --quiet refs/heads/master; then
+ # Switch to master branch
+ git checkout master || handle_error "Failed to checkout master branch"
+ # Pull latest changes
+ git pull origin master || handle_error "Failed to pull latest changes"
+ else
+ # Create and checkout master branch
+ git checkout -b master origin/master || handle_error "Failed to checkout repository"
+ fi
# Verify repository path
if [[ ! -d "$domain_path" ]]; then
@@ -850,13 +800,108 @@ set_apache_ownership() {
log_message "INFO" "Apache ownership set"
}
+# Function to display GPL license
+display_gpl_license() {
+ echo -e "\n${BOLD}${BLUE}=================================================${NC}"
+ echo -e "${BOLD}${BLUE} GNU General Public License v3 ${NC}"
+ echo -e "${BOLD}${BLUE}=================================================${NC}"
+ echo -e "${YELLOW}This program is free software: you can redistribute it and/or modify${NC}"
+ echo -e "${YELLOW}it under the terms of the GNU General Public License as published by${NC}"
+ echo -e "${YELLOW}the Free Software Foundation, either version 3 of the License, or${NC}"
+ echo -e "${YELLOW}(at your option) any later version.${NC}"
+ echo -e "\n${YELLOW}This program is distributed in the hope that it will be useful,${NC}"
+ echo -e "${YELLOW}but WITHOUT ANY WARRANTY; without even the implied warranty of${NC}"
+ echo -e "${YELLOW}MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.${NC}"
+ echo -e "${YELLOW}See the GNU General Public License for more details.${NC}"
+ echo -e "${BOLD}${BLUE}=================================================${NC}\n"
+
+ read -p "Do you accept the terms of the GNU General Public License v3? (y/n) [y]: " response
+ if [[ ! "$response" =~ ^[Yy]$ ]] && [[ -n "$response" ]]; then
+ handle_error "License terms not accepted"
+ fi
+}
+
+# Function to confirm installation
+confirm_installation() {
+ echo -e "\n${BOLD}${BLUE}=================================================${NC}"
+ echo -e "${BOLD}${BLUE} Installation Confirmation ${NC}"
+ echo -e "${BOLD}${BLUE}=================================================${NC}"
+ echo -e "${YELLOW}This script will install:${NC}"
+ echo -e "• PHP and required extensions"
+ echo -e "• MySQL/MariaDB"
+ echo -e "• Apache"
+ echo -e "• Node.js"
+ echo -e "• Composer"
+ echo -e "• phpMyAdmin"
+ echo -e "• Hesabix Core"
+ echo -e "• Hesabix Web UI"
+ echo -e "\n${YELLOW}The installation will require approximately 2GB of disk space.${NC}"
+ echo -e "${BOLD}${BLUE}=================================================${NC}\n"
+
+ read -p "Do you want to continue with the installation? (y/n) [y]: " response
+ if [[ ! "$response" =~ ^[Yy]$ ]] && [[ -n "$response" ]]; then
+ handle_error "Installation cancelled by user"
+ fi
+}
+
+# Function to install phpMyAdmin
+install_phpmyadmin() {
+ log_message "INFO" "Installing phpMyAdmin..."
+
+ # Update package lists first
+ update_packages
+
+ # Install phpMyAdmin
+ apt-get install -y phpmyadmin || handle_error "Failed to install phpMyAdmin"
+
+ # Configure phpMyAdmin
+ log_message "INFO" "Configuring phpMyAdmin..."
+
+ # Create Apache configuration
+ cat > /etc/apache2/conf-available/phpmyadmin.conf << EOF
+Alias /phpmyadmin /usr/share/phpmyadmin
+
+ Options FollowSymLinks
+ DirectoryIndex index.php
+ AllowOverride All
+ Require all granted
+
+EOF
+
+ # Enable configuration
+ a2enconf phpmyadmin || handle_error "Failed to enable phpMyAdmin configuration"
+
+ # Restart Apache
+ systemctl restart apache2 || handle_error "Failed to restart Apache"
+
+ log_message "INFO" "phpMyAdmin installation completed"
+}
+
# Function to show installation summary
show_installation_summary() {
local domain="$1"
local domain_path="/var/www/html/$domain"
+ local missing_packages=()
+ local db_name="hesabix_$(echo "$domain" | tr '.-' '_')"
+ local db_user="hesabix_user"
+ local db_password
+
+ # Get database password from env file
+ if [[ -f "$domain_path/hesabixCore/.env.local.php" ]]; then
+ db_password=$(php -r "include '$domain_path/hesabixCore/.env.local.php'; echo \$env['DATABASE_URL']; echo PHP_EOL;" | grep -oP '(?<=://)[^:]+(?=:)')
+ fi
log_message "INFO" "Showing installation summary..."
+ # Check for missing packages
+ for version in $(get_installed_php_versions); do
+ for pkg in 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; do
+ if ! dpkg -l | grep -q "^ii $pkg "; then
+ missing_packages+=("$pkg")
+ fi
+ done
+ done
+
echo -e "\n${BOLD}${BLUE}=================================================${NC}"
echo -e "${BOLD}${BLUE} Hesabix Installation Summary ${NC}"
echo -e "${BOLD}${BLUE}=================================================${NC}"
@@ -866,11 +911,27 @@ show_installation_summary() {
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 "phpMyAdmin URL: https://$domain/phpmyadmin"
+
+ echo -e "\n${YELLOW}Database Information:${NC}"
+ echo -e "Database Name: $db_name"
+ echo -e "Database User: $db_user"
+ echo -e "Database Password: $db_password"
+ echo -e "Database Host: localhost"
+ echo -e "Database Port: 3306"
+
+ if [[ ${#missing_packages[@]} -gt 0 ]]; then
+ echo -e "\n${RED}Warning: The following packages were not installed:${NC}"
+ printf '%s\n' "${missing_packages[@]}"
+ echo -e "\n${YELLOW}Please install these packages manually using:${NC}"
+ echo -e "sudo apt-get install ${missing_packages[*]}"
+ fi
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 "3. Access phpMyAdmin: https://$domain/phpmyadmin"
+ echo -e "4. Register the first user (system administrator)"
echo -e "\n${YELLOW}Support:${NC}"
echo -e "• Developer: Babak Alizadeh (alizadeh.babak)"
@@ -880,6 +941,10 @@ show_installation_summary() {
echo -e "\n${GREEN}Installation completed successfully!${NC}"
echo -e "${BOLD}${BLUE}=================================================${NC}"
+
+ # Restart Apache at the end of installation
+ log_message "INFO" "Restarting Apache..."
+ systemctl restart apache2 || log_message "WARNING" "Failed to restart Apache"
}
# Function to display telemetry consent
@@ -908,6 +973,8 @@ main() {
init_logging
check_system_requirements
install_tools
+ display_gpl_license
+ confirm_installation
display_telemetry_consent
update_packages
install_php
@@ -915,6 +982,7 @@ main() {
install_nodejs
install_apache
install_composer
+ install_phpmyadmin
domain=$(get_domain)
setup_domain "$domain"