Update to 7.7.0

Since version 7.7.0, we recommend yours update python to 3.12.

[+] Using nginx technology to load static files improves access speed
[+] Refactor homepage, website, FTP, and database using vue3
[+] Table loading changed to skeleton screen
[+] Add Website statistics-v2 professional plug-in
[+] Add Home page - top 5 resource occupancy
[+] Add protection for Files management (requires Tamper-proof for Enterprise 3.7)
[+] Website, FTP, Databases page add program status
[+] Add FTP log analysis (only supports Centos)
[+] Add password-free login to phpMyAdmin
[+] Add Proxy Project in Website (Supported when web service uses Nginx)
[+] Add WP Toolkit (Pro version only)
[+] Redesigned Docker module
[+] Add WP Toolkit Protection
[+] Add WP Toolkit Backup and Restore
[+] Add WP Toolkit Migrated
[+] Add WP Toolkit Clone site (supports new domain and subdomain)
[+] Add WP Toolkit Create site from backup of other panel
[+] Add WP Toolkit support for Cron automatic backup (only save Local disk)
[+] Add WP Toolkit operation log
[+] Add Integrity check for WP Toolkit
[+] Add WP Toolkit plug-in management and themes management

[*] Optimize phpMyAdmin formula access method
[*] Optimize Home page PHP display problem
[*] Optimize jump to the login interface after the login expires
[*] Optimize automatic renewal of SSL at some times
[*] Optimize Let's Encrypt to increase application success rate

[-] Fix Logs Audit cannot be opened
[-] Fix apache URL rewrite issue
[-] Fix phpmyadmin installation problem
[-] Fix the problem that some servers cannot install software
[-] Fix upload file error
[-] Fix left menu hiding problem
[-] Fix aaPanel Mobile QR code display problem
[-] Fix problem that third-party plug-ins are not displayed in the App Store
[-] Fix issue where the menu bar is blank when opening new tabs
[-] Fixed panel not being accessible in some cases
[-] Fix the issue where Curl warning caused the inability to apply for SSL
[-] Fix Quota issues for Website, FTP, Databases
[-] Fix file interface display problem on mobile terminal
This commit is contained in:
Jack
2024-07-19 11:25:10 +08:00
parent ed34994f45
commit ed55fa708d
1949 changed files with 329210 additions and 26644 deletions
+381
View File
@@ -0,0 +1,381 @@
#coding: utf-8
#-------------------------------------------------------------------
# aaPanel
#-------------------------------------------------------------------
# Copyright (c) 2015-2018 aaPanel(www.aapanel.com) All rights reserved.
#-------------------------------------------------------------------
# Author: hwliang <hwl@aapanel.com>
#-------------------------------------------------------------------
#------------------------------
# Apache管理模块
#------------------------------
import public,os,re,shutil,math,psutil,time
from json import loads
os.chdir("/www/server/panel")
class apache:
setupPath = '/www/server'
apachedefaultfile = "%s/apache/conf/extra/httpd-default.conf" % (setupPath)
apachempmfile = "%s/apache/conf/extra/httpd-mpm.conf" % (setupPath)
httpdconf = "%s/apache/conf/httpd.conf" % (setupPath)
def GetProcessCpuPercent(self,i,process_cpu):
try:
pp = psutil.Process(i)
if pp.name() not in process_cpu.keys():
process_cpu[pp.name()] = float(pp.cpu_percent(interval=0.1))
process_cpu[pp.name()] += float(pp.cpu_percent(interval=0.1))
except:
pass
def GetApacheStatus(self):
process_cpu = {}
apacheconf = "%s/apache/conf/httpd.conf" % (self.setupPath)
confcontent = public.readFile(apacheconf)
rep = "#Include conf/extra/httpd-info.conf"
if re.search(rep,confcontent):
confcontent = re.sub(rep,"Include conf/extra/httpd-info.conf",confcontent)
public.writeFile(apacheconf,confcontent)
public.serviceReload()
result = public.HttpGet('http://127.0.0.1/server-status?auto')
try:
workermen = int(public.ExecShell("ps aux|grep httpd|grep 'start'|awk '{memsum+=$6};END {print memsum}'")[0]) / 1024
except:
return public.return_msg_gettext(False,"Get worker RAM False")
for proc in psutil.process_iter():
if proc.name() == "httpd":
self.GetProcessCpuPercent(proc.pid,process_cpu)
time.sleep(0.5)
data = {}
# 计算启动时间
Uptime = re.search(r"ServerUptimeSeconds:\s+(.*)",result)
if not Uptime:
return public.return_msg_gettext(False, "Get worker Uptime False")
Uptime = int(Uptime.group(1))
min = Uptime / 60
hours = min / 60
days = math.floor(hours / 24)
hours = math.floor(hours - (days * 24))
min = math.floor(min - (days * 60 * 24) - (hours * 60))
#格式化重启时间
restarttime = re.search(r"RestartTime:\s+(.*)",result)
if not restarttime:
return public.return_msg_gettext(False, "Get worker Restart Time False")
restarttime = restarttime.group(1)
rep = r"\w+,\s([\w-]+)\s([\d\:]+)\s\w+"
date = re.search(rep,restarttime)
if not date:
return public.return_msg_gettext(False, "Get worker date False")
date = date.group(1)
timedetail = re.search(rep,restarttime)
if not timedetail:
return public.return_msg_gettext(False, "Get worker time detail False")
timedetail=timedetail.group(2)
monthen = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
n = 0
for m in monthen:
if m in date:
date = re.sub(m,str(n+1),date)
n+=1
date = date.split("-")
date = "%s-%s-%s" % (date[2],date[1],date[0])
reqpersec = re.search(r"ReqPerSec:\s+(.*)", result)
if not reqpersec:
return public.return_msg_gettext(False, "Get worker reqpersec False")
reqpersec = reqpersec.group(1)
if re.match(r"^\.", reqpersec):
reqpersec = "%s%s" % (0,reqpersec)
data["RestartTime"] = "%s %s" % (date,timedetail)
data["UpTime"] = "%s day %s hour %s minute" % (str(int(days)),str(int(hours)),str(int(min)))
total_acc = re.search(r"Total Accesses:\s+(\d+)",result)
if not total_acc:
return public.return_msg_gettext(False, "Get worker TotalAccesses False")
data["TotalAccesses"] = total_acc.group(1)
total_kb = re.search(r"Total kBytes:\s+(\d+)",result)
if not total_kb:
return public.return_msg_gettext(False, "Get worker TotalKBytes False")
data["TotalKBytes"] = total_kb.group(1)
data["ReqPerSec"] = round(float(reqpersec), 2)
busywork = re.search(r"BusyWorkers:\s+(\d+)",result)
if not busywork:
return public.return_msg_gettext(False, "Get worker BusyWorkers False")
data["BusyWorkers"] = busywork.group(1)
idlework = re.search(r"IdleWorkers:\s+(\d+)",result)
if not idlework:
return public.return_msg_gettext(False, "Get worker IdleWorkers False")
data["IdleWorkers"] = idlework.group(1)
data["workercpu"] = round(float(process_cpu["httpd"]),2)
data["workermem"] = "%s%s" % (int(workermen),"MB")
return data
def GetApacheValue(self):
apachedefaultcontent = public.readFile(self.apachedefaultfile)
apachempmcontent = public.readFile(self.apachempmfile)
if not "mpm_event_module" in apachempmcontent:
return public.returnMsg(False,"mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty")
apachempmcontent = re.search("\\<IfModule mpm_event_module\\>(\n|.)+?\\</IfModule\\>",apachempmcontent).group()
ps = ["%s%s" % (public.get_msg_gettext('Second'),public.get_msg_gettext('Request timeout')),
public.get_msg_gettext('Keep alive'),
"%s%s" % (public.get_msg_gettext('Second'),public.get_msg_gettext('Connection timeout')),
public.get_msg_gettext('Max keep-alive requests per connection')]
gets = ["Timeout","KeepAlive","KeepAliveTimeout","MaxKeepAliveRequests"]
if public.get_webserver() == 'apache':
shutil.copyfile(self.apachedefaultfile, '/tmp/apdefault_file_bk.conf')
shutil.copyfile(self.apachempmfile, '/tmp/apmpm_file_bk.conf')
conflist = []
n = 0
for i in gets:
rep = r"(%s)\s+(\w+)" % i
k = re.search(rep, apachedefaultcontent)
if not k:
return public.return_msg_gettext(False, "Get Key {} False",(i,))
k = k.group(1)
v = re.search(rep, apachedefaultcontent)
if not v:
return public.return_msg_gettext(False, "Get Value {} False",(v,))
v = v.group(2)
psstr = ps[n]
kv = {"name":k,"value":v,"ps":psstr}
conflist.append(kv)
n += 1
ps = [public.get_msg_gettext('Default processes'),
public.get_msg_gettext('Maximum number of idle threads'),
public.get_msg_gettext('Minimum number of idle threads available to handle request spikes'),
public.get_msg_gettext('Number of threads created by each child process'),
public.get_msg_gettext('Maximum number of connections that will be processed simultaneously'),
public.get_msg_gettext('Limit on the number of connections that an individual child server will handle during its life')]
gets = ["StartServers","MaxSpareThreads","MinSpareThreads","ThreadsPerChild","MaxRequestWorkers","MaxConnectionsPerChild"]
n = 0
for i in gets:
rep = r"(%s)\s+(\w+)" % i
k = re.search(rep, apachempmcontent)
if not k:
return public.return_msg_gettext(False, "Get Key {} False",(i,))
k = k.group(1)
v = re.search(rep, apachempmcontent)
if not v:
return public.return_msg_gettext(False, "Get Value {} False",(v,))
v = v.group(2)
psstr = ps[n]
kv = {"name": k, "value": v, "ps": psstr}
conflist.append(kv)
n += 1
return(conflist)
def SetApacheValue(self,get):
apachedefaultcontent = public.readFile(self.apachedefaultfile)
apachempmcontent = public.readFile(self.apachempmfile)
if not "mpm_event_module" in apachempmcontent:
return public.return_msg_gettext(False,"mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty")
conflist = []
getdict = get.get_items()
for i in getdict.keys():
if i != "__module__" and i != "__doc__" and i != "data" and i != "args" and i != "action":
getpost = {
"name": i,
"value": str(getdict[i])
}
conflist.append(getpost)
for c in conflist:
if c["name"] == "KeepAlive":
if not re.search("on|off", c["value"]):
return public.return_msg_gettext(False, 'Parameter ERROR!')
else:
print(c["value"])
if not re.search(r"\d+", c["value"]):
print(c["name"],c["value"])
return public.return_msg_gettext(False, 'Parameter ERROR!')
rep = r"%s\s+\w+" % c["name"]
if re.search(rep,apachedefaultcontent):
newconf = "%s %s" % (c["name"],c["value"])
apachedefaultcontent = re.sub(rep,newconf,apachedefaultcontent)
elif re.search(rep,apachempmcontent):
newconf = "%s\t\t\t%s" % (c["name"], c["value"])
apachempmcontent = re.sub(rep, newconf , apachempmcontent)
public.writeFile(self.apachedefaultfile,apachedefaultcontent)
public.writeFile(self.apachempmfile, apachempmcontent)
isError = public.checkWebConfig()
if (isError != True):
shutil.copyfile('/tmp/_file_bk.conf', self.apachedefaultfile)
shutil.copyfile('/tmp/proxyfile_bk.conf', self.apachempmfile)
return public.returnMsg(False, 'ERROR: %s<br><a style="color:red;">' % public.get_msg_gettext('Configuration ERROR') + isError.replace("\n",
'<br>') + '</a>')
public.serviceReload()
return public.return_msg_gettext(True, 'Setup successfully!')
def add_httpd_access_log_format(self,args):
'''
@name 添加httpd日志格式
@author zhwen<zhw@aapanel.com>
@param log_format 需要设置的日志格式["$server_name","$remote_addr","-"....]
@param log_format_name
@param act 操作方式 add/edit
'''
try:
log_format = loads(args.log_format)
data = """
#LOG_FORMAT_BEGIN_{n}
LogFormat '{c}' {n}
#LOG_FORMAT_END_{n}
""".format(n=args.log_format_name,c=' '.join(log_format))
data = data.replace('%{User-agent}i','"%{User-agent}i"')
data = data.replace('%{Referer}i', '"%{Referer}i"')
if args.act == 'edit':
self.del_httpd_access_log_format(args)
conf = public.readFile(self.httpdconf)
if not conf:
return public.return_msg_gettext(False,'Configuration file not exist')
reg = '<IfModule log_config_module>'
conf = re.sub(reg,'<IfModule log_config_module>'+data,conf)
public.writeFile(self.httpdconf,conf)
public.serviceReload()
return public.return_msg_gettext(True, 'Setup successfully!')
except:
return public.returnMsg(False, str(public.get_error_info()))
def del_httpd_access_log_format(self,args):
'''
@name 删除日志格式
@author zhwen<zhw@aapanel.com>
@param log_format_name
'''
conf = public.readFile(self.httpdconf)
if not conf:
return public.return_msg_gettext(False, 'Configuration file not exist')
reg = '\\s*#LOG_FORMAT_BEGIN_{n}(\n|.)+#LOG_FORMAT_END_{n}\n?'.format(n=args.log_format_name)
conf = re.sub(reg,'',conf)
self._del_format_log_of_website(args.log_format_name)
public.writeFile(self.httpdconf,conf)
public.serviceReload()
return public.return_msg_gettext(True, 'Setup successfully!')
def del_all_log_format(self,args):
all_format = self.get_httpd_access_log_format(args)
for i in all_format:
args.log_format_name = i
self.del_httpd_access_log_format(args)
def _del_format_log_of_website(self,log_format_name):
site_format_log_status = self._get_format_log_to_website(log_format_name)
try:
for s in site_format_log_status.keys():
if not site_format_log_status[s]:
continue
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(s)
format_exist_reg = r'CustomLog\s+"/www.*"\s+{}'.format(log_format_name)
conf = public.readFile(website_conf_file)
if not conf:continue
if not re.search(format_exist_reg,conf):continue
access_log = re.search(format_exist_reg,conf).group().split()
access_log = access_log[0] + ' ' +access_log[1] + 'combined'
conf = re.sub(format_exist_reg,access_log,conf)
public.writeFile(website_conf_file,conf)
return True
except:
return False
def get_httpd_access_log_format_parameter(self,args=None):
data = {
"%h":"Client's IP address",
"%r":"Request agreement",
"%t":"Request time",
"%>s":"http status code",
"%b":"Send data size",
"%{Referer}i":"http referer",
"%{User-agent}i":"http user agent",
"%{X-Forwarded-For}i":"The real ip of the client",
"%l":"Remote login name",
"%u":"Remote user",
"-":"-"
}
if hasattr(args,'log_format_name'):
site_list = self._get_format_log_to_website(args.log_format_name)
return {'site_list':site_list,'format_log':data}
else:
return data
def _process_log_format(self,tmp):
log_tips = self.get_httpd_access_log_format_parameter()
data = []
for t in tmp:
t = t.replace('\"','')
t = t.replace("'", "")
if t not in log_tips:
continue
data.append({t:log_tips[t]})
return data
def get_httpd_access_log_format(self,args=None):
try:
reg = "#LOG_FORMAT_BEGIN.*"
conf = public.readFile(self.httpdconf)
if not conf:
return public.return_msg_gettext(False, 'Configuration file not exist')
data = re.findall(reg,conf)
format_name = [i.split('LOG_FORMAT_BEGIN_')[-1] for i in data]
format_log = {}
for i in format_name:
format_reg = "#LOG_FORMAT_BEGIN_{n}(\n|.)+LogFormat\\s+\'(.*)\'\\s+{n}".format(n=i)
tmp = re.search(format_reg,conf)
if not tmp:
continue
tmp = tmp.groups()[1].split()
format_log[i] = self._process_log_format(tmp)
return format_log
except:
return public.returnMsg(False,public.get_error_info())
def set_httpd_format_log_to_website(self,args):
'''
@name 设置网站日志格式
@author zhwen<zhw@aapanel.com>
@param sites aaa.com,bbb.com
@param log_format_name
'''
# sites = args.sites.split(',')
sites = loads(args.sites)
try:
all_site = public.M('sites').field('name').select()
reg = r'CustomLog\s+"/www.*{}\s*'.format(args.log_format_name)
for site in all_site:
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(site['name'])
conf = public.readFile(website_conf_file)
if not conf:
return public.return_msg_gettext(False, 'Configuration file not exist')
format_exist_reg = r'(CustomLog\s+"/www.*\_log).*'
access_log = re.search(format_exist_reg, conf).groups()[0] + '" ' + args.log_format_name
if site['name'] not in sites and re.search(format_exist_reg,conf):
access_log = ' '.join(access_log.split()[:-1])
conf = re.sub(reg, access_log, conf)
public.writeFile(website_conf_file,conf)
continue
conf = re.sub(format_exist_reg,access_log,conf)
public.writeFile(website_conf_file,conf)
public.serviceReload()
return public.return_msg_gettext(True, 'Setup successfully!')
except:
return public.returnMsg(False, str(public.get_error_info()))
def _get_format_log_to_website(self,log_format_name):
tmp = public.M('sites').field('name').select()
reg = 'CustomLog.*{}'.format(log_format_name)
data = {}
for i in tmp:
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(i['name'])
conf = public.readFile(website_conf_file)
if not conf:
data[i['name']] = False
continue
if re.search(reg,conf):
data[i['name']] = True
else:
data[i['name']] = False
return data