Files
aaPanel/class/webserver.py
T
Jack ed55fa708d 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
2024-07-19 11:25:10 +08:00

430 lines
14 KiB
Python

# coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Linux面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2017 宝塔软件(http://bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: hwl@bt.cn
# +-------------------------------------------------------------------
# +--------------------------------------------------------------------
# | 前置web服务器控制器
# +--------------------------------------------------------------------
import os
from sys import path as sys_path
from platform import machine
# 设置运行目录
os.chdir('/www/server/panel')
# 添加自定义公共模块路径
if not 'class/' in sys_path:
sys_path.insert(0,'class/')
# 引入自定义公共模块
import public
class webserver:
def __init__(self):
'''
@name 初始化
'''
if not hasattr(public,'get_panel_path'):
self.__panel_path = '/www/server/panel'
else:
self.__panel_path = public.get_panel_path()
self.__webserver_bin = os.path.join(self.__panel_path,'webserver/sbin/webserver') # webserver二进制文件
self.__webserver_conf = os.path.join(self.__panel_path,'webserver/conf/webserver.conf') # webserver配置文件
self.__webserver_pid = os.path.join(self.__panel_path,'webserver/logs/webserver.pid') # webserver进程PID
self.__webserver_ctl = os.path.join(self.__panel_path,'script/webserver-ctl.sh') # webserver控制脚本
self.__panel_port_file = os.path.join(self.__panel_path, 'data/port.pl') # 面板端口文件
self.__ssl_key_file = os.path.join(self.__panel_path, 'ssl/privateKey.pem') # SSL私钥文件
self.__ssl_crt_file = os.path.join(self.__panel_path,'ssl/certificate.pem') # SSL证书文件
self.__is_ssl_file = os.path.join(self.__panel_path,'data/ssl.pl') # 是否开启SSL文件
self.__examples_path = os.path.join(self.__panel_path, 'webserver/tpls') # 配置文件模板目录
self.__webserver_conf_example = os.path.join(self.__examples_path ,'webserver.conf') # webserver配置文件模板
self.__webserver_ssl_conf_example = os.path.join(self.__examples_path ,'webserver_ssl.conf') # webserver SSL配置文件模板
self.__webserver_listen_conf_example = os.path.join(self.__examples_path ,'webserver_listen.conf') # webserver监听配置文件模板
self.__webserver_listen_ssl_conf_example = os.path.join(self.__examples_path ,'webserver_listen_ssl.conf') # webserver SSL监听配置文件模板
self.__default_port = 8888 # 默认端口
self.__log_error = 'ERROR'
self.__log_debug = 'DEBUG'
self.__log_info = 'INFO'
self.__log_warning = 'WARNING'
def print_log(self,msg,level='INFO'):
'''
@name 打印日志
@param msg str 日志内容
@param level str 日志级别
@return void
'''
if not hasattr(public,'print_log'):
print("[{}]".format(level),msg)
else:
public.print_log(msg,level)
def exec_shell(self,cmd):
'''
@name 执行shell命令
@param cmd str shell命令
@return tuple
'''
res = public.ExecShell(cmd)
if isinstance(res,tuple):
return res
else:
return ('',res)
# 获取系统架构是X86还是ARM
def get_machine(self):
arch = machine()
return arch
def bin_exists(self):
'''
@name 判断webserver二进制文件是否存在
@return bool
'''
return os.path.exists(self.__webserver_bin)
def conf_exists(self):
'''
@name 判断webserver配置文件是否存在
@return bool
'''
return os.path.exists(self.__webserver_conf)
def conf_example_exists(self):
'''
@name 判断webserver配置文件模板是否存在
@return bool
'''
return os.path.exists(self.__webserver_conf_example)
def pid_exists(self):
'''
@name 判断webserver进程PID是否存在
@return bool
'''
return os.path.exists(self.__webserver_pid)
def ctl_exists(self):
'''
@name 判断webserver-ctl.sh是否存在
@return bool
'''
return os.path.exists(self.__webserver_ctl)
def hasattr_public(self,defname):
'''
@name 判断public模块是否存在
@return bool
'''
if hasattr(public,defname):
return True
self.print_log('public.{} not found.'.format(defname),self.__log_error)
return False
def process_status(self):
'''
@name 判断webserver进程是否存在
@return bool
'''
if not self.pid_exists():
return False
res = self.exec_shell("bash {} status".format(self.__webserver_ctl))
if res[0].find('not running') != -1:
return False
return True
def start(self):
'''
@name 启动webserver
@return bool
'''
if self.process_status():
return False
res = self.exec_shell("bash {} start".format(self.__webserver_ctl))
if res[0].find('Failed') != -1:
self.print_log(res,self.__log_warning)
return False
return True
def stop(self):
'''
@name 停止webserver
@return bool
'''
if not self.process_status():
return False
res = self.exec_shell("bash {} stop".format(self.__webserver_ctl))
if res[0].find('Failed') != -1:
self.print_log(res,self.__log_warning)
return False
return True
def restart(self):
'''
@name 重启webserver
@return bool
'''
res = self.exec_shell("bash {} restart".format(self.__webserver_ctl))
if res[0].find('Failed') != -1:
self.print_log(res,self.__log_warning)
return False
return True
def reload(self):
'''
@name 重载webserver
@return bool
'''
res = self.exec_shell("bash {} reload".format(self.__webserver_ctl))
if res[0].find('Failed') != -1:
self.print_log(res,self.__log_warning)
return False
return True
def get_status(self):
'''
@name 获取webserver状态
@return bool
'''
return self.process_status()
def configtest(self):
'''
@name 测试配置文件是否正确
@return bool
'''
res = self.exec_shell("bash {} configtest".format(self.__webserver_ctl))
result = "\n".join(res)
if result.find('successful') != -1:
return True
self.print_log(result,self.__log_warning)
return False
def get_panel_port(self):
'''
@name 获取面板端口
@return int
'''
# 如果面板端口文件不存在,则使用默认端口
if not os.path.exists(self.__panel_port_file):
return self.__default_port
# 读取面板端口文件
port = public.ReadFile(self.__panel_port_file)
if not port:
return self.__default_port
try:
# 判断端口是否在1-65535之间
port = int(port)
if port < 1 or port > 65535:
return self.__default_port
except:
return self.__default_port
return port
def is_ssl(self):
'''
@name 是否开启了SSL
@return bool
'''
if os.path.exists(self.__is_ssl_file):
# 如果开启了SSL,则判断SSL证书和私钥文件是否存在
if os.path.exists(self.__ssl_key_file) and os.path.exists(self.__ssl_crt_file):
return True
return False
def get_ssl_config(self):
'''
@name 获取SSL配置
@return dict
'''
# 如果没有开启SSL,则返回空字符串
if not self.is_ssl():
return ''
ssl_conf = public.ReadFile(self.__webserver_ssl_conf_example)
if not ssl_conf:
self.print_log('Ssl configuration file not found.',self.__log_warning)
return ''
return ssl_conf
def get_http3_header(self):
'''
@name 获取HTTP3头部
@return dict
'''
http3_header = ''
# 如果开启了SSL,则添加HTTP3头部
if self.is_ssl():
'''h3=":443"; ma=2592000,h3-29=":443"; ma=2592000'''
http3_header = "add_header Alt-Svc 'h3=\":{PORT}\"; ma=86400,h3-29=\":{PORT}\"; ma=86400';".format(PORT=self.get_panel_port())
return http3_header
def get_listen_config(self):
'''
@name 获取监听配置
@return dict
'''
default_listen = '''
listen {PORT};
listen [::]:{PORT};
'''
# 是否开启了SSL
if self.is_ssl():
listen_conf = public.ReadFile(self.__webserver_listen_ssl_conf_example)
else:
listen_conf = public.ReadFile(self.__webserver_listen_conf_example)
# 如果没有找到监听配置文件,则使用默认监听配置
if not listen_conf:
listen_conf = default_listen
# 替换面板端口
port = self.get_panel_port()
listen_conf = listen_conf.format(PORT=port)
return listen_conf
def create_conf(self):
'''
@name 创建配置文件
@return bool
'''
if not self.conf_example_exists():
self.print_log('Web server configuration file template not found.',self.__log_error)
return False
# 读取配置文件模板
config_example = public.ReadFile(self.__webserver_conf_example)
if not config_example:
self.print_log('Web server configuration file template read failed.',self.__log_error)
return False
# 获取监听配置
listen_conf = self.get_listen_config()
# 获取SSL配置
ssl_conf = self.get_ssl_config()
# 获取HTTP3头部
http3_header = self.get_http3_header()
config = config_example.format(LISTEN=listen_conf, SSL_CONFIG=ssl_conf, HTTP3_HEADER=http3_header)
res = public.WriteFile(self.__webserver_conf, config)
if not res:
self.print_log("Write Web server configuration file failed",self.__log_error)
return False
# 测试配置文件是否正确
if not self.configtest():
self.print_log('Web server configuration file test failed.',self.__log_error)
return False
return True
def chmod_static_files(self):
'''
@name 设置静态文件权限
@return bool
'''
# 设置静态目录链权限
res = self.exec_shell('chmod 755 /www /www/server /www/server/panel /www/server/panel/BTPanel')
if res[1] != '':
self.print_log('\n'.join(res),self.__log_warning)
return False
# 递归设置静态文件权限
res = self.exec_shell('chmod -R 755 /www/server/panel/BTPanel/static')
if res[1] != '':
self.print_log('\n'.join(res),self.__log_warning)
return False
return True
def run_webserver(self):
'''
@name 启动web服务器
@return bool 是否成功
'''
try:
# 判断public模块是否符合要求
public_function = ['ExecShell','ReadFile','WriteFile','get_error_info','get_panel_path','get_panel_port','print_log']
for func in public_function:
if not self.hasattr_public(func):
return False
# 判断webserver二进制文件是否存在,如果不存在则尝试调用下载脚本下载
if not self.bin_exists():
if os.path.exists(self.__webserver_ctl):
self.exec_shell("bash {} download".format(self.__webserver_ctl))
self.print_log('Web server binary file not found.',self.__log_error)
return False
# 判断cli工具脚本是否存在
if not self.ctl_exists():
self.print_log('Web server command line tool not found.',self.__log_error)
return False
# 设置静态文件权限
if not self.chmod_static_files():
self.print_log('Web server static file permission setting failed.',self.__log_error)
return False
# 创建配置文件
if not self.create_conf():
self.print_log('Web server configuration file creation failed.',self.__log_error)
return False
# 检查webserver进程是否存在,如果存在则重载,否则启动
if self.process_status():
res = self.reload()
if not res:
self.print_log('Web server reload failed.',self.__log_warning)
else:
res = self.start()
# 检查进程是否正常运行
if not res:
self.print_log('Web server start failed.',self.__log_error)
return False
except:
# 将错误信息写入到日志
self.print_log(public.get_error_info(),self.__log_error)
return False
return True
if __name__ == '__main__':
web = webserver()
print(web.run_webserver())