Files
yakpanel-core/class/webserver.py
2026-04-07 02:04:22 +05:30

430 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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())