#!/bin/bash ################################################################################ # Простая установка/обновление/удаление Odoo 19 на Debian 13 (Trixie) # - автоматический выбор свежего архива odoo_19.0+e.*.tar.gz # - распаковка clean-enterprise-19.0.zip (если есть) # - PostgreSQL 16 с принудительным созданием пользователя postgres и кластера # - wkhtmltopdf с libssl1.1 (совместимость с Trixie) # - venv, systemd, конфиг в /etc/odoo/ #------------------------------------------------------------------------------- # Сделайте скрипт исполняемым: chmod +x odoo19_install_debian13.sh # Использование: sudo ./odoo19_install_debian13.sh {install|update|uninstall} ################################################################################ set -e # ------------------------- ПЕРЕМЕННЫЕ ------------------------- OE_USER="odoo" OE_HOME="/opt/$OE_USER" OE_HOME_EXT="$OE_HOME/${OE_USER}-server" OE_VENV="$OE_HOME/venv" OE_CONFIG_DIR="/etc/$OE_USER" OE_CONFIG_FILE="$OE_CONFIG_DIR/${OE_USER}-server.conf" OE_SERVER="${OE_USER}-server" OE_VERSION="19.0" OE_PORT="8069" OE_SUPERADMIN="admin" GENERATE_RANDOM_PASSWORD="True" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ODOO_ARCHIVE=$(ls -t "$SCRIPT_DIR"/odoo_19.0+e.*.tar.gz 2>/dev/null | head -n1) CUSTOM_ARCHIVE=$(ls -t "$SCRIPT_DIR"/clean-enterprise-19.0.zip 2>/dev/null | head -n1) if [[ -z "$ODOO_ARCHIVE" ]]; then echo "ОШИБКА: Не найден архив odoo_19.0+e.*.tar.gz в $SCRIPT_DIR" exit 1 fi echo "Используется архив: $ODOO_ARCHIVE" if [[ -n "$CUSTOM_ARCHIVE" ]]; then echo "Найден архив кастомных модулей: $CUSTOM_ARCHIVE" fi WKHTMLTOX_URL="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" LIBSSL_URL="http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb" # ------------------------- ФУНКЦИИ ------------------------- log_info() { echo -e "\n[INFO] $*"; } log_error() { echo -e "\n[ERROR] $*" >&2; } check_root() { if [[ $EUID -ne 0 ]]; then log_error "Скрипт должен запускаться от root (sudo)." exit 1 fi } generate_password() { tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 20 | head -n 1 } install_wkhtmltopdf() { log_info "Установка wkhtmltopdf" wget -q "$LIBSSL_URL" -O /tmp/libssl1.1.deb dpkg -i /tmp/libssl1.1.deb || apt-get install -f -y >/dev/null 2>&1 rm -f /tmp/libssl1.1.deb wget -q "$WKHTMLTOX_URL" -O /tmp/wkhtmltox.deb dpkg -i /tmp/wkhtmltox.deb || apt-get install -f -y >/dev/null 2>&1 rm -f /tmp/wkhtmltox.deb ln -sf /usr/local/bin/wkhtmltopdf /usr/bin/wkhtmltopdf ln -sf /usr/local/bin/wkhtmltoimage /usr/bin/wkhtmltoimage log_info "wkhtmltopdf установлен" } create_odoo_user_and_dirs() { log_info "Создание пользователя $OE_USER и каталогов" adduser --system --quiet --shell=/bin/bash --home=$OE_HOME --gecos 'ODOO' --group $OE_USER adduser $OE_USER sudo mkdir -p /var/log/$OE_USER mkdir -p $OE_HOME_EXT mkdir -p $OE_HOME/custom-addons mkdir -p $OE_CONFIG_DIR } extract_odoo() { log_info "Распаковка $ODOO_ARCHIVE в $OE_HOME_EXT" rm -rf $OE_HOME_EXT/* tar -C $OE_HOME_EXT -xzf "$ODOO_ARCHIVE" --strip-components=1 if [[ ! -d "$OE_HOME_EXT/odoo" || ! -f "$OE_HOME_EXT/odoo/__main__.py" ]]; then log_error "Структура архива неверна: нет odoo/__main__.py" exit 1 fi } extract_custom() { if [[ -n "$CUSTOM_ARCHIVE" && -f "$CUSTOM_ARCHIVE" ]]; then log_info "Распаковка $CUSTOM_ARCHIVE в $OE_HOME/custom-addons" unzip -qo "$CUSTOM_ARCHIVE" -d $OE_HOME/custom-addons if [[ -d "$OE_HOME/custom-addons/clean-enterprise-19.0" ]]; then if [[ -d "$OE_HOME/custom-addons/clean-enterprise-19.0/clean_enterprise" ]]; then mv "$OE_HOME/custom-addons/clean-enterprise-19.0/clean_enterprise" "$OE_HOME/custom-addons/" fi rm -rf "$OE_HOME/custom-addons/clean-enterprise-19.0" fi else log_info "Файл $CUSTOM_ARCHIVE не найден, пропускаем." fi } setup_venv() { log_info "Создание venv в $OE_VENV" if [[ -d "$OE_VENV" ]]; then rm -rf $OE_VENV fi su - $OE_USER -c "python3 -m venv $OE_VENV" log_info "Установка зависимостей Python и odoo" su - $OE_USER -s /bin/bash < $OE_CONFIG_FILE < /etc/systemd/system/${OE_SERVER}.service </dev/null; then log_info "Создание системного пользователя postgres" adduser --system --quiet --shell=/bin/bash --home=/var/lib/postgresql --group postgres fi # Добавляем репозиторий PostgreSQL (ключ перезаписывается без подтверждения) curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/postgresql-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list apt-get update apt-get install -y postgresql-16 postgresql-client-16 postgresql-server-dev-16 # Проверяем, существует ли кластер 16 main if ! pg_lsclusters 2>/dev/null | grep -q "16 main"; then log_info "Кластер 16/main не найден, создаём вручную от пользователя postgres" su - postgres -c "pg_createcluster 16 main --start --locale C.UTF-8" || { log_error "Не удалось создать кластер. Попробуйте вручную: su - postgres -c 'pg_createcluster 16 main --start'" exit 1 } else log_info "Кластер 16/main уже существует, запускаем" pg_ctlcluster 16 main start || true fi # Создаём пользователя odoo в PostgreSQL su - postgres -c "createuser -s $OE_USER" 2>/dev/null || true # 3. wkhtmltopdf install_wkhtmltopdf # 4. Глобальные пакеты npm npm install -g less less-plugin-clean-css rtlcss # 5. Пользователь и каталоги create_odoo_user_and_dirs # 6. Распаковка архивов extract_odoo extract_custom # 7. Права chown -R $OE_USER:$OE_USER $OE_HOME chown -R $OE_USER:$OE_USER /var/log/$OE_USER # 8. Виртуальное окружение setup_venv # 9. Конфиг и сервис create_config create_service echo "-----------------------------------------------------------" echo "УСТАНОВКА ЗАВЕРШЕНА" echo "Веб-интерфейс: http://$(hostname -I | awk '{print $1}'):$OE_PORT" echo "Мастер-пароль: $OE_SUPERADMIN" echo "Конфиг: $OE_CONFIG_FILE" echo "Логи: /var/log/${OE_USER}/${OE_SERVER}.log" echo "Управление: systemctl {start|stop|restart|status} ${OE_SERVER}" echo "-----------------------------------------------------------" } do_update() { log_info "=== ОБНОВЛЕНИЕ Odoo $OE_VERSION ===" systemctl stop ${OE_SERVER} || true ODOO_ARCHIVE=$(ls -t "$SCRIPT_DIR"/odoo_19.0+e.*.tar.gz 2>/dev/null | head -n1) if [[ -z "$ODOO_ARCHIVE" ]]; then log_error "Архив не найден для обновления!" exit 1 fi CUSTOM_ARCHIVE=$(ls -t "$SCRIPT_DIR"/clean-enterprise-19.0.zip 2>/dev/null | head -n1) extract_odoo extract_custom chown -R $OE_USER:$OE_USER $OE_HOME su - $OE_USER -s /bin/bash </dev/null || true systemctl disable ${OE_SERVER} 2>/dev/null || true rm -f /etc/systemd/system/${OE_SERVER}.service systemctl daemon-reload rm -f $OE_CONFIG_FILE rmdir $OE_CONFIG_DIR 2>/dev/null || true rm -rf $OE_HOME rm -rf /var/log/$OE_USER userdel -r $OE_USER 2>/dev/null || true # Удаляем ключ репозитория, чтобы при повторной установке не было запроса rm -f /usr/share/keyrings/postgresql-archive-keyring.gpg read -p "Удалить пользователя PostgreSQL 'odoo' и его базы? (y/N): " del_pg if [[ "$del_pg" =~ ^[Yy]$ ]]; then if id "postgres" &>/dev/null; then dbs=$(sudo -Hiu postgres psql -tAc "SELECT datname FROM pg_database WHERE datdba = (SELECT oid FROM pg_roles WHERE rolname='$OE_USER')") for db in $dbs; do sudo -u postgres dropdb "$db" 2>/dev/null || true done sudo -u postgres dropuser --if-exists $OE_USER 2>/dev/null || true log_info "Пользователь PostgreSQL удалён" fi fi read -p "Удалить PostgreSQL полностью? (y/N): " del_pg_all if [[ "$del_pg_all" =~ ^[Yy]$ ]]; then apt-get purge -y postgresql-16 postgresql-client-16 postgresql-server-dev-16 rm -rf /var/lib/postgresql /var/log/postgresql userdel -r postgres 2>/dev/null || true groupdel postgres 2>/dev/null || true log_info "PostgreSQL удалён" fi log_info "Удаление завершено" } # ------------------------- MAIN ------------------------- check_root if [[ $# -eq 0 ]]; then echo "Использование: $0 {install|update|uninstall}" exit 1 fi case "$1" in install) do_install ;; update) do_update ;; uninstall) do_uninstall ;; *) echo "Неверный параметр: $1"; exit 1 ;; esac exit 0