Эх сурвалжийг харах

v3.6.12 2025-06-26
092371dd6de1bd6032f2a194b99c5e52f1cded68

oubo 3 долоо хоног өмнө
parent
commit
6fb4aee926
5 өөрчлөгдсөн 810 нэмэгдсэн , 120 устгасан
  1. 5 1
      CHANGELOG.md
  2. 802 0
      bin/install.sh
  3. 1 0
      bin/utool.sh
  4. 2 2
      data/version
  5. 0 117
      sapi/web/upgrade.php

+ 5 - 1
CHANGELOG.md

@@ -75,4 +75,8 @@ REPLACE INTO `t_affair_system_column` (`column_id`, `column_pid`, `group_id`, `c
 
 ##### v3.6.11 2025-06-24
 
-1. 票据编号设置调整
+1. 票据编号设置调整
+
+##### v3.6.12 2025-06-26
+
+1. 修复utool.sh备份数据BUG

+ 802 - 0
bin/install.sh

@@ -0,0 +1,802 @@
+#!/usr/bin/env bash
+export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+
+export TMOUT=0
+current_dir=$(cd `dirname "${BASH_SOURCE}"` ; pwd)
+log_path="${current_dir}"/install.log
+function InstallLog(){
+    [[ $# -ne 2 ]] && return 0
+    case $1 in
+        -E|-e)
+            echo -e "$(date +%Y-%m-%d\ %H:%M:%S) [ERROR]  [${FUNCNAME[2]}]:  ${2}" >> "${log_path}"
+            echo -e "\033[1;31m$(date +%Y-%m-%d\ %H:%M:%S) [ERROR]  [${FUNCNAME[1]}]:  ${2}\033[0m"
+            ;;
+        -I|-i)
+            echo -e "$(date +%Y-%m-%d\ %H:%M:%S) [INFO]  [${FUNCNAME[2]}]:  ${2}" >> "${log_path}"
+            echo -e "\033[1;32m$(date +%Y-%m-%d\ %H:%M:%S) [INFO]  [${FUNCNAME[1]}]:  ${2}\033[0m"
+            ;;
+        -C|-c)
+            echo -e "$(date +%Y-%m-%d\ %H:%M:%S) [INPUT]  [${FUNCNAME[2]}]:  ${2}" >> "${log_path}"
+            echo -e "\033[1;34m$(date +%Y-%m-%d\ %H:%M:%S) [INPUT]  [${FUNCNAME[1]}]:  ${2}\033[0m"
+            ;;
+        -W|-w)
+            echo -e "$(date +%Y-%m-%d\ %H:%M:%S) [WARN]  [${FUNCNAME[2]}]:  ${2}"  >> "${log_path}"
+            echo -e "\033[1;33m$(date +%Y-%m-%d\ %H:%M:%S) [WARN]  [${FUNCNAME[1]}]:  ${2}\033[0m"
+            ;;
+        *)
+            echo -e "$(date +%Y-%m-%d\ %H:%M:%S)[Other]  [${FUNCNAME[2]}]:  ${2}"  >> "${log_path}"
+            echo -e "$(date +%Y-%m-%d\ %H:%M:%S)[Other]  [${FUNCNAME[1]}]:  ${2}"
+    esac
+}
+
+function CheckRunning() {
+    TMPFILE=/tmp/install.run
+    if [ -e $TMPFILE ]; then
+      InstallLog -e "Other instance is running!"
+      exit 0
+    else
+      touch $TMPFILE
+      chmod 600 $TMPFILE
+    fi
+    # shellcheck disable=SC2064
+    trap "rm -f ${TMPFILE}; exit" 0 1 2 3 15
+    if [ "$(id -u)" != "0" ]; then
+      InstallLog -e "Require user root to run the script."
+      exit 1
+    fi
+}
+CheckRunning
+Kill_PM()
+{
+    if ps aux | grep -E "yum|dnf" | grep -qv "grep"; then
+        kill -9 $(ps -ef|grep -E "yum|dnf"|grep -v grep|awk '{print $2}')
+        if [ -s /var/run/yum.pid ]; then
+            rm -f /var/run/yum.pid
+        fi
+    elif ps aux | grep -E "apt-get|dpkg|apt" | grep -qv "grep"; then
+        kill -9 $(ps -ef|grep -E "apt-get|apt|dpkg"|grep -v grep|awk '{print $2}')
+        if [[ -s /var/lib/dpkg/lock-frontend || -s /var/lib/dpkg/lock ]]; then
+            rm -f /var/lib/dpkg/lock-frontend
+            rm -f /var/lib/dpkg/lock
+            dpkg --configure -a
+        fi
+    fi
+}
+
+Press_Start()
+{
+    echo ""
+    InstallLog -i "Press any key to start...or Press Ctrl+c to cancel"
+    OLDCONFIG=`stty -g`
+    stty -icanon -echo min 1 time 0
+    dd count=1 2>/dev/null
+    stty ${OLDCONFIG}
+}
+
+Install_LSB()
+{
+    echo "[+] Installing lsb..."
+    if [ "$PM" == "yum" ]; then
+        yum -y install redhat-lsb
+    elif [ "$PM" == "apt" ]; then
+        apt-get update
+        apt-get --no-install-recommends install -y lsb-release
+    fi
+}
+Get_Dist_Version()
+{
+    if command -v lsb_release >/dev/null 2>&1; then
+        DISTRO_Version=$(lsb_release -sr)
+    elif [ -f /etc/lsb-release ]; then
+        . /etc/lsb-release
+        DISTRO_Version="$DISTRIB_RELEASE"
+    elif [ -f /etc/os-release ]; then
+        . /etc/os-release
+        DISTRO_Version="$VERSION_ID"
+    fi
+    if [[ "${DISTRO}" = "" || "${DISTRO_Version}" = "" ]]; then
+        if command -v python2 >/dev/null 2>&1; then
+            DISTRO_Version=$(python2 -c 'import platform; print platform.linux_distribution()[1]')
+        elif command -v python3 >/dev/null 2>&1; then
+            DISTRO_Version=$(python3 -c 'import platform; print(platform.linux_distribution()[1])')
+        else
+            Install_LSB
+            DISTRO_Version=`lsb_release -rs`
+        fi
+    fi
+    printf -v "${DISTRO}_Version" '%s' "${DISTRO_Version}"
+}
+
+Get_Dist_Name()
+{
+    if grep -Eqi "Alibaba" /etc/issue || grep -Eq "Alibaba Cloud Linux" /etc/*-release; then
+        DISTRO='Alibaba'
+        PM='yum'
+    elif grep -Eqi "Aliyun" /etc/issue || grep -Eq "Aliyun Linux" /etc/*-release; then
+        DISTRO='Aliyun'
+        PM='yum'
+    elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eq "Amazon Linux" /etc/*-release; then
+        DISTRO='Amazon'
+        PM='yum'
+    elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
+        DISTRO='Fedora'
+        PM='yum'
+    elif grep -Eqi "Oracle Linux" /etc/issue || grep -Eq "Oracle Linux" /etc/*-release; then
+        DISTRO='Oracle'
+        PM='yum'
+    elif grep -Eqi "rockylinux" /etc/issue || grep -Eq "Rocky Linux" /etc/*-release; then
+        DISTRO='Rocky'
+        PM='yum'
+    elif grep -Eqi "almalinux" /etc/issue || grep -Eq "AlmaLinux" /etc/*-release; then
+        DISTRO='Alma'
+        PM='yum'
+    elif grep -Eqi "openEuler" /etc/issue || grep -Eq "openEuler" /etc/*-release; then
+        DISTRO='openEuler'
+        PM='yum'
+    elif grep -Eqi "Anolis OS" /etc/issue || grep -Eq "Anolis OS" /etc/*-release; then
+        DISTRO='Anolis'
+        PM='yum'
+    elif grep -Eqi "Kylin Linux Advanced Server" /etc/issue || grep -Eq "Kylin Linux Advanced Server" /etc/*-release; then
+        DISTRO='Kylin'
+        PM='yum'
+    elif grep -Eqi "OpenCloudOS" /etc/issue || grep -Eq "OpenCloudOS" /etc/*-release; then
+        DISTRO='OpenCloudOS'
+        PM='yum'
+    elif grep -Eqi "Huawei Cloud EulerOS" /etc/issue || grep -Eq "Huawei Cloud EulerOS" /etc/*-release; then
+        DISTRO='HCE'
+        PM='yum'
+    elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
+        DISTRO='CentOS'
+        PM='yum'
+        if grep -Eq "CentOS Stream" /etc/*-release; then
+            isCentosStream='y'
+        fi
+    elif grep -Eqi "Red Hat Enterprise Linux" /etc/issue || grep -Eq "Red Hat Enterprise Linux" /etc/*-release; then
+        DISTRO='RHEL'
+        PM='yum'
+    elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
+        DISTRO='Ubuntu'
+        PM='apt'
+    elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
+        DISTRO='Raspbian'
+        PM='apt'
+    elif grep -Eqi "Deepin" /etc/issue || grep -Eq "Deepin" /etc/*-release; then
+        DISTRO='Deepin'
+        PM='apt'
+    elif grep -Eqi "Mint" /etc/issue || grep -Eq "Mint" /etc/*-release; then
+        DISTRO='Mint'
+        PM='apt'
+    elif grep -Eqi "Kali" /etc/issue || grep -Eq "Kali" /etc/*-release; then
+        DISTRO='Kali'
+        PM='apt'
+    elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
+        DISTRO='Debian'
+        PM='apt'
+    elif grep -Eqi "UnionTech OS|UOS" /etc/issue || grep -Eq "UnionTech OS|UOS" /etc/*-release; then
+        DISTRO='UOS'
+        if command -v apt >/dev/null 2>&1; then
+            PM='apt'
+        elif command -v yum >/dev/null 2>&1; then
+            PM='yum'
+        fi
+    elif grep -Eqi "Kylin Linux Desktop" /etc/issue || grep -Eq "Kylin Linux Desktop" /etc/*-release; then
+        DISTRO='Kylin'
+        PM='apt'
+    else
+        DISTRO='unknow'
+    fi
+    Get_OS_Bit
+}
+
+Get_RHEL_Version()
+{
+    Get_Dist_Name
+    if [ "${DISTRO}" = "RHEL" ]; then
+        if grep -Eqi "release 5." /etc/redhat-release; then
+            echo "Current Version: RHEL Ver 5"
+            RHEL_Ver='5'
+        elif grep -Eqi "release 6." /etc/redhat-release; then
+            echo "Current Version: RHEL Ver 6"
+            RHEL_Ver='6'
+        elif grep -Eqi "release 7." /etc/redhat-release; then
+            echo "Current Version: RHEL Ver 7"
+            RHEL_Ver='7'
+        elif grep -Eqi "release 8." /etc/redhat-release; then
+            echo "Current Version: RHEL Ver 8"
+            RHEL_Ver='8'
+        elif grep -Eqi "release 9." /etc/redhat-release; then
+            echo "Current Version: RHEL Ver 9"
+            RHEL_Ver='9'
+        fi
+        RHEL_Version="$(cat /etc/redhat-release | sed 's/.*release\ //' | sed 's/\ .*//')"
+    fi
+}
+
+Get_OS_Bit()
+{
+    if [[ `getconf WORD_BIT` = '32' && `getconf LONG_BIT` = '64' ]] ; then
+        Is_64bit='y'
+        ARCH='x86_64'
+        DB_ARCH='x86_64'
+    else
+        Is_64bit='n'
+        ARCH='i386'
+        DB_ARCH='i686'
+    fi
+
+    if uname -m | grep -Eqi "arm|aarch64"; then
+        Is_ARM='y'
+        if uname -m | grep -Eqi "armv7|armv6"; then
+            ARCH='armhf'
+        elif uname -m | grep -Eqi "aarch64"; then
+            ARCH='aarch64'
+            DB_ARCH='aarch64'
+        else
+            ARCH='arm'
+        fi
+    fi
+}
+
+Get_Server_Type()
+{
+  if [[ $(hostname) =~ "iZbp" ]]
+  then
+      SERVER_TYPE="aliyun"
+  elif [[ $(hostname) =~ "lacew" ]]
+    then
+        SERVER_TYPE="aliyun"
+  elif [[ $(hostname) =~ "VM-" ]]
+  then
+      SERVER_TYPE="qcloud"
+  else
+      SERVER_TYPE="unknown"
+  fi
+}
+
+PressInstall() {
+  InstallLog -I "按任意键开始安装,按Ctrl+C中止安装"
+  local OLDCONFIG
+  OLDCONFIG=$(stty -g)
+  stty cbreak
+  #  dd if=/dev/tty bs=1 count=1 2> /dev/null
+  stty -raw
+  stty echo
+  #  stty -icanon -echo min 1 time 0
+  dd count=1 2>/dev/null
+  stty "${OLDCONFIG}"
+}
+#检测端口
+function CheckPort() {
+  local port
+  port=$1
+  InstallLog -I "检测端口 [$port]"
+  local checkResult
+  checkResult=$(netstat -tlpn | grep "\b$port\b")
+  if [ -n "$checkResult" ]; then
+    InstallLog -E "端口 $port 被占用"
+    exit 1
+  fi
+}
+function CheckEmpty() {
+  if [ -z "$1" ]; then
+    InstallLog -E "$2"
+    exit 1
+  fi
+}
+#检查API
+CheckAPI() {
+  # shellcheck disable=SC1083
+  local code
+  code=$(curl -I -m 5 -X POST -o /dev/null -s -w "%{http_code}" "$1")
+  if [ "${code}" != 200 ]; then
+    code=$(curl -I -m 5 -o /dev/null -s -w "%{http_code}" "$1&check")
+    if [ "${code}" != 200 ]; then
+      InstallLog -E "API $1 检查失败,返回代码 ${code}, 中止安装"
+      exit
+    else
+     InstallLog -I "API $1 检查通过"
+    fi
+  else
+     InstallLog -I "API $1 检查通过"
+  fi
+}
+#更新repo源
+function UpdateRepo() {
+  Get_Dist_Name
+  Get_Server_Type
+  local install=0
+  InstallLog -I "修改 ${DISTRO} 镜像源,服务器类型 ${SERVER_TYPE}"
+  if [ "${DISTRO}" == "Debian" ]; then
+    #阿里云
+    if [[ ${SERVER_TYPE} == "aliyun" ]]; then
+        if [ -f /etc/apt/sources.list ] && grep -Eqi "mirrors.cloud.aliyuncs.com" /etc/apt/sources.list; then
+          InstallLog -i "Aliyun apt repo already installed, skip..."
+        else
+          InstallLog -i "Change /etc/apt/sources.list to mirrors.cloud.aliyuncs.com"
+          sed -i "s#deb https\?://.*/debian#deb http://mirrors.cloud.aliyuncs.com/debian#" /etc/apt/sources.list
+          sed -i "s/^deb-src/#deb-src/" /etc/apt/sources.list
+          apt update
+        fi
+    #腾讯云
+    elif [[ ${SERVER_TYPE} == "qcloud" ]]; then
+          if [ -f /etc/apt/sources.list ] && grep -Eqi "mirrors.tencentyun.com" /etc/apt/sources.list; then
+            InstallLog -i "Tencent apt repo already installed, skip..."
+          else
+            InstallLog -i "Change /etc/apt/sources.list to mirrors.tencentyun.com"
+            sed -i "s#deb https\?://.*/debian#deb http://mirrors.tencentyun.com/debian#" /etc/apt/sources.list
+            sed -i "s/^deb-src/#deb-src/" /etc/apt/sources.list
+            apt update
+          fi
+    fi
+  elif [ "${DISTRO}" == "Ubuntu" ]; then
+    #阿里云
+    if [[ ${SERVER_TYPE} == "aliyun" ]]; then
+        if [ -f /etc/apt/sources.list ] && grep -Eqi "mirrors.cloud.aliyuncs.com" /etc/apt/sources.list; then
+          InstallLog -i "Aliyun apt repo already installed, skip..."
+        else
+          InstallLog -i "Change /etc/apt/sources.list to mirrors.cloud.aliyuncs.com"
+          sed -i "s#deb http\?://.*/ubuntu/#deb http://mirrors.cloud.aliyuncs.com/ubuntu/#" /etc/apt/sources.list
+          sed -i "s/^deb-src/#deb-src/" /etc/apt/sources.list
+          apt update
+        fi
+    #腾讯云
+    elif [[ ${SERVER_TYPE} == "qcloud" ]]; then
+         if [ -f /etc/apt/sources.list ] && grep -Eqi "mirrors.tencentyun.com" /etc/apt/sources.list; then
+           InstallLog -i "Tencent apt repo already installed, skip..."
+         else
+           InstallLog -i "Change /etc/apt/sources.list to mirrors.tencentyun.com"
+           sed -i "s#deb https\?://.*/ubuntu#deb http://mirrors.tencentyun.com/ubuntu#" /etc/apt/sources.list
+           sed -i "s/^deb-src/#deb-src/" /etc/apt/sources.list
+           apt update
+         fi
+    fi
+  elif [ "${DISTRO}" == "CentOS" ]; then
+    #阿里云
+    if [[ ${SERVER_TYPE} == "aliyun" ]]; then
+        if [ -f /etc/yum.repos.d/CentOS-Base.repo ] && grep -Eqi "mirrors.cloud.aliyuncs.com" /etc/yum.repos.d/CentOS-Base.repo; then
+          InstallLog -i "Aliyun yum repo already installed, skip..."
+        else
+          # shellcheck disable=SC2004
+          install=$(($install + 1))
+          wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.cloud.aliyuncs.com/repo/Centos-7.repo
+        fi
+        if [ -f /etc/yum.repos.d/ius.repo ] && grep -Eqi "mirrors.cloud.aliyuncs.com" /etc/yum.repos.d/ius.repo; then
+          InstallLog -i "Aliyun ius repo already installed, skip..."
+        else
+          # shellcheck disable=SC2004
+          install=$(($install + 1))
+          yum install -y http://mirrors.cloud.aliyuncs.com/ius/ius-release-el7.rpm
+          sed -i 's|baseurl.*/ius|baseurl=http://mirrors.cloud.aliyuncs.com/ius|g' /etc/yum.repos.d/ius*
+          rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
+        fi
+        if [ -f /etc/yum.repos.d/epel.repo ] && grep -Eqi "mirrors.cloud.aliyuncs.com" /etc/yum.repos.d/epel.repo; then
+          InstallLog -i "Aliyun epel repo already installed, skip..."
+        else
+          # shellcheck disable=SC2004
+          install=$(($install + 1))
+          wget -O /etc/yum.repos.d/epel.repo http://mirrors.cloud.aliyuncs.com/repo/epel-7.repo
+          sed -i 's|#\?baseurl.*/epel|baseurl=http://mirrors.cloud.aliyuncs.com/epel|' /etc/yum.repos.d/epel*
+        fi
+    elif [[ ${SERVER_TYPE} == "qcloud" ]]; then
+    #腾讯云
+      if [ -f /etc/yum.repos.d/CentOS-Base.repo ] && grep -Eqi "mirrors.cloud.tencent.com" /etc/yum.repos.d/CentOS-Base.repo; then
+        InstallLog -i "Tencent yum repo already installed, skip..."
+      else
+        # shellcheck disable=SC2004
+        install=$(($install + 1))
+        wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.cloud.tencent.com/repo/centos7_base.repo
+      fi
+
+      if [ -f /etc/yum.repos.d/ius.repo ] && grep -Eqi "mirrors.cloud.tencent.com" /etc/yum.repos.d/ius.repo; then
+        InstallLog -i "Tencent ius repo already installed, skip..."
+      else
+        # shellcheck disable=SC2004
+        install=$(($install + 1))
+        yum install -y https://mirrors.cloud.tencent.com/ius/ius-release-el7.rpm
+        rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
+        sed -i 's|baseurl.*/ius|baseurl=http://mirrors.cloud.tencent.com/ius|g' /etc/yum.repos.d/ius*
+      fi
+      if [ -f /etc/yum.repos.d/epel.repo ] && grep -Eqi "mirrors.cloud.tencent.com" /etc/yum.repos.d/epel.repo; then
+        InstallLog -i "Tencent epel repo already installed, skip..."
+      else
+        # shellcheck disable=SC2004
+        install=$(($install + 1))
+        wget -O /etc/yum.repos.d/epel.repo http://mirrors.cloud.tencent.com/repo/epel-7.repo
+        sed -i 's|baseurl.*/epel|baseurl=http://mirrors.cloud.tencent.com/epel|g' /etc/yum.repos.d/epel*
+      fi
+    elif [[ ${SERVER_TYPE} == "unknown" ]]; then
+      InstallLog -e "服务器类型不支持"
+      exit
+    fi
+    if [ "$install" -gt 0 ]; then
+      if [ "$PM" == "yum" ];then
+        yum clean all
+        yum makecache
+      fi
+    fi
+  fi
+}
+function InstallNecessary() {
+  Get_Dist_Name
+  Get_Server_Type
+  InstallLog -I "安装必要支持软件"
+  ${PM} install -y ca-certificates &> /dev/null
+  if ! command -v wget >/dev/null; then
+    InstallLog -I "安装wget"
+    ${PM} install -y wget &> /dev/null
+  fi
+  if ! command -v curl >/dev/null; then
+    InstallLog -I "安装curl"
+    ${PM} install -y curl &> /dev/null
+  fi
+  if ! command -v netstat >/dev/null; then
+    InstallLog -I "安装net-tools"
+    ${PM} install -y net-tools &> /dev/null
+  fi
+  if ! command -v screen >/dev/null; then
+    InstallLog -I "安装screen"
+    ${PM} install -y screen &> /dev/null
+  fi
+  if ! command -v vim >/dev/null; then
+    InstallLog -I "安装vi"
+    ${PM} install -y vim &> /dev/null
+  fi
+  echo 'set mouse=""' >> ~/.vimrc
+  if ! command -v gpg >/dev/null; then
+    InstallLog -I "安装gnupg"
+    ${PM} install -y gnupg &> /dev/null
+  fi
+  if ! command -v git >/dev/null; then
+    InstallLog -I "安装git"
+    if [ "${DISTRO}" == "Debian" ] || [ "${DISTRO}" == "Ubuntu" ]; then
+      ${PM} install -y git &> /dev/null
+    elif [ "${DISTRO}" == "CentOS" ]; then
+      ${PM} install -y git236 &> /dev/null
+    fi
+  fi
+  if ! command -v rz >/dev/null; then
+    InstallLog -I "安装lrzsz"
+    ${PM} install -y lrzsz &> /dev/null
+  fi
+  if ! command -v docker >/dev/null; then
+    InstallLog -I "安装docker和docker-compose"
+    if [ "${DISTRO}" == "Debian" ]; then
+      if [[ ${SERVER_TYPE} == "aliyun" ]]; then
+         curl -fsSL http://mirrors.cloud.aliyuncs.com/docker-ce/linux/debian/gpg | gpg --yes --dearmor -o /usr/share/keyrings/aliyun-docker-ce-keyring.gpg
+         echo "deb [arch=amd64 signed-by=/usr/share/keyrings/aliyun-docker-ce-keyring.gpg] http://mirrors.cloud.aliyuncs.com/docker-ce/linux/debian/ \
+         $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/aliyun-docker-ce.list > /dev/null
+      elif [[ ${SERVER_TYPE} == "qcloud" ]]; then
+         curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/debian/gpg | gpg --yes --dearmor -o /usr/share/keyrings/aliyun-docker-ce-keyring.gpg
+         echo "deb [arch=amd64 signed-by=/usr/share/keyrings/aliyun-docker-ce-keyring.gpg] http://mirrors.aliyun.com/docker-ce/linux/debian/ \
+         $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/aliyun-docker-ce.list > /dev/null
+      elif [[ ${SERVER_TYPE} == "unknown" ]]; then
+        InstallLog -e "服务器类型不支持"
+        exit
+      fi
+      apt update
+      apt install -y docker-ce docker-ce-cli containerd.io
+    elif [ "${DISTRO}" == "Ubuntu" ]; then
+       if [[ ${SERVER_TYPE} == "aliyun" ]]; then
+         curl -fsSL http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu/gpg | gpg --yes --dearmor -o /usr/share/keyrings/aliyun-docker-ce-keyring.gpg
+         echo "deb [arch=amd64 signed-by=/usr/share/keyrings/aliyun-docker-ce-keyring.gpg] http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu/ \
+         $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/aliyun-docker-ce.list > /dev/null
+      elif [[ ${SERVER_TYPE} == "qcloud" ]]; then
+         curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --yes --dearmor -o /usr/share/keyrings/aliyun-docker-ce-keyring.gpg
+         echo "deb [arch=amd64 signed-by=/usr/share/keyrings/aliyun-docker-ce-keyring.gpg] http://mirrors.aliyun.com/docker-ce/linux/ubuntu/ \
+         $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/aliyun-docker-ce.list > /dev/null
+      elif [[ ${SERVER_TYPE} == "unknown" ]]; then
+        InstallLog -e "服务器类型不支持"
+        exit
+      fi
+      apt update
+      apt install -y docker-ce docker-ce-cli containerd.io
+    elif [ "${DISTRO}" == "CentOS" ]; then
+      yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
+      #yum list docker-ce --showduplicates | sort -r
+      yum -y install docker-ce-26.1.4-1.el7 &> /dev/null
+    fi
+    if [ ! -f /usr/bin/docker-compose ];then
+      wget -O /usr/bin/docker-compose http://mi.lacecdn.com/docker/docker-compose-linux-x86_64-v2.32.4
+      chmod +x /usr/bin/docker-compose
+    fi
+    systemctl enable docker
+    systemctl start docker
+    apt upgrade
+  fi
+}
+#克隆代码
+function CloneCode() {
+  InstallLog -I "克隆代码..."
+  if [ -d ${LACEMI_CODE_PROD_PATH} ]; then
+    InstallLog -E "安装目录 ${LACEMI_CODE_PROD_PATH} 已存在,中止安装..."
+    exit
+  fi
+  if [ -d /tmp/lacemi ]; then
+    rm -rf /tmp/lacemi
+  fi
+  git clone "${LACEMI_CODE_URL}" /tmp/lacemi
+  InstallLog -I "复制代码至目录 ${LACEMI_CODE_PROD_PATH} "
+  mv -f /tmp/lacemi "${LACEMI_CODE_PROD_PATH}"
+  chmod 777 -R "${LACEMI_CODE_PROD_PATH}/sapi/runtime"
+  chmod 777 -R "${LACEMI_CODE_PROD_PATH}/sapi/web"
+}
+function CloneShareCode() {
+  InstallLog -I "克隆共享版代码..."
+  if [ -d ${LACEMI_CODE_SHARE_PATH} ]; then
+    InstallLog -E "安装目录 ${LACEMI_CODE_SHARE_PATH} 已存在,中止安装..."
+    exit
+  fi
+  if [ -d /tmp/lacemi ]; then
+    rm -rf /tmp/lacemi
+  fi
+  git clone "${LACEMI_SHARE_CODE_URL}" /tmp/lacemi
+  InstallLog -I "复制代码至目录 ${LACEMI_CODE_SHARE_PATH} "
+  mv -f /tmp/lacemi "${LACEMI_CODE_SHARE_PATH}"
+  chmod 777 -R "${LACEMI_CODE_SHARE_PATH}/sapi/runtime"
+  chmod 777 -R "${LACEMI_CODE_SHARE_PATH}/sapi/web"
+}
+function SecuritySSHLogin()
+{
+  if [ -s /root/.ssh/authorized_keys ]; then
+    InstallLog -I "成功禁用密码登录"
+    chattr -i /etc/ssh/sshd_config
+    sed -i "s/#\+PasswordAuthentication \w\+/PasswordAuthentication no/g" /etc/ssh/sshd_config
+    systemctl restart sshd
+  else
+    InstallLog -W "由于未启用SSH公钥登录,不允许禁用密码登录"
+  fi
+  iFlag=$(lsattr /etc/ssh/sshd_config | cut -d "-" -f 5)
+  if [ "$iFlag" != "i" ]; then
+    chattr +i /etc/ssh/sshd_config
+  fi
+ iFlag=$(lsattr /root/.ssh/authorized_keys | cut -d "-" -f 5)
+  if [ "$iFlag" != "i" ]; then
+    chattr +i /root/.ssh/authorized_keys
+  fi
+  grep 'PasswordAuthentication ' /etc/ssh/sshd_config
+}
+#下载公钥并允许公钥登录
+function EnableSSHLogin() {
+  InstallLog -I "允许SSH登陆"
+  CheckEmpty "${LACEMI_KEY}" "安装密钥为空,安装中止"
+  local rsaPublicKey
+  rsaPublicKey=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-pub\",\"key\":\"${LACEMI_KEY}\"}")
+  InstallLog -E "$rsaPublicKey"
+#  rsaPublicKey=$(curl -s -H "Accept:text/html" "${getPubApi}")
+  if [ -z "${rsaPublicKey}" ]; then
+    InstallLog -E "无法获取公钥,安装中止"
+    exit 1
+  fi
+  # shellcheck disable=SC2046
+  if [ $(echo "${rsaPublicKey}" | grep -c 'ssh-rsa') -eq 0 ]; then
+    InstallLog -E "公钥格式不正确,安装中止"
+    exit 1
+  fi
+  local checkString="${rsaPublicKey:50:36}"
+  InstallLog -I "检查公钥字符串 ${checkString}"
+  # shellcheck disable=SC2046
+  if [ $(grep -c "${checkString}" /root/.ssh/authorized_keys) -gt 0 ]; then
+    InstallLog -W "公钥已存在,跳过"
+  else
+    InstallLog -I "公钥不存在,附加"
+    chattr -i /root/.ssh/authorized_keys
+    cat >>/root/.ssh/authorized_keys <<EOF
+${rsaPublicKey}
+EOF
+  fi
+}
+function InstallCrontabTask() {
+    if [ -f /var/spool/cron/root ]; then
+        sed -i "/systemctl restart docker/d" /var/spool/cron/root
+      fi
+      echo "0 4 * * * systemctl restart docker" >>/var/spool/cron/root
+}
+Get_Dist_Name
+if [ "${DISTRO}" = "unknow" ]; then
+    Echo_Red "Unable to get Linux distribution name, or do NOT support the current distribution."
+    exit 1
+fi
+
+################
+VERSION="v3.1.0"
+RELEASE="2025-03-18"
+SEED=$(date +%Y%m%d%H%M%S)
+LACEMI_API_VERSION="v32"
+LACEMI_METHOD="install"
+LACEMI_CODE_DEV_PATH="/home/lacemi_api_dev_${LACEMI_API_VERSION}"
+LACEMI_CODE_SHARE_PATH="/home/lacemi_api_share_${LACEMI_API_VERSION}"
+LACEMI_CODE_PROD_PATH="/home/lacemi_api_prod_${LACEMI_API_VERSION}"
+LACEMI_API="https://api.hlace.com/v1/install"
+LACEMI_SHARE_CODE_URL="https://gogs.hlace.com/centrenda/lacemi_share_${LACEMI_API_VERSION}.git"
+LACEMI_DEV_CODE_URL="https://gogs.hlace.com/centrenda/lacemi_dev_${LACEMI_API_VERSION}.git"
+LACEMI_CODE_URL="https://gogs.hlace.com/centrenda/lacemi_${LACEMI_API_VERSION}.git"
+LACEMI_QUIET="0"
+LACEMI_DOMAIN=""
+LACEMI_DOMAIN_NAME=""
+LACEMI_KEY=""
+################
+
+start_date=$(date +'%Y-%m-%d %H:%M:%S')
+step=0
+steps=12
+
+VERSION_POSTGRESQL="17.4.0"
+VERSION_MARIADB="11.6.2"
+VERSION_MONGODB="8.0.5"
+VERSION_NGINX="1.27.4"
+VERSION_REDIS="7.4.2"
+VERSION_PHP="8.1.31"
+
+
+Usage() {
+  cat <<EOF
+  $(basename "$0") [options]
+
+Usage:
+
+  -k, --key                         target key
+  -m, --method=[install|upgrade|append]    method to execute, options for install or upgrade, default to execute install
+
+  -H, --help                        show usage
+  -Q, --quiet                       execute with quiet mode without pressing key to autostart
+  -V, --version                     print version information
+
+EOF
+  # 短格式中,选项值为可选的选项,选项值只能紧接选项而不可使用任何符号将其他选项隔开;如-p80,不要写成-p
+  # 短格式中,选项值为必有的选项,选项值既可紧接选项也可以使用空格与选项隔开;如-i192.168.1.,也可写成-i 192.168.1.1
+  # 长格式中,选项值为可选的选项,选项值只能使用=号连接选项;如--port=,不可写成性--port80或--port
+  # 长格式中,选项值为必有的选项,选项值既可使用=号连接选项也可使用空格连接选项;如--ip=192.168.1.1,也可写成--ip 192.168.1.1
+  # 为简便起见,建议凡是短格式都使用“选项+选项值”的形式(-p80),凡是长格式都使用“选项+=+选项值”的形式(--port=)
+}
+main() {
+  echo ""
+
+  local optstring="k:m:H::Q::V::"
+  local optstringLong="key:,method:,help::,quiet::,version::"
+  local GETOPT_OUT
+  GETOPT_OUT=$(getopt --options $optstring --longoptions $optstringLong -- "$@")
+  local exitCode=$?
+  if [ $exitCode -ne 0 ]; then
+    Usage
+    exit 1
+  fi
+  eval set -- "$GETOPT_OUT"
+  while true; do
+      case "$1" in
+      -k | --key)
+        LACEMI_KEY="$2"
+        shift
+        ;;
+      -m | --method)
+        LACEMI_METHOD="$2"
+        shift
+        ;;
+      -H | --help)
+        Usage
+        exit
+        ;;
+      -Q | --quiet)
+        LACEMI_QUIET="1"
+        shift
+        ;;
+      -V | --version)
+        InstallLog -I "Version: ${VERSION}, Release: ${RELEASE}"
+        exit
+        ;;
+      --)
+        shift
+        break
+        ;;
+      *)
+        InstallLog -E "非法参数: $1"
+        Usage
+        ;;
+      esac
+      shift
+    done
+
+
+  UpdateRepo
+  InstallNecessary
+#  PullDockerImage
+#  PullCode
+  if [ ! -d /root/server_blocks ]; then
+    mkdir /root/server_blocks
+  fi
+  if [ "${LACEMI_METHOD}" == "install" ]; then
+    #全新安装
+    InstallLog -I "全新安装"
+    CheckAPI "${LACEMI_API}"
+    CheckEmpty "${LACEMI_KEY}" "安装密钥为空,请使用-k指定安装密钥"
+    LACEMI_DOMAIN=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-domain\",\"key\":\"${LACEMI_KEY}\"}")
+    LACEMI_DOMAIN_NAME=$(echo "${LACEMI_DOMAIN}" | cut -d \. -f 1)
+    InstallLog -I "获取域名:${LACEMI_DOMAIN}"
+    InstallLog -C "安装参数\n\n域名全称:${LACEMI_DOMAIN}\n域名前缀:${LACEMI_DOMAIN_NAME}\n安装密钥:${LACEMI_KEY}\n代码地址:${LACEMI_CODE_URL}\n安装目录:${LACEMI_CODE_PROD_PATH}\n当前版本:${LACEMI_API_VERSION}\n访问接口:${LACEMI_API}\n"
+    PressInstall
+    rm -rf /root/server_blocks/*
+    CloneCode
+    InstallLog -I "获取docker-compose.yml"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_DOCKER_COMPOSE_TEMPLATE\"}" -o /root/docker-compose.yml
+    InstallLog -I "获取local.lic"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_LOCAL_LICENSE\"}" -o "/root/local.lic"
+    docker-compose up -d
+    InstallLog -I "获取${LACEMI_DOMAIN_NAME}.conf"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_NGINX_TEMPLATE\"}" -o "/root/server_blocks/${LACEMI_DOMAIN_NAME}.conf"
+    InstallLog -I "获取default.conf"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_NGINX_DEFAULT_TEMPLATE\"}" -o /root/server_blocks/default.conf
+    InstallLog -I "获取授权文件"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-license\",\"key\":\"${LACEMI_KEY}\"}" -o "${LACEMI_DOMAIN_NAME}.lic"
+    runCmd=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_DOCKER_RUN_CMD\"}")
+    InstallLog -I "生成docker run命令: ${runCmd}"
+    if [[ -n $(docker ps -a -q -f "name=${LACEMI_DOMAIN_NAME}") ]];then
+      echo "Docker container ${LACEMI_DOMAIN_NAME} is running, aborting."
+    else
+      ${runCmd}
+    fi
+    docker restart root-nginx-1
+    sleep 5
+    docker exec "${LACEMI_DOMAIN_NAME}" /app/bin/utool.sh fI
+  elif [ "${LACEMI_METHOD}" == "append" ]; then
+    #增量安装
+    InstallLog -i "增量安装"
+    CheckAPI "${LACEMI_API}"
+    CheckEmpty "${LACEMI_KEY}" "安装密钥为空,请使用-k指定安装密钥"
+    LACEMI_DOMAIN=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-domain\",\"key\":\"${LACEMI_KEY}\"}")
+    LACEMI_DOMAIN_NAME=$(echo "${LACEMI_DOMAIN}" | cut -d \. -f 1)
+    InstallLog -I "获取域名:${LACEMI_DOMAIN}"
+    InstallLog -C "安装参数\n\n域名全称:${LACEMI_DOMAIN}\n域名前缀:${LACEMI_DOMAIN_NAME}\n安装密钥:${LACEMI_KEY}\n代码地址:${LACEMI_CODE_URL}\n安装目录:${LACEMI_CODE_PROD_PATH}\n当前版本:${LACEMI_API_VERSION}\n访问接口:${LACEMI_API}\n"
+    PressInstall
+    InstallLog -I "获取${LACEMI_DOMAIN_NAME}.conf"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_NGINX_TEMPLATE\"}" -o "/root/server_blocks/${LACEMI_DOMAIN_NAME}.conf"
+    InstallLog -I "获取授权文件"
+    curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-license\",\"key\":\"${LACEMI_KEY}\"}" -o "${LACEMI_DOMAIN_NAME}.lic"
+    runCmd=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_DOCKER_RUN_CMD\"}")
+    InstallLog -I "生成docker run命令: ${runCmd}"
+    if [[ -n $(docker ps -a -q -f "name=${LACEMI_DOMAIN_NAME}") ]];then
+      echo "Docker container ${LACEMI_DOMAIN_NAME} is running, aborting."
+    else
+      ${runCmd}
+    fi
+    docker restart root-nginx-1
+    sleep 5
+    docker exec "${LACEMI_DOMAIN_NAME}" /app/bin/utool.sh fI
+  elif [ "${LACEMI_METHOD}" == "share" ]; then
+      #增量安装
+      InstallLog -i "共享版"
+      if [ ! -d "${LACEMI_CODE_SHARE_PATH}" ];then
+        CloneShareCode
+      fi
+      CheckAPI "${LACEMI_API}"
+      CheckEmpty "${LACEMI_KEY}" "安装密钥为空,请使用-k指定安装密钥"
+      LACEMI_DOMAIN=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-domain\",\"key\":\"${LACEMI_KEY}\"}")
+      LACEMI_DOMAIN_NAME=$(echo "${LACEMI_DOMAIN}" | cut -d \. -f 1)
+      InstallLog -I "获取域名:${LACEMI_DOMAIN}"
+      InstallLog -C "安装参数\n\n域名全称:${LACEMI_DOMAIN}\n域名前缀:${LACEMI_DOMAIN_NAME}\n安装密钥:${LACEMI_KEY}\n代码地址:${LACEMI_SHARE_CODE_URL}\n安装目录:${LACEMI_CODE_SHARE_PATH}\n当前版本:${LACEMI_API_VERSION}\n访问接口:${LACEMI_API}\n"
+      PressInstall
+      InstallLog -I "获取${LACEMI_DOMAIN_NAME}.conf"
+      curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_NGINX_TEMPLATE\"}" -o "/root/server_blocks/${LACEMI_DOMAIN_NAME}.conf"
+      runCmd=$(curl -s -H "Accept:text/html" "${LACEMI_API}" -X POST -d "{\"op\": \"get-template\",\"key\":\"${LACEMI_KEY}\",\"data\":\"INSTALL_DOCKER_RUN_CMD\"}")
+      InstallLog -I "生成docker run命令: ${runCmd}"
+      if [[ -n $(docker ps -a -q -f "name=${LACEMI_DOMAIN_NAME}") ]];then
+        echo "Docker container ${LACEMI_DOMAIN_NAME} is running, aborting."
+      else
+        ${runCmd}
+      fi
+      docker restart root-nginx-1
+      sleep 5
+      docker exec "${LACEMI_DOMAIN_NAME}" /app/bin/utool.sh fI
+    fi
+#  EnableSSHLogin
+#  SecuritySSHLogin
+}
+
+# shellcheck disable=SC2068
+clear
+echo "+------------------------------------------------------------------------+"
+echo "|          LACEMI ${VERSION}(${RELEASE}) for ${DISTRO} Linux Server             |"
+echo "+------------------------------------------------------------------------+"
+echo "|          A tool to install lacemi on Linux                             |"
+echo "+------------------------------------------------------------------------+"
+echo "|          For more information please visit https://hlace.com           |"
+echo "+------------------------------------------------------------------------+"
+main "$@"

+ 1 - 0
bin/utool.sh

@@ -287,6 +287,7 @@ function bD() {
   fi
 
   tar -zcvPf "${baseDir}${DOMAIN_NAME}_${SEED}.tar.gz" "${backupDir}" --remove-files
+  rm -rf "${backupDir}"
   rm -rf "$(find /app/data/backup/ -name '*.tar.gz' -mtime 30)" #删除30天前的备份文件
   echo -e "Success BackupDB: ${baseDir}${DOMAIN_NAME}_${SEED}.tar.gz"
 }

+ 2 - 2
data/version

@@ -1,10 +1,10 @@
 [version]
-sapi=v3.6.9
+sapi=v3.6.12
 android=v3.5.2
 ios=v3.5.1
 pc=v3.6.3
 [app]
-sapi=v3.6.11.20250624
+sapi=v3.6.12.20250626
 android=v3.5.2.20250117
 ios=v3.5.1.20240715
 pc=v3.6.3.20250412

+ 0 - 117
sapi/web/upgrade.php

@@ -1,117 +0,0 @@
-<?php //000a0
-// /**
-//  * @link http://www.lacew.com/
-//  * @copyright Copyright (c) 2015 Centrenda IT LLC
-//  * @license http://www.lacew.com/license/
-//  */
-
-?>
-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
-hGrNNm27j6N7bUZjKBdSLuxl