#!/bin/bash
# ---------------------------------------------------------------------------
#  AIOhPanel server installer - Host Manager + Hosting Panel + Webmail
#  Author: MD OMAR FARUK | www.faruk.bd | Copyright (c) 2026
#
#  Run on a FRESH server as root:
#     cd /home && curl -o install-aiohpanel.sh https://aiohpanel.com/install.sh && bash install-aiohpanel.sh
#  or with the zip you downloaded from license.faruk.bd next to it:
#     bash install.sh ./AIOhPanel-1.0.0.zip
#
#  Supported: Ubuntu 22.04 / 24.04, Debian 12, AlmaLinux / Rocky 8 / 9.
#  What it does: installs Apache (web sites), nginx (the panel on 2082/2083/2086/2087),
#  PHP-FPM (several versions where available), MariaDB, BIND, Postfix + Dovecot,
#  Pure-FTPd, cron, acme.sh; creates the aiohpanel system user and sudo rule; copies the
#  panel to /usr/local/aiohpanel; writes a self-signed certificate; starts the services;
#  then tells you to open https://<ip>:2087 to finish in the browser.
# ---------------------------------------------------------------------------
set -euo pipefail

PANEL_DIR=/usr/local/aiohpanel
ETC_DIR=/etc/aiohpanel
LOG=/var/log/aiohpanel-install.log
ZIP="${1:-}"
DOWNLOAD_URL="${AIOHPANEL_ZIP_URL:-https://aiohpanel.com/download/latest.zip}"
HOSTNAME_FQDN="$(hostname -f 2>/dev/null || hostname)"
MAIN_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"

say()  { printf '\033[1;34m==>\033[0m %s\n' "$*" | tee -a "$LOG"; }
warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" | tee -a "$LOG"; }
die()  { printf '\033[1;31mERROR:\033[0m %s\n' "$*" | tee -a "$LOG"; exit 1; }

[ "$(id -u)" = "0" ] || die "Run this installer as root."
mkdir -p "$(dirname "$LOG")"; : > "$LOG"

. /etc/os-release
FAMILY=""
case "${ID:-}" in
  ubuntu|debian) FAMILY=debian ;;
  almalinux|rocky|centos|rhel|fedora) FAMILY=rhel ;;
  *) case "${ID_LIKE:-}" in *debian*|*ubuntu*) FAMILY=debian ;; *rhel*|*fedora*|*centos*) FAMILY=rhel ;; esac ;;
esac
[ -n "$FAMILY" ] || die "Unsupported OS: ${PRETTY_NAME:-unknown}. Use Ubuntu 22.04/24.04, Debian 12, AlmaLinux or Rocky 8/9."
say "Installing AIOhPanel on ${PRETTY_NAME} (${FAMILY}) - hostname ${HOSTNAME_FQDN}, IP ${MAIN_IP}"

if [ -d "$PANEL_DIR" ] && [ -f "$PANEL_DIR/installed.lock" ]; then
  die "AIOhPanel is already installed in $PANEL_DIR. Use the Host Manager -> Upgrade to Latest Version, or remove installed.lock to run the wizard again."
fi
for p in 2087 2222 8443 8090; do
  if ss -ltn 2>/dev/null | grep -q ":$p "; then die "Another control panel seems to be installed (port $p is in use). AIOhPanel needs a clean server."; fi
done

# ------------------------------------------------------------- packages
export DEBIAN_FRONTEND=noninteractive
if [ "$FAMILY" = debian ]; then
  say "Updating package lists"
  apt-get update -y >>"$LOG" 2>&1
  say "Installing web, database, DNS, mail, FTP and PHP packages (this takes a few minutes)"
  apt-get install -y --no-install-recommends \
    apache2 apache2-utils libapache2-mod-security2 nginx mariadb-server mariadb-client bind9 bind9utils dnsutils \
    postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd pure-ftpd \
    cron curl wget unzip zip git socat quota sudo ca-certificates openssl rsync tar gzip bzip2 \
    php php-cli php-fpm php-mysql php-curl php-json php-mbstring php-xml php-zip php-gd php-intl php-bcmath php-soap php-imap php-posix \
    fail2ban >>"$LOG" 2>&1 || warn "Some packages did not install - see $LOG"
  # more PHP versions through Ondrej's PPA on Ubuntu / sury on Debian (best effort)
  if [ "${ID}" = ubuntu ]; then
    apt-get install -y software-properties-common >>"$LOG" 2>&1 || true
    add-apt-repository -y ppa:ondrej/php >>"$LOG" 2>&1 || true
    apt-get update -y >>"$LOG" 2>&1 || true
    for v in 7.4 8.1 8.2 8.3; do
      apt-get install -y php${v}-fpm php${v}-mysql php${v}-curl php${v}-mbstring php${v}-xml php${v}-zip php${v}-gd php${v}-intl php${v}-bcmath >>"$LOG" 2>&1 || warn "PHP $v not available"
    done
  fi
  a2enmod rewrite proxy proxy_fcgi ssl headers expires suexec status setenvif >>"$LOG" 2>&1 || true
  APACHE_UNIT=apache2; APACHE_CONF_DIR=/etc/apache2; APACHE_USER=www-data
  NAMED_UNIT=bind9; NAMED_CONF=/etc/bind/named.conf.local; NAMED_GROUP=bind
  PHP_FPM_UNIT="php$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')-fpm"
  FPM_POOL_DIR="/etc/php/$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')/fpm/pool.d"
else
  say "Installing EPEL and Remi, then the packages"
  dnf install -y epel-release >>"$LOG" 2>&1 || yum install -y epel-release >>"$LOG" 2>&1 || true
  dnf install -y https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm >>"$LOG" 2>&1 || true
  dnf module reset -y php >>"$LOG" 2>&1 || true
  dnf module enable -y php:remi-8.3 >>"$LOG" 2>&1 || true
  dnf install -y httpd mod_ssl mod_security nginx mariadb-server mariadb bind bind-utils \
    postfix dovecot pure-ftpd cronie curl wget unzip zip git socat quota sudo openssl rsync tar gzip bzip2 \
    php php-cli php-fpm php-mysqlnd php-curl php-json php-mbstring php-xml php-zip php-gd php-intl php-bcmath php-soap php-process \
    fail2ban policycoreutils-python-utils >>"$LOG" 2>&1 || warn "Some packages did not install - see $LOG"
  for v in 74 81 82; do dnf install -y php${v}-php-fpm php${v}-php-mysqlnd php${v}-php-mbstring php${v}-php-xml php${v}-php-zip php${v}-php-gd >>"$LOG" 2>&1 || true; done
  APACHE_UNIT=httpd; APACHE_CONF_DIR=/etc/httpd; APACHE_USER=apache
  NAMED_UNIT=named; NAMED_CONF=/etc/named.conf; NAMED_GROUP=named
  PHP_FPM_UNIT=php-fpm; FPM_POOL_DIR=/etc/php-fpm.d
  setsebool -P httpd_can_network_connect 1 >>"$LOG" 2>&1 || true
fi

# ------------------------------------------------------------- the panel files
say "Placing the panel in $PANEL_DIR"
mkdir -p "$PANEL_DIR" "$ETC_DIR"/{apache,apache/custom,mail} /var/log/aiohpanel/domlogs
TMPD="$(mktemp -d)"
if [ -n "$ZIP" ] && [ -f "$ZIP" ]; then
  cp "$ZIP" "$TMPD/panel.zip"
elif [ -f "$(dirname "$0")/index.php" ] && [ -d "$(dirname "$0")/includes" ]; then
  say "Using the panel files next to this script"
  rsync -a --exclude data --exclude config.php --exclude installed.lock "$(dirname "$0")/" "$PANEL_DIR/"
else
  say "Downloading the panel from $DOWNLOAD_URL"
  curl -fsSL -o "$TMPD/panel.zip" "$DOWNLOAD_URL" || die "Could not download the panel. Put the zip next to this script: bash install.sh AIOhPanel-x.y.z.zip"
  # a zip starts with "PK"; anything else (a sign-in page) means the download needs your account
  [ "$(head -c 2 "$TMPD/panel.zip")" = "PK" ] || die "The download is not a zip (it needs your account). Download AIOhPanel-x.y.z.zip from your account on the License Center, put it next to this script and run: bash install.sh AIOhPanel-x.y.z.zip"
fi
if [ -f "$TMPD/panel.zip" ]; then
  unzip -q -o "$TMPD/panel.zip" -d "$TMPD/unz"
  SRC="$(dirname "$(find "$TMPD/unz" -name index.php -path '*includes*' -prune -o -name index.php -print | head -1)")"
  [ -f "$SRC/includes/init.php" ] || SRC="$(dirname "$(find "$TMPD/unz" -path '*/includes/init.php' | head -1)")/.."
  rsync -a --exclude data --exclude config.php --exclude installed.lock "$SRC/" "$PANEL_DIR/"
fi
rm -rf "$TMPD"
[ -f "$PANEL_DIR/includes/init.php" ] || die "The panel files are incomplete (includes/init.php missing)."
mkdir -p "$PANEL_DIR/data"/{logs,backups,tmp,cache}
chmod +x "$PANEL_DIR/bin/aiohpanel" 2>/dev/null || true
ln -sf "$PANEL_DIR/bin/aiohpanel" /usr/local/bin/aiohpanel

# ------------------------------------------------------------- users, sudo, noshell
id aiohpanel >/dev/null 2>&1 || useradd -r -d "$PANEL_DIR" -s /usr/sbin/nologin aiohpanel
printf '#!/bin/sh\necho "This account has no shell access. Use the hosting panel."\nexit 1\n' > "$PANEL_DIR/bin/noshell"; chmod 755 "$PANEL_DIR/bin/noshell"
grep -q "$PANEL_DIR/bin/noshell" /etc/shells || echo "$PANEL_DIR/bin/noshell" >> /etc/shells
cat > /etc/sudoers.d/aiohpanel <<'EOF'
# AIOhPanel: the panel's php-fpm pool runs as aiohpanel and manages the server through sudo
Defaults:aiohpanel !requiretty
aiohpanel ALL=(root) NOPASSWD: ALL
EOF
chmod 440 /etc/sudoers.d/aiohpanel
chown -R aiohpanel:aiohpanel "$PANEL_DIR"
chmod 750 "$PANEL_DIR/data"
chown -R root:"$NAMED_GROUP" "$ETC_DIR" 2>/dev/null || true
chmod 755 "$ETC_DIR"

# ------------------------------------------------------------- MariaDB
say "Configuring MariaDB"
systemctl enable --now mariadb >>"$LOG" 2>&1 || systemctl enable --now mysql >>"$LOG" 2>&1 || warn "MariaDB did not start"
DB_PASS="$(openssl rand -hex 16)"
ROOT_DB_PASS="$(openssl rand -hex 16)"
mysql -uroot <<EOF >>"$LOG" 2>&1 || warn "MariaDB setup needs attention (see $LOG)"
CREATE DATABASE IF NOT EXISTS aiohpanel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'aiohpanel'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON aiohpanel.* TO 'aiohpanel'@'localhost';
ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('${ROOT_DB_PASS}');
FLUSH PRIVILEGES;
EOF
cat > /root/.my.cnf <<EOF
[client]
user=root
password=${ROOT_DB_PASS}
EOF
chmod 600 /root/.my.cnf
printf '%s\n' "$DB_PASS" > "$ETC_DIR/db.pass"; chmod 600 "$ETC_DIR/db.pass"
printf '%s\n' "$ROOT_DB_PASS" > "$ETC_DIR/mysql-root.pass"; chmod 600 "$ETC_DIR/mysql-root.pass"

# ------------------------------------------------------------- config.php (the wizard still asks for the rest)
cat > "$PANEL_DIR/config.php" <<EOF
<?php
/** AIOhPanel configuration - written by install.sh on $(date '+%Y-%m-%d %H:%M') */
define('AP_DB_HOST', '127.0.0.1');
define('AP_DB_PORT', 3306);
define('AP_DB_NAME', 'aiohpanel');
define('AP_DB_USER', 'aiohpanel');
define('AP_DB_PASS', '${DB_PASS}');
define('AP_DB_PREFIX', 'ap_');
define('AP_DRIVER', 'linux');
define('AP_HOME_ROOT', '/home');
define('AP_SECRET', '$(openssl rand -hex 24)');
define('AP_LICENSE_CENTER', 'https://license.faruk.bd');
define('AP_LICENSE_PRODUCT', 'aiohpanel');
EOF
chown aiohpanel:aiohpanel "$PANEL_DIR/config.php"; chmod 640 "$PANEL_DIR/config.php"

# ------------------------------------------------------------- php-fpm pool for the panel
say "Configuring PHP-FPM for the panel"
mkdir -p "$FPM_POOL_DIR" /run/php
cat > "$FPM_POOL_DIR/aiohpanel.conf" <<EOF
[aiohpanel]
user = aiohpanel
group = aiohpanel
listen = /run/php/aiohpanel.sock
listen.owner = $( [ "$FAMILY" = debian ] && echo www-data || echo nginx )
listen.group = $( [ "$FAMILY" = debian ] && echo www-data || echo nginx )
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 4
php_admin_value[upload_max_filesize] = 2G
php_admin_value[post_max_size] = 2G
php_admin_value[memory_limit] = 512M
php_admin_value[max_execution_time] = 600
php_admin_value[error_log] = $PANEL_DIR/data/logs/php-errors.log
php_admin_flag[log_errors] = on
php_admin_flag[display_errors] = off
EOF
systemctl enable --now "$PHP_FPM_UNIT" >>"$LOG" 2>&1 || warn "php-fpm did not start"
systemctl restart "$PHP_FPM_UNIT" >>"$LOG" 2>&1 || true

# ------------------------------------------------------------- self-signed certificate for the panel ports
say "Creating the panel's certificate"
mkdir -p "$ETC_DIR/ssl"
openssl req -x509 -nodes -newkey rsa:2048 -days 3650 -subj "/CN=${HOSTNAME_FQDN}/O=AIOhPanel" \
  -keyout "$ETC_DIR/ssl/panel.key" -out "$ETC_DIR/ssl/panel.crt" >>"$LOG" 2>&1
chmod 600 "$ETC_DIR/ssl/panel.key"

# ------------------------------------------------------------- nginx: the four panel ports
say "Configuring nginx on ports 2082/2083 (hosting panel), 2086/2087 (Host Manager), 2095/2096 (webmail)"
NGX_DIR=/etc/nginx/conf.d
mkdir -p "$NGX_DIR"
[ -f /etc/nginx/sites-enabled/default ] && rm -f /etc/nginx/sites-enabled/default
face_block() { # $1 face $2 http port $3 https port
cat <<EOF
server {
    listen $2; listen [::]:$2;
    server_name _;
    return 301 https://\$host:$3\$request_uri;
}
server {
    listen $3 ssl http2; listen [::]:$3 ssl http2;
    server_name _;
    ssl_certificate     $ETC_DIR/ssl/panel.crt;
    ssl_certificate_key $ETC_DIR/ssl/panel.key;
    root $PANEL_DIR;
    index index.php;
    client_max_body_size 2G;
    location ~ ^/(includes|data|cron|bin|languages|api|manager/pages|hosting/pages|hosting/filemanager)/ { return 404; }
    location ~ ^/(config\.php|installed\.lock|db_update_lib\.php) { return 404; }
    location ~ ^/(assets|themes)/ { try_files \$uri =404; expires 7d; }
    location ~ ^/plugins/[^/]+/assets/ { try_files \$uri =404; expires 7d; }
    location / { try_files \$uri /index.php\$is_args\$args; }
    location ~ \.php$ {
        try_files \$uri /index.php =404;
        fastcgi_pass unix:/run/php/aiohpanel.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
        fastcgi_param HTTP_X_AIOHPANEL_FACE $1;
        fastcgi_param SERVER_PORT $3;
        fastcgi_param HTTPS on;
        fastcgi_read_timeout 600;
    }
}
EOF
}
{
  face_block hosting 2082 2083
  face_block manager 2086 2087
  face_block webmail 2095 2096
} > "$NGX_DIR/aiohpanel.conf"
# make sure nginx does not also grab port 80 (Apache serves the sites)
for f in /etc/nginx/sites-enabled/* /etc/nginx/conf.d/default.conf; do [ -f "$f" ] && grep -q "listen.*80" "$f" && mv "$f" "$f.disabled-by-aiohpanel"; done
nginx -t >>"$LOG" 2>&1 || warn "nginx configuration test failed - see $LOG"
systemctl enable --now nginx >>"$LOG" 2>&1 && systemctl restart nginx >>"$LOG" 2>&1 || warn "nginx did not start"

# ------------------------------------------------------------- Apache: include the panel's vhosts
say "Configuring Apache for the hosting accounts"
if [ -d "$APACHE_CONF_DIR/conf-available" ]; then APACHE_INC="$APACHE_CONF_DIR/conf-available/aiohpanel.conf"; else APACHE_INC="$APACHE_CONF_DIR/conf.d/aiohpanel.conf"; fi
cat > "$APACHE_INC" <<EOF
# AIOhPanel: every account's virtual hosts
IncludeOptional $ETC_DIR/apache/*.conf
<Location /server-status>
    SetHandler server-status
    Require ip 127.0.0.1 ::1
</Location>
ServerName ${HOSTNAME_FQDN}
EOF
[ -d "$APACHE_CONF_DIR/conf-available" ] && a2enconf aiohpanel >>"$LOG" 2>&1 || true
mkdir -p "$PANEL_DIR/public/suspended"
cat > "$PANEL_DIR/public/suspended/suspended.html" <<'EOF'
<!doctype html><html><head><meta charset="utf-8"><title>Account Suspended</title><style>body{font-family:Arial,sans-serif;background:#f4f4f4;margin:0;padding:60px 20px;text-align:center;color:#333}.box{max-width:560px;margin:0 auto;background:#fff;border:1px solid #ddd;border-radius:6px;padding:40px}h1{color:#c0392b;margin-top:0}</style></head><body><div class="box"><h1>Account Suspended</h1><p>This account has been suspended.</p><p>Either the domain has been overused, or the reseller ran out of resources.</p></div></body></html>
EOF
systemctl enable --now "$APACHE_UNIT" >>"$LOG" 2>&1 || warn "Apache did not start"
systemctl restart "$APACHE_UNIT" >>"$LOG" 2>&1 || warn "Apache restart failed - check 'apachectl configtest'"

# ------------------------------------------------------------- BIND
say "Configuring BIND"
touch "$ETC_DIR/named.zones"; chown root:"$NAMED_GROUP" "$ETC_DIR/named.zones"; chmod 644 "$ETC_DIR/named.zones"
grep -q "aiohpanel/named.zones" "$NAMED_CONF" 2>/dev/null || echo "include \"$ETC_DIR/named.zones\";" >> "$NAMED_CONF"
if [ "$FAMILY" = rhel ]; then
  sed -i 's/listen-on port 53 { 127.0.0.1; };/listen-on port 53 { any; };/; s/allow-query     { localhost; };/allow-query { any; };/' /etc/named.conf 2>/dev/null || true
  mkdir -p /var/named/aiohpanel; chown root:named /var/named/aiohpanel; chmod 750 /var/named/aiohpanel
else
  mkdir -p /var/cache/bind/aiohpanel; chown root:bind /var/cache/bind/aiohpanel; chmod 750 /var/cache/bind/aiohpanel
fi
systemctl enable --now "$NAMED_UNIT" >>"$LOG" 2>&1 || warn "BIND did not start"

# ------------------------------------------------------------- Postfix + Dovecot (virtual users from the panel's maps)
say "Configuring Postfix and Dovecot"
touch "$ETC_DIR"/mail/{vdomains,vmailbox,valias,passwd}
chown root:dovecot "$ETC_DIR/mail/passwd" 2>/dev/null || true; chmod 640 "$ETC_DIR/mail/passwd"
for m in vdomains vmailbox valias; do postmap "$ETC_DIR/mail/$m" >>"$LOG" 2>&1 || true; done
postconf -e "myhostname = ${HOSTNAME_FQDN}" \
  "virtual_mailbox_domains = hash:$ETC_DIR/mail/vdomains" \
  "virtual_mailbox_maps = hash:$ETC_DIR/mail/vmailbox" \
  "virtual_alias_maps = hash:$ETC_DIR/mail/valias" \
  "virtual_transport = lmtp:unix:private/dovecot-lmtp" \
  "smtpd_sasl_type = dovecot" "smtpd_sasl_path = private/auth" "smtpd_sasl_auth_enable = yes" \
  "smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination" \
  "smtpd_tls_cert_file = $ETC_DIR/ssl/panel.crt" "smtpd_tls_key_file = $ETC_DIR/ssl/panel.key" "smtpd_use_tls = yes" \
  "message_size_limit = 52428800" "inet_interfaces = all" "mynetworks = 127.0.0.0/8 [::1]/128" >>"$LOG" 2>&1 || warn "postconf failed"
postconf -M submission/inet="submission inet n - y - - smtpd -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes" >>"$LOG" 2>&1 || true
postconf -M smtps/inet="smtps inet n - y - - smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes" >>"$LOG" 2>&1 || true
mkdir -p /etc/dovecot/conf.d
cat > /etc/dovecot/conf.d/99-aiohpanel.conf <<EOF
# AIOhPanel: virtual users from $ETC_DIR/mail/passwd (SHA512-CRYPT), Maildir under each account's home
mail_location = maildir:%h
mail_privileged_group = mail
first_valid_uid = 1000
disable_plaintext_auth = no
auth_mechanisms = plain login
passdb { driver = passwd-file; args = scheme=SHA512-CRYPT username_format=%u $ETC_DIR/mail/passwd }
userdb  { driver = passwd-file; args = username_format=%u $ETC_DIR/mail/passwd }
service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600; user = postfix; group = postfix } }
service auth { unix_listener /var/spool/postfix/private/auth { mode = 0660; user = postfix; group = postfix } }
ssl = yes
ssl_cert = <$ETC_DIR/ssl/panel.crt
ssl_key = <$ETC_DIR/ssl/panel.key
protocols = imap pop3 lmtp
EOF
systemctl enable --now postfix dovecot >>"$LOG" 2>&1 || warn "Postfix / Dovecot did not start"
systemctl restart postfix dovecot >>"$LOG" 2>&1 || true

# ------------------------------------------------------------- Pure-FTPd (virtual users + PAM for the account users)
say "Configuring Pure-FTPd"
mkdir -p /etc/pure-ftpd
touch /etc/pure-ftpd/pureftpd.passwd; chmod 600 /etc/pure-ftpd/pureftpd.passwd
pure-pw mkdb /etc/pure-ftpd/pureftpd.pdb -f /etc/pure-ftpd/pureftpd.passwd >>"$LOG" 2>&1 || true
if [ -d /etc/pure-ftpd/conf ]; then
  echo yes > /etc/pure-ftpd/conf/ChrootEveryone; echo yes > /etc/pure-ftpd/conf/NoAnonymous
  echo "/etc/pure-ftpd/pureftpd.pdb" > /etc/pure-ftpd/conf/PureDB; echo yes > /etc/pure-ftpd/conf/PAMAuthentication
  echo "1" > /etc/pure-ftpd/conf/TLS; echo "30000 35000" > /etc/pure-ftpd/conf/PassivePortRange
  ln -sf /etc/pure-ftpd/conf/PureDB /etc/pure-ftpd/auth/50pure 2>/dev/null || true
  mkdir -p /etc/ssl/private; cat "$ETC_DIR/ssl/panel.key" "$ETC_DIR/ssl/panel.crt" > /etc/ssl/private/pure-ftpd.pem; chmod 600 /etc/ssl/private/pure-ftpd.pem
elif [ -f /etc/pure-ftpd/pure-ftpd.conf ]; then
  sed -i 's/^#\? *PureDB .*/PureDB \/etc\/pure-ftpd\/pureftpd.pdb/; s/^#\? *ChrootEveryone .*/ChrootEveryone yes/; s/^#\? *NoAnonymous .*/NoAnonymous yes/; s/^#\? *PassivePortRange .*/PassivePortRange 30000 35000/' /etc/pure-ftpd/pure-ftpd.conf
fi
systemctl enable --now pure-ftpd >>"$LOG" 2>&1 || warn "Pure-FTPd did not start"

# ------------------------------------------------------------- acme.sh for Auto SSL
say "Installing acme.sh for Auto SSL (Let's Encrypt)"
mkdir -p "$PANEL_DIR/acme/data"
if [ ! -f "$PANEL_DIR/acme/acme.sh" ]; then
  curl -fsSL https://raw.githubusercontent.com/acmesh-official/acme.sh/master/acme.sh -o "$PANEL_DIR/acme/acme.sh" >>"$LOG" 2>&1 && chmod +x "$PANEL_DIR/acme/acme.sh" || warn "acme.sh could not be downloaded - Auto SSL will use self-signed certificates until it is installed"
fi
"$PANEL_DIR/acme/acme.sh" --home "$PANEL_DIR/acme/data" --register-account -m "admin@${HOSTNAME_FQDN}" >>"$LOG" 2>&1 || true

# ------------------------------------------------------------- firewall
if command -v ufw >/dev/null 2>&1; then
  for p in 20 21 22 25 53 80 110 143 443 465 587 993 995 2082 2083 2086 2087 2095 2096; do ufw allow "$p"/tcp >>"$LOG" 2>&1 || true; done
  ufw allow 53/udp >>"$LOG" 2>&1 || true; ufw allow 30000:35000/tcp >>"$LOG" 2>&1 || true
elif command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active firewalld >/dev/null 2>&1; then
  for p in 20 21 22 25 53 80 110 143 443 465 587 993 995 2082 2083 2086 2087 2095 2096; do firewall-cmd --permanent --add-port="$p"/tcp >>"$LOG" 2>&1 || true; done
  firewall-cmd --permanent --add-port=53/udp --add-port=30000-35000/tcp >>"$LOG" 2>&1 || true; firewall-cmd --reload >>"$LOG" 2>&1 || true
fi

# ------------------------------------------------------------- cron + IP aliases at boot
say "Installing the panel's timer and boot script"
cat > /etc/systemd/system/aiohpanel-cron.service <<EOF
[Unit]
Description=AIOhPanel tasks
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/php $PANEL_DIR/cron/cron.php
EOF
cat > /etc/systemd/system/aiohpanel-cron.timer <<'EOF'
[Unit]
Description=AIOhPanel tasks every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
EOF
cat > /etc/systemd/system/aiohpanel.service <<EOF
[Unit]
Description=AIOhPanel (IP aliases and sanity at boot)
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c 'while read cidr dev; do [ -n "\$cidr" ] && ip addr add "\$cidr" dev "\$dev" 2>/dev/null; done < $ETC_DIR/ips.boot; exit 0'
[Install]
WantedBy=multi-user.target
EOF
touch "$ETC_DIR/ips.boot"
systemctl daemon-reload
systemctl enable --now aiohpanel-cron.timer aiohpanel.service >>"$LOG" 2>&1 || true

# ------------------------------------------------------------- first database update + done
say "Creating the panel tables"
sudo -u aiohpanel php "$PANEL_DIR/db_update.php" >>"$LOG" 2>&1 || php "$PANEL_DIR/db_update.php" >>"$LOG" 2>&1 || warn "db_update reported a problem - the wizard will try again"
# the wizard needs to store the MariaDB root password for the SQL functions
php -r "define('AP_ROOT','$PANEL_DIR'); define('AP_NO_SESSION',1); define('AP_INSTALLING',1); require AP_ROOT.'/includes/init.php'; set_setting('mysql_root_password', trim(file_get_contents('$ETC_DIR/mysql-root.pass'))); set_setting('hostname','${HOSTNAME_FQDN}'); set_setting('main_ip','${MAIN_IP}'); set_setting('shared_ip','${MAIN_IP}');" >>"$LOG" 2>&1 || true
chown -R aiohpanel:aiohpanel "$PANEL_DIR/data"

cat <<EOF

 ============================================================================
   AIOhPanel is installed.  Finish the setup in your browser:

      Host Manager (Host Manager):   https://${MAIN_IP}:2087/
      Hosting panel:        https://${MAIN_IP}:2083/

   The browser will warn about the self-signed certificate - that is expected
   until Auto SSL issues one for ${HOSTNAME_FQDN}.
   The wizard asks for the root password of the panel and your licence key
   from https://license.faruk.bd .

   Log: $LOG
 ============================================================================

EOF
