#!/bin/bash ################################################################################ # Простая установка/обновление/удаление Odoo 19 на Debian 13 (Trixie) # - автоматический выбор свежего архива odoo_19.0+e.*.tar.gz # - распаковка clean-enterprise-19.0.zip (с запросом при обновлении) # - PostgreSQL 16 с надёжным созданием/запуском кластера # - wkhtmltopdf (нативная сборка для Bookworm/Trixie) # - venv, systemd, конфиг в /etc/odoo/ # - обновление только баз, принадлежащих пользователю 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" UPDATE_MODE="False" # флаг для режима обновления 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) WKHTMLTOX_URL="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb" 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 # ------------------------- ФУНКЦИИ ------------------------- 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 "$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 # Если это обновление и папка уже существует, спрашиваем if [[ "$UPDATE_MODE" == "True" ]] && [[ -d "$OE_HOME/custom-addons/clean_enterprise" ]]; then read -p "Обновить кастомные модули (папка clean_enterprise уже существует)? (y/N): " confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then log_info "Пропускаем обновление кастомных модулей." return fi # Если подтвердили, удаляем папку rm -rf "$OE_HOME/custom-addons/clean_enterprise" fi 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 < /etc/apt/sources.list.d/pgdg.list # 1. Обновление системы apt-get update apt-get upgrade -y # 2. Базовые пакеты apt-get install -y git python3 python3-pip build-essential wget \ python3-dev python3-venv python3-wheel python3-cffi \ libssl-dev libffi-dev libxml2-dev libxslt1-dev \ libzip-dev libldap2-dev libsasl2-dev libpq-dev \ libjpeg-dev zlib1g-dev libpng-dev \ nodejs npm gdebi-core curl unzip # 3. PostgreSQL 16 (установка, кластер уже не требует добавления репозитория) log_info "Установка PostgreSQL 16" # Убедимся, что системный пользователь postgres существует if ! id "postgres" &>/dev/null; then log_info "Создание системного пользователя postgres" adduser --system --quiet --shell=/bin/bash --home=/var/lib/postgresql --group postgres fi apt-get install -y postgresql-16 postgresql-client-16 postgresql-server-dev-16 # Убедимся, что кластер существует и запущен if ! pg_isready -q; then log_info "PostgreSQL не отвечает, пробуем запустить кластер" if pg_ctlcluster 16 main start 2>/dev/null; then log_info "Кластер 16/main запущен" else log_info "Не удалось запустить кластер, пробуем создать" su - postgres -c "pg_createcluster 16 main --start --locale C.UTF-8" 2>/dev/null || { log_info "Создание не удалось, возможно кластер уже есть, пробуем запустить" pg_ctlcluster 16 main start || { log_error "Не удалось запустить или создать кластер" exit 1 } } fi else log_info "PostgreSQL уже работает" fi # Создаём пользователя odoo в PostgreSQL su - postgres -c "createuser -s $OE_USER" 2>/dev/null || true # 4. wkhtmltopdf install_wkhtmltopdf # 5. Глобальные пакеты npm npm install -g less less-plugin-clean-css rtlcss # 6. Пользователь и каталоги create_odoo_user_and_dirs # 7. Распаковка архивов extract_odoo extract_custom # 8. Права chown -R $OE_USER:$OE_USER $OE_HOME chown -R $OE_USER:$OE_USER /var/log/$OE_USER # 9. Виртуальное окружение setup_venv # 10. Конфиг и сервис 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 ===" UPDATE_MODE="True" 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 # 2. Удаление конфига и каталогов Odoo rm -f $OE_CONFIG_FILE rmdir $OE_CONFIG_DIR 2>/dev/null || true rm -rf $OE_HOME rm -rf /var/log/$OE_USER # 3. Удаление пользователя odoo userdel -r $OE_USER 2>/dev/null || true # 4. Удаление ключа репозитория PostgreSQL и файла источника rm -f /usr/share/keyrings/postgresql-archive-keyring.gpg rm -f /etc/apt/sources.list.d/pgdg.list # 5. Удаление wkhtmltopdf и симлинков log_info "Удаление wkhtmltopdf" apt-get purge -y wkhtmltox 2>/dev/null || true rm -f /usr/bin/wkhtmltopdf /usr/bin/wkhtmltoimage log_info "wkhtmltopdf и симлинки удалены" # 6. (Опционально) Удаление глобальных npm-пакетов # npm uninstall -g less less-plugin-clean-css rtlcss # 7. Удаление пользователя PostgreSQL и баз 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 # 8. Удаление PostgreSQL полностью (опционально) 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