Files
yakpanel-core/class/webserver.py

430 lines
14 KiB
Python
Raw Normal View History

2026-04-07 02:04:22 +05:30
# coding: utf-8
# +-------------------------------------------------------------------
# | YakPanel
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2017 YakPanel(https://www.yakpanel.com) All rights reserved.
# +-------------------------------------------------------------------
# | Author: hwl@yakpanel.com
# +-------------------------------------------------------------------
# +--------------------------------------------------------------------
# | 前置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/YakPanel')
if res[1] != '':
self.print_log('\n'.join(res),self.__log_warning)
return False
# 递归设置静态文件权限
res = self.exec_shell('chmod -R 755 /www/server/panel/YakPanel/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())