Update to v7.65.0

This commit is contained in:
aapanel.com
2026-01-22 17:59:04 +08:00
parent a001604b69
commit 63124f6fa6
1201 changed files with 23139 additions and 7857 deletions
+102
View File
@@ -0,0 +1,102 @@
_BT_PYTHON_NAME_MAP_FILE="/www/server/panel/data/python_project_name2env.txt"
ACTIVATE_NAME="${1}"
if [ -z "${ACTIVATE_NAME}" ]; then
echo "Usage: \" source py-project-env <project_name> \" to activate virtual environment"
echo "Usage: \" bt_env_deactivate \" command to exit virtual environment and return to previous environment"
echo "(Only supported on Linux servers)"
return 1
fi
_bt_map_safe_get() {
awk -F: -v key="$1" '$1 == key {print $2; exit}' "${_BT_PYTHON_NAME_MAP_FILE}" 2>/dev/null
}
if [ -z "${_BT_PROJECT_ENV}" ]; then
_BT_PROJECT_ENV="$(_bt_map_safe_get ${ACTIVATE_NAME})"
else
ACTIVATE_NAME=$(basename "${_BT_PROJECT_ENV}")
fi
if [ ! -d "${_BT_PROJECT_ENV}" ]; then
echo "Virtual environment for project: ${ACTIVATE_NAME} does not exist"
return 1
fi
# This file must be used with "source bin/activate" *from bash*
# You cannot run it directly
bt_env_deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" ] || [ -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f bt_env_deactivate
fi
}
# unset irrelevant variables
bt_env_deactivate nondestructive
_OLD_VIRTUAL_PATH="$PATH"
if [ -d "${_BT_PROJECT_ENV}/bin" ] ; then
VIRTUAL_ENV="${_BT_PROJECT_ENV}"
PATH="$VIRTUAL_ENV/bin:$PATH"
else
VIRTUAL_ENV="${_BT_PROJECT_ENV}"
PATH="$VIRTUAL_ENV:$PATH"
fi
# use the path as-is
export VIRTUAL_ENV
export PATH
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
PS1="(${ACTIVATE_NAME}) ${PS1:-}"
export PS1
VIRTUAL_ENV_PROMPT="(${ACTIVATE_NAME}) "
export VIRTUAL_ENV_PROMPT
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" ] || [ -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
pyversion=${1}
py_path=/www/server/pyporject_evn/versions
py_cache=/www/server/pyporject_evn/versions/cached
cpuCore=$(cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l)
mkdir -p ${py_path}
download_Url='https://node.aapanel.com'
install_python() {
\cp ${py_cache}/Python-${pyversion}.tar.xz /tmp/Python-${pyversion}.tar.xz
cd /tmp/ && xz -d /tmp/Python-${pyversion}.tar.xz && tar -xvf /tmp/Python-${pyversion}.tar
cd /tmp/Python-${pyversion} || exit
if [ ${pyversion:0:1} -ge 2 ]; then
openssl111check=$(openssl version | grep 1.1.1)
if [ -z "${openssl111check}" ]; then
Install_Openssl111
WITH_SSL="--with-openssl=/usr/local/openssl111"
else
WITH_SSL=""
fi
cd /tmp/Python-${pyversion} || exit
./configure --prefix=${py_path}/${pyversion} ${WITH_SSL} -with-openssl-rpath=auto
make -j${cpuCore}
make install
rm -rf /tmp/Python-*
else
./configure --prefix=${py_path}/${pyversion}
make -j${cpuCore}
make install
rm -rf /tmp/Python-*
fi
}
Install_Openssl111() {
opensslCheck=$(/usr/local/openssl111/bin/openssl version | grep 1.1.1)
if [ -z "${opensslCheck}" ]; then
opensslVersion="1.1.1o"
cd /tmp/
wget ${download_Url}/src/openssl-${opensslVersion}.tar.gz
tar -zxf openssl-${opensslVersion}.tar.gz
rm -f openssl-${opensslVersion}.tar.gz
cd openssl-${opensslVersion} || exit
./config --prefix=/usr/local/openssl111 zlib-dynamic
make -j${cpuCore}
make install
echo "/usr/local/openssl111/lib" >>/etc/ld.so.conf.d/openssl111.conf
ldconfig
ldconfig /lib64
cd ..
rm -rf openssl-${opensslVersion}
fi
}
install_python
+58
View File
@@ -0,0 +1,58 @@
# coding: utf-8
import sys
import os
import traceback
import argparse
os.chdir('/www/server/panel/')
sys.path.insert(0, "/www/server/panel/class/")
sys.path.insert(0, "/www/server/panel/")
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
parser = argparse.ArgumentParser()
parser.add_argument("--task_type", type=str, required=True, help="Task type")
parser.add_argument("--task_id", type=int, required=True, help="Task ID")
parser.add_argument("--exclude_nodes", type=str, help="Exclude nodes")
args = parser.parse_args()
task_type, task_id = args.task_type, args.task_id
ex_ids = []
if args.exclude_nodes:
ex_ids = [int(i) for i in args.exclude_nodes.split(",")]
pid_file = "{}/logs/executor_log/{}_{}_0.pid".format(public.get_panel_path(), task_type, task_id)
with open(pid_file, "w") as f:
f.write(str(os.getpid()))
def cmd_task(main_id: int, log_id: int = 0):
from mod.project.node.task_flow.command_task import CMDTask
_task = CMDTask(main_id, log_id, print)
_task.start()
def file_task(main_id: int, exclude_nodes: list, log_id: int = 0):
from mod.project.node.task_flow.file_task import SelfFiletransferTask
_task = SelfFiletransferTask(main_id, exclude_nodes, log_id)
_task.start()
def flow_task(main_id: int):
from mod.project.node.task_flow.flow import FlowTask
_task = FlowTask(main_id)
_task.start()
try:
if task_type == "command":
cmd_task(task_id)
elif task_type == "file":
file_task(task_id, ex_ids)
elif task_type == "flow":
flow_task(task_id)
except:
traceback.print_exc()
with open('/tmp/node_flow_task.log', 'w') as f:
f.write(traceback.format_exc())
os.remove(pid_file)
+17
View File
@@ -0,0 +1,17 @@
# coding: utf-8
import sys
import os
import traceback
os.chdir('/www/server/panel/')
sys.path.insert(0, "/www/server/panel/class/")
sys.path.insert(0, "/www/server/panel/")
try:
from mod.project.node.filetransfer import run_file_transfer_task
run_file_transfer_task(int(sys.argv[1]))
except:
traceback.print_exc()
with open('/tmp/node_file_transfer.pl', 'w') as f:
f.write(traceback.format_exc())
+20
View File
@@ -0,0 +1,20 @@
# coding: utf-8
import sys
import os
import traceback
import time
os.chdir('/www/server/panel/')
sys.path.insert(0, "/www/server/panel/class/")
sys.path.insert(0, "/www/server/panel/")
try:
from mod.project.node.nodeutil import monitor_all_node_status
monitor_all_node_status()
except Exception as e:
traceback.print_exc()
with open('/tmp/node_monitor.pl', 'w') as f:
f.write(str(int(time.time())))
f.write("{}".format(traceback.format_exc()))
+35
View File
@@ -0,0 +1,35 @@
#!/www/server/panel/pyenv/bin/python
#coding: utf-8
import os,sys
os.chdir("/www/server/panel")
sys.path.insert(0,"class/")
from projectModel.nodejsModel import main
import public
p = main()
if len(sys.argv) < 3:
print("Usage: nodejs-service [project_name] [start|stop|restart]")
sys.exit()
get = public.dict_obj()
get.project_name = sys.argv[1].strip()
action = sys.argv[2].strip()
if action not in ['start','stop','restart','status']:
print("Usage: nodejs-service [project_name] [start|stop|restart]")
sys.exit()
if action == 'start':
res = p.start_project(get)
elif action == 'stop':
res = p.stop_project(get)
elif action == 'restart':
res = p.restart_project(get)
elif action == 'status':
res = p.get(get)
if res['status']:
print("\033[1;32mSUCCESS: " + res['data'] + "\033[0m")
else:
print("\033[1;31mERROR: " + res['error_msg'] + "\033[0m")
+34 -17
View File
@@ -13,7 +13,6 @@ import fcntl
os.chdir("/www/server/panel")
sys.path.insert(0, "class/")
sys.path.insert(0, "class_v2/")
from public import readFile
SETUP_PATH = "/www/server"
DATA_PATH = os.path.join(SETUP_PATH, "panel/data")
@@ -25,6 +24,21 @@ DAEMON_RESTART_RECORD = os.path.join(DATA_PATH, "daemon_restart_record.pl")
MANUAL_FLAG = os.path.join(SETUP_PATH, "panel/data/mod_push_data", "manual_flag.pl")
def read_file(filename: str):
fp = None
try:
fp = open(filename, "rb")
f_body_bytes: bytes = fp.read()
f_body = f_body_bytes.decode("utf-8", errors='ignore')
fp.close()
return f_body
except Exception:
return False
finally:
if fp and not fp.closed:
fp.close()
def run_command(cmd, timeout=5) -> str:
try:
result = subprocess.run(
@@ -213,7 +227,7 @@ SERVICES_MAP = {
f"{SETUP_PATH}/nginx/version.pl",
),
"openlitespeed": (
"litespeed", "/tmp/lshttpd/lsphp.sock", "/usr/local/lsws/bin/lswsctrl",
"litespeed", "/tmp/lshttpd/lshttpd.pid", "/usr/local/lsws/bin/lswsctrl",
"/usr/local/lsws/VERSION",
),
"redis": (
@@ -347,7 +361,12 @@ class ServicesHelper:
# 进程是否名字匹配
with open(f"/proc/{pid}/comm", "r") as f:
proc_name = f.read().strip()
return proc_name == self.nick_name or proc_name == self._serviced
# 特殊处理 mysql
if self.nick_name != "mysql":
return proc_name == self._serviced or proc_name == self.nick_name
else:
return proc_name in ["mysqld", "mariadbd"]
except (FileNotFoundError, IndexError):
return False
except Exception:
@@ -507,10 +526,12 @@ class ServicesHelper:
else:
cmd = [bash_path, act]
result = run_command(cmd)
if not result and act in ["start", "restart"]:
write_logs(f"Failed to {act} {self.nick_name}, error: command returned no output.")
subprocess.Popen(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True
)
except Exception as e:
print(str(e))
write_logs(f"Failed to {act} {self.nick_name}, error: {e}")
@@ -629,7 +650,7 @@ class DaemonManager:
def safe_read():
"""服务守护进程服务列表"""
try:
res = readFile(DAEMON_SERVICE)
res = read_file(DAEMON_SERVICE)
return json.loads(res) if res else []
except:
return []
@@ -639,7 +660,7 @@ class DaemonManager:
def manual_safe_read():
"""手动干预服务字典, 0: 需要干预, 1: 被手动关闭的"""
try:
manual = readFile(MANUAL_FLAG)
manual = read_file(MANUAL_FLAG)
return json.loads(manual) if manual else {}
except:
return {}
@@ -705,8 +726,8 @@ class RestartServices:
@DaemonManager.read_lock
def main(self):
manaul = readFile(MANUAL_FLAG)
services = readFile(DAEMON_SERVICE)
manaul = read_file(MANUAL_FLAG)
services = read_file(DAEMON_SERVICE)
try:
manual_info = json.loads(manaul) if manaul else {}
check_list = json.loads(services) if services else []
@@ -731,13 +752,9 @@ class RestartServices:
write_logs(f"Service [ {obj.nick_name} ] is Not Running, Try to start it...")
if not self._overhead(obj.nick_name):
obj.script("start")
time.sleep(3)
if not obj.is_running:
if not self._overhead(obj.nick_name):
obj.script("restart")
obj.script("restart")
if manual_info.get(obj.nick_name) == 1:
if obj.is_running and manual_info.get(obj.nick_name) == 1:
# service is running, fix the wrong flag
manual_info[obj.nick_name] = 0
# under lock file read lock
+171
View File
@@ -0,0 +1,171 @@
#!/usr/bin/python
# coding: utf-8
# -----------------------------
# Website run log split script
# -----------------------------
import sys
import os
import time
import glob
os.chdir("/www/server/panel")
if '/www/server/panel' not in sys.path:
sys.path.insert(0,'/www/server/panel')
if '/www/server/panel/class' not in sys.path:
sys.path.insert(0,'class/')
if '/www/server/panel/class_v2' not in sys.path:
sys.path.insert(0,'class_v2/')
import public, json
try:
from projectModelV2.pythonModel import main as pythonMod
from projectModelV2.nodejsModel import main as nodejsMod
mods = {
"python": pythonMod(),
"node": nodejsMod(),
}
except Exception as e:
print(str(e))
print("****** project split log task error ******")
print('==================================================================')
print('★[' + time.strftime("%Y/%m/%d %H:%M:%S") + '] split log task start ★')
print('==================================================================')
class LogSplit:
__slots__ = ("stype", "log_size", "limit", "_time", "compress", "exclude_sites")
@classmethod
def build_log_split(cls, name):
logsplit = cls()
path = '{}/data/run_log_split.conf'.format(public.get_panel_path())
data = {}
if os.path.exists(path):
try:
data = json.loads(public.readFile(path))
except:
public.ExecShell("rm -f {}".format(path))
return "file not found"
_clean(data)
public.writeFile(path, json.dumps(data))
target = data.get(name)
if not target :
return "file not found"
else:
for i in cls.__slots__:
if i in target:
setattr(logsplit, i, target[i])
logsplit._show()
return logsplit
def __init__(self, split_type: str = "day", limit: int = 180, log_size: int = 1024, compress: bool = False) -> None:
self.stype = split_type
self.log_size = log_size
self.limit = limit
self._time = time.strftime("%Y-%m-%d_%H%M%S")
self.compress = compress
self.exclude_sites = []
def _show(self):
if self.stype == "day":
print('|---Split method: Split 1 copy per day')
else:
print('|---Split method: Split by file size, split when file exceeds {}'.format(public.to_size(self.log_size)))
print('|---Currently keeping the latest [{}] copies'.format(self.limit))
def _to_zip(self, file_path):
os.system('gzip {}'.format(file_path))
def _del_surplus_log(self, history_log_path, log_prefix):
if not os.path.exists(history_log_path):
os.makedirs(history_log_path, mode=0o755)
logs = sorted(glob.glob(history_log_path + '/' + log_prefix + "*_log.*"))
count = len(logs)
if count >= self.limit:
for i in logs[:count - self.limit + 1]:
if os.path.exists(i):
os.remove(i)
print('|---Surplus log [' + i + '] has been deleted!')
def __call__(self, pjanme: str, sfile: str, log_prefix: str):
base_path, filename = sfile.rsplit("/", 1)
history_log_path = '{}/{}-history_logs'.format(base_path, pjanme)
if self.stype == 'size' and os.path.getsize(sfile) < self.log_size:
print('|---File size has not exceeded [{}], skipping!'.format(public.to_size(self.log_size)))
return
self._del_surplus_log(history_log_path, log_prefix)
if os.path.exists(sfile):
history_log_file = history_log_path + '/' + log_prefix + '_' + self._time + '_log.log'
if not os.path.exists(history_log_file):
with open(history_log_file, 'wb') as hf, open(sfile, 'r+b') as lf:
while True:
chunk_data = lf.read(1024*100)
if not chunk_data:
break
hf.write(chunk_data)
lf.seek(0)
lf.truncate()
if self.compress:
self._to_zip(history_log_file)
print('|---Log has been split to: ' + history_log_file + (".gz" if self.compress else ""))
else:
print('|---Target log file {} for project {} is missing, please note'.format(sfile, pjanme))
def main(name):
logsplit = LogSplit.build_log_split(name)
if logsplit=="file not found":
print(
"****** Detected panel project log split task configuration is empty,"
" please reset project log split task ******"
)
return
if not logsplit:
print("****** Panel project log split task configuration file is missing ******")
return
project = public.M('sites').where("project_type <> ? and name = ?", ("PHP", name)).find()
project['project_config'] = json.loads(project['project_config'])
for_split_func = getattr(mods.get(project["project_type"].lower()), "for_split")
if callable(for_split_func):
print('|---Starting to operate on {} project [{}] logs'.format(project["project_type"], project["name"]))
try:
for_split_func(logsplit, project)
print('|---Completed log split task for {} project [{}]'.format(project["project_type"], project["name"]))
except:
import traceback
print(traceback.format_exc())
print('|---Log split task error for {} project [{}]'.format(project["project_type"], project["name"]))
else:
print("****** Panel project log split task error ******")
print('================= All log split tasks completed ==================')
def _clean(data):
res = public.M('crontab').field('name').select()
del_config = []
for i in data.keys():
for j in res:
if j["name"].find(i) != -1 and j["name"].find("log split"):
break
else:
del_config.append(i)
for i in del_config:
del data[i]
if __name__ == '__main__':
if len(sys.argv) == 2:
name = sys.argv[1].strip()
main(name)
else:
print("****** Panel project log split task configuration parameter error ******")