Initial YakPanel commit

This commit is contained in:
Niranjan
2026-04-07 02:04:22 +05:30
commit 2826d3e7f3
5359 changed files with 1390724 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
import json
import os.path
import shutil
import sys
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
from .ip_restrict import IpRestrict, RealIpRestrict
from .redirect import RealRedirect, Redirect
from .access_restriction import AccessRestriction, RealAccessRestriction
from .domain_tool import domain_to_puny_code, check_domain, normalize_domain, NginxDomainTool, ApacheDomainTool, \
is_domain
from .dir_tool import DirTool
from .referer import Referer, RealReferer
from .logmanager import LogMgr, RealLogMgr
from .proxy import Proxy, RealProxy
from .ssl import SSLManager, RealSSLManger
from .config_mgr import ConfigMgr
from .default_site import set_default_site, get_default_site, check_default
from .server_extension import NginxExtension as ng_ext, ApacheExtension as ap_ext
def remove_sites_service_config(site_name: str, config_prefix: str = ""):
"""
用于删除一个网站的nginxapache的所有相关配置文件和配置项
包含:
配置文件,访问限制, 反向代理, 重定向, 防盗链,证书目录, IP黑白名单, 历史配置文件, 默认站点, 日志格式配置记录, 伪静态等
"""
# 配置文件
ng_file = "/www/server/panel/vhost/nginx/{}{}.conf".format(config_prefix, site_name)
if os.path.exists(ng_file):
os.remove(ng_file)
ap_file = "/www/server/panel/vhost/apache/{}{}.conf".format(config_prefix, site_name)
if os.path.exists(ap_file):
os.remove(ap_file)
# 访问限制
RealAccessRestriction(config_prefix=config_prefix).remove_site_access_restriction_info(site_name)
# 反向代理
RealProxy(config_prefix=config_prefix).remove_site_proxy_info(site_name)
# 重定向
RealRedirect(config_prefix=config_prefix).remove_site_redirect_info(site_name)
# 防盗链
RealReferer(config_prefix=config_prefix).remove_site_referer_info(site_name)
# 证书目录
cert_path = "/www/server/panel/vhost/cert/" + site_name
if os.path.isdir(cert_path):
shutil.rmtree(cert_path)
# IP黑白名单
RealIpRestrict(config_prefix=config_prefix).remove_site_ip_restrict_info(site_name)
# 历史配置文件
ConfigMgr(site_name=site_name, config_prefix=config_prefix).clear_history_file()
# 默认站点
d_site_name, d_prefix = get_default_site()
if d_site_name == site_name and d_prefix == config_prefix:
d_file = "/www/server/panel/data/mod_default_site.pl"
f = open(d_file, mode="w+")
json.dump({"name": None, "prefix": None}, f)
# 日志格式配置记录
RealLogMgr(conf_prefix=config_prefix).remove_site_log_format_info(site_name)
# 伪静态
rewrite_path = "/www/server/panel/vhost/rewrite/{}{}.conf".format(config_prefix, site_name)
if os.path.isdir(rewrite_path):
os.remove(rewrite_path)

Binary file not shown.

View File

@@ -0,0 +1,608 @@
# 访问限制, 目前不兼容之前版本的访问限制
# nginx 使用 if 和 正则实现,保障与反向代理、重定向的兼容性
# apache 实现方案未变
import os
import re
import json
import shutil
import warnings
from typing import Optional, Union, List, Dict
from itertools import chain
from .util import webserver, check_server_config, write_file, read_file, DB, service_reload, get_log_path, pre_re_key
from mod.base import json_response
warnings.filterwarnings("ignore", category=SyntaxWarning)
class _ConfigObject:
_config_file_path = ""
panel_path = "/www/server/panel"
def __init__(self):
self._config: Optional[dict] = None
@property
def config(self) -> Dict[str, dict]:
if self._config is None:
try:
self._config = json.loads(read_file(self._config_file_path))
except (json.JSONDecodeError, TypeError, ValueError):
self._config = {}
return self._config
def save_config(self):
if self._config:
write_file(self._config_file_path, json.dumps(self._config))
class ServerConfig:
_vhost_path = "/www/server/panel/vhost"
def __init__(self, config_prefix: str):
self.config_prefix: str = config_prefix
@staticmethod
def crypt_password(password) -> str:
import crypt
return crypt.crypt(password,password)
# nginx配置文件相关操作
class _NginxAccessConf(ServerConfig):
# 添加 include 导入配置项
def set_nginx_access_include(self, site_name) -> Optional[str]:
ng_file = "{}/nginx/{}{}.conf".format(self._vhost_path, self.config_prefix, site_name)
ng_conf = read_file(ng_file)
if not ng_conf:
return "配置文件丢失"
access_dir = "{}/nginx/access/{}".format(self._vhost_path, site_name)
if not os.path.isdir(os.path.dirname(access_dir)):
os.makedirs(os.path.dirname(access_dir))
if not os.path.isdir(access_dir):
os.makedirs(access_dir)
include_conf = (
" #引用访问限制规则,注释后配置的访问限制将无效\n"
" include /www/server/panel/vhost/nginx/access/%s/*.conf;\n"
) % site_name
rep_include = re.compile(r"\s*include.*/access/.*/\*\.conf\s*;", re.M)
if rep_include.search(ng_conf):
return
# 添加 引入
rep_list = [
(re.compile(r"#SSL-END"), False), # 匹配Referer配置, 加其下
(re.compile(r"(\s*#.*)?\s*include\s+.*/redirect/.*\.conf;"), True), # 重定向
(re.compile(r"(\s*#.*)?\s*include\s+.*/ip-restrict/.*\.conf;"), True), # Ip黑白名单
]
# 使用正则匹配确定插入位置 use_start 在前面插入还是后面插入
def set_by_rep_idx(tmp_rep: re.Pattern, use_start: bool) -> bool:
tmp_res = tmp_rep.search(ng_conf)
if not tmp_res:
return False
if use_start:
new_conf = ng_conf[:tmp_res.start()] + include_conf + tmp_res.group() + ng_conf[tmp_res.end():]
else:
new_conf = ng_conf[:tmp_res.start()] + tmp_res.group() + include_conf + ng_conf[tmp_res.end():]
write_file(ng_file, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return False
return True
for r, s in rep_list:
if set_by_rep_idx(r, s):
break
else:
return "无法在配置文件中定位到需要添加的项目"
# 写入配置文件
def set_nginx_access_by_conf(self, site_name: str, configs: Dict[str, List[Dict[str, str]]]) -> Optional[str]:
""" configs 示例结构
configs = {
"auth_dir": [
{
"name": "aaa",
"dir_path": "/",
"auth_file": "/www/server/pass/www.cache.com/aaa.pass",
"username":"aaaa",
"password":"aaaa",
}
],
"file_deny": [
{
"name": "bbb",
"dir_path": "/",
"suffix": ["png", "jpg"]
}
]
}
"""
path_map = {}
for c in chain(configs.get("auth_dir", []), configs.get("file_deny", [])):
if c["dir_path"] not in path_map:
path_map[c["dir_path"]] = {"path": c["dir_path"]}
path_map[c["dir_path"]].update(c)
path_list = list(path_map.values())
path_list.sort(key=lambda x: len(x["path"].split("/")), reverse=True)
conf_template = r"""location ~ "^%s.*$" {
auth_basic "Authorization";
auth_basic_user_file %s;
%s
}
"""
suffix_template = r'{tmp_pre}if ( $uri ~ "\.({suffix})$" ) {{\n{tmp_pre} return 404;\n{tmp_pre}}}'
suffix_template2 = r'if ( $uri ~ "^{path}.*\.({suffix})$" ) {{\n return 404;\n}}\n'
tmp_conf_list = []
for i in path_list:
if "auth_file" in i and "suffix" in i:
tmp_pre = " "
tmp_conf = conf_template % (
i["path"], i["auth_file"], suffix_template.format(tmp_pre=tmp_pre, suffix="|".join(i["suffix"]))
)
write_file(i["auth_file"], "{}:{}".format(i["username"], self.crypt_password(i["password"])))
elif "auth_file" in i:
tmp_conf = conf_template % (i["path"], i["auth_file"], "")
write_file(i["auth_file"], "{}:{}".format(i["username"], self.crypt_password(i["password"])))
else:
tmp_conf = suffix_template2.format(path=i["path"], suffix="|".join(i["suffix"]))
tmp_conf_list.append(tmp_conf)
config_data = "\n".join(tmp_conf_list)
config_file = "{}/nginx/access/{}/{}{}.conf".format(self._vhost_path, site_name, self.config_prefix, site_name)
old_config = read_file(config_file)
write_file(config_file, config_data)
if webserver() == "nginx" and check_server_config() is not None:
if isinstance(old_config, str):
write_file(config_file, old_config)
else:
write_file(config_file, "")
return "配置失败"
class _ApacheAccessConf(ServerConfig):
def set_apache_access_include(self, site_name) -> Optional[str]:
ap_file = "{}/apache/{}{}.conf".format(self._vhost_path, self.config_prefix, site_name)
ap_conf = read_file(ap_file)
if not ap_conf:
return "配置文件丢失"
access_dir = "{}/apache/access/{}".format(self._vhost_path, site_name)
if not os.path.isdir(os.path.dirname(access_dir)):
os.makedirs(os.path.dirname(access_dir))
if not os.path.isdir(access_dir):
os.makedirs(access_dir)
pass_dir = "/www/server/pass/" + site_name
if not os.path.isdir(os.path.dirname(pass_dir)):
os.makedirs(os.path.dirname(pass_dir))
if not os.path.isdir(pass_dir):
os.makedirs(pass_dir)
include_conf = (
"\n #引用访问限制规则,注释后配置的访问限制将无效\n"
" IncludeOptional /www/server/panel/vhost/apache/access/%s/*.conf\n"
) % site_name
rep_include = re.compile(r"\s*IncludeOptional.*/access/.*/\*\.conf", re.M)
if rep_include.search(ap_conf):
return
# 添加 引入
rep_vhost_r = re.compile(r"</VirtualHost>")
new_conf = rep_vhost_r.sub(include_conf + "</VirtualHost>", ap_conf)
if not rep_include.search(new_conf):
return "配置添加失败"
write_file(ap_file, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return "配置添加失败"
def set_apache_access_by_conf(self, site_name: str, configs: Dict[str, List[Dict[str, str]]]) -> Optional[str]:
""" configs 示例结构
configs = {
"auth_dir": [
{
"name": "aaa",
"dir_path": "/",
"auth_file": "/www/server/pass/www.cache.com/aaa.pass",
"username":"aaaa",
"password":"aaaa",
}
],
"file_deny": [
{
"name": "bbb",
"dir_path": "/",
"suffix": ["png", "jpg"]
}
]
}
"""
site_path = DB("sites").where("name=?", (site_name, )).find()["path"]
names = []
old_configs = []
access_dir = "{}/apache/access/{}".format(self._vhost_path, site_name)
for i in os.listdir(access_dir):
if not os.path.isfile(os.path.join(access_dir, i)):
continue
old_configs.append((i, read_file(os.path.join(access_dir, i))))
for c in chain(configs.get("auth_dir", []), configs.get("file_deny", [])):
if "suffix" in c:
self._set_apache_file_deny(c, site_name)
names.append("deny_{}.conf".format(c["name"]))
else:
self._set_apache_auth_dir(c, site_name, site_path)
names.append("auth_{}.conf".format(c["name"]))
for i in os.listdir(access_dir):
if i not in names:
os.remove(os.path.join(access_dir, i))
if webserver() == "apache" and check_server_config() is not None:
for i in os.listdir(access_dir):
os.remove(os.path.join(access_dir, i))
for n, data in old_configs: # 还原之前的配置文件
write_file(os.path.join(access_dir, n), data)
return "配置保存失败"
def _set_apache_file_deny(self, data: dict, site_name: str):
conf = r'''
#BEGIN_DENY_{n}
<Directory ~ "{d}.*\.({s})$">
Order allow,deny
Deny from all
</Directory>
#END_DENY_{n}
'''.format(n=data["name"], d=data["dir_path"], s="|".join(data["suffix"]))
access_file = "{}/apache/access/{}/deny_{}.conf".format(self._vhost_path, site_name, data["name"])
write_file(access_file, conf)
def _set_apache_auth_dir(self, data: dict, site_path: str, site_name: str):
conf = '''
<Directory "{site_path}{site_dir}">
#AUTH_START
AuthType basic
AuthName "Authorization "
AuthUserFile {auth_file}
Require user {username}
#AUTH_END
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
#Require all granted
DirectoryIndex index.php index.html index.htm default.php default.html default.htm
</Directory>'''.format(site_path=site_path, site_dir=data["dir_path"], auth_file=data["auth_file"],
username=data["username"], site_name=site_name)
write_file(data["auth_file"], "{}:{}".format(data["username"], self.crypt_password(data["password"])))
access_file = "{}/apache/access/{}/auth_{}.conf".format(self._vhost_path, site_path, data["name"])
write_file(access_file, conf)
class RealAccessRestriction(_ConfigObject, _ApacheAccessConf, _NginxAccessConf):
_config_file_path = "/www/server/panel/data/site_access.json"
def __init__(self, config_prefix: str):
super(RealAccessRestriction, self).__init__()
super(_ApacheAccessConf, self).__init__(config_prefix)
# 把配置信息更新到服务配置文件中
def _refresh_web_server_conf(self, site_name: str, site_access_conf: dict, web_server=None) -> Optional[str]:
if web_server is None:
web_server = webserver()
error_msg = self.set_apache_access_by_conf(site_name, site_access_conf)
if web_server == "apache" and error_msg is not None:
return error_msg
error_msg = self.set_nginx_access_by_conf(site_name, site_access_conf)
if web_server == "nginx" and error_msg is not None:
return error_msg
# 添加include配置到对应站点的配置文件中
def _set_web_server_conf_include(self, site_name, web_server=None) -> Optional[str]:
if web_server is None:
web_server = webserver()
error_msg = self.set_apache_access_include(site_name)
if web_server == "apache" and error_msg is not None:
return error_msg
error_msg = self.set_nginx_access_include(site_name)
if web_server == "nginx" and error_msg is not None:
return error_msg
def check_auth_dir_args(self, get, is_modify=False) -> Union[str, dict]:
values = {}
try:
values["site_name"] = get.site_name.strip()
values["dir_path"] = get.dir_path.strip()
except AttributeError:
return "parameter error"
if hasattr(get, "password"):
password = get.password.strip()
if len(password) < 3:
return '密码不能少于3位'
if re.search(r'\s', password):
return '密码不能存在空格'
values['password'] = password
else:
return '请输入密码!'
if hasattr(get, "username"):
username = get.username.strip()
if len(username) < 3:
return '账号不能少于3位'
if re.search(r'\s', username):
return '账号不能存在空格'
values['username'] = username
else:
return '请输入用户!'
if hasattr(get, "name"):
name = get.name.strip()
if len(name) < 3:
return '名称不能少于3位'
if re.search(r'\s', name):
return '名称不能存在空格'
if not re.search(r'^\w+$', name):
return '名称格式错误,仅支持数字字母下划线请参考格式aaa_bbb'
values['name'] = name
else:
return '请输入名称!'
if not is_modify:
data = self.config.get(values["site_name"], {}).get("auth_dir", [])
for i in data:
if i["dir_path"] == values["dir_path"]:
return "此路径已存在"
if i["name"] == values["name"]:
return "此名称已存在"
values["auth_file"] = "/www/server/pass/{}/{}.pass".format(values["site_name"], values["name"])
return values
def create_auth_dir(self, get) -> Optional[str]:
conf = self.check_auth_dir_args(get, is_modify=False)
if isinstance(conf, str):
return conf
web_server = webserver()
error_msg = self._set_web_server_conf_include(conf["site_name"], web_server)
if error_msg:
return error_msg
if conf["site_name"] not in self.config:
self.config[conf["site_name"]] = {"auth_dir": [], "file_deny": []}
self.config[conf["site_name"]]["auth_dir"].append(conf)
error_msg = self._refresh_web_server_conf(conf["site_name"], self.config[conf["site_name"]], web_server)
if error_msg:
return error_msg
self.save_config()
service_reload()
def modify_auth_dir(self, get) -> Optional[str]:
conf = self.check_auth_dir_args(get, is_modify=True)
if isinstance(conf, str):
return conf
data = self.config.get(conf["site_name"], {}).get("auth_dir", [])
target_idx = None
for idx, i in enumerate(data):
if i["name"] == conf["name"]:
target_idx = idx
break
if target_idx is None:
return "没有指定的配置信息"
web_server = webserver()
error_msg = self._set_web_server_conf_include(conf["site_name"], web_server)
if error_msg:
return error_msg
if conf["site_name"] not in self.config:
self.config[conf["site_name"]] = {"auth_dir": [], "file_deny": []}
self.config[conf["site_name"]]["auth_dir"][target_idx] = conf
error_msg = self._refresh_web_server_conf(conf["site_name"], self.config[conf["site_name"]], web_server)
if error_msg:
return error_msg
self.save_config()
service_reload()
def remove_auth_dir(self, site_name: str, name: str) -> Optional[str]:
if site_name not in self.config:
return "没有该网站的配置"
target = None
for idx, i in enumerate(self.config[site_name].get("auth_dir", [])):
if i.get("name", None) == name:
target = idx
if target is None:
return "没有该路径的配置"
del self.config[site_name]["auth_dir"][target]
web_server = webserver()
error_msg = self._refresh_web_server_conf(site_name, self.config[site_name], web_server)
if error_msg:
return error_msg
self.save_config()
service_reload()
return
def check_file_deny_args(self, get, is_modify=False) -> Union[str, dict]:
values = {}
try:
values["site_name"] = get.site_name.strip()
values["name"] = get.name.strip()
values["dir_path"] = get.dir_path.strip()
values["suffix"] = list(filter(lambda x: bool(x.strip()), json.loads(get.suffix.strip())))
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return "Parameter error"
if len(values["name"]) < 3:
return '规则名最少需要输入3个字符串'
if not values["suffix"]:
return '文件扩展名不可为空!'
if not values["dir_path"]:
return '目录不可为空!'
if not is_modify:
data = self.config.get(values["site_name"], {}).get("file_deny", [])
for i in data:
if i["dir_path"] == values["dir_path"]:
return "此路径已存在"
if i["name"] == values["name"]:
return "此名称已存在"
return values
def create_file_deny(self, get) -> Optional[str]:
conf = self.check_file_deny_args(get, is_modify=False)
if isinstance(conf, str):
return conf
web_server = webserver()
error_msg = self._set_web_server_conf_include(conf["site_name"], web_server)
if error_msg:
return error_msg
if conf["site_name"] not in self.config:
self.config[conf["site_name"]] = {"auth_dir": [], "file_deny": []}
self.config[conf["site_name"]]["file_deny"].append(conf)
error_msg = self._refresh_web_server_conf(conf["site_name"], self.config[conf["site_name"]], web_server)
if error_msg:
return error_msg
self.save_config()
service_reload()
def modify_file_deny(self, get) -> Optional[str]:
conf = self.check_file_deny_args(get, is_modify=True)
if isinstance(conf, str):
return conf
data = self.config.get(conf["site_name"], {}).get("file_deny", [])
target_idx = None
for idx, i in enumerate(data):
if i["name"] == conf["name"]:
target_idx = idx
break
if target_idx is None:
return "没有指定的配置信息"
web_server = webserver()
error_msg = self._set_web_server_conf_include(conf["site_name"], web_server)
if error_msg:
return error_msg
if conf["site_name"] not in self.config:
self.config[conf["site_name"]] = {"auth_dir": [], "file_deny": []}
self.config[conf["site_name"]]["file_deny"][target_idx] = conf
error_msg = self._refresh_web_server_conf(conf["site_name"], self.config[conf["site_name"]], web_server)
if error_msg:
return error_msg
self.save_config()
service_reload()
def remove_file_deny(self, site_name: str, name: str) -> Optional[str]:
if site_name not in self.config:
return "没有该网站的配置"
target = None
for idx, i in enumerate(self.config[site_name].get("file_deny", [])):
if i.get("name", None) == name:
target = idx
if target is None:
return "没有该路径的配置"
del self.config[site_name]["file_deny"][target]
web_server = webserver()
error_msg = self._refresh_web_server_conf(site_name, self.config[site_name], web_server)
if error_msg:
return error_msg
self.save_config()
service_reload()
return
def site_access_restriction_info(self, site_name: str) -> dict:
if site_name not in self.config:
return {"auth_dir": [], "file_deny": []}
else:
return self.config[site_name]
def remove_site_access_restriction_info(self, site_name):
if site_name in self.config:
del self.config["site_name"]
self.save_config()
ng_access_dir = "{}/nginx/access/{}".format(self._vhost_path, site_name)
ap_access_dir = "{}/apache/access/{}".format(self._vhost_path, site_name)
if os.path.isdir(ng_access_dir):
shutil.rmtree(ng_access_dir)
if os.path.isdir(ap_access_dir):
shutil.rmtree(ap_access_dir)
class AccessRestriction:
def __init__(self, config_prefix: str = ""):
self.config_prefix: str = config_prefix
self._ar = RealAccessRestriction(config_prefix)
def create_auth_dir(self, get):
res = self._ar.create_auth_dir(get)
if isinstance(res, str):
return json_response(status=False, msg=res)
return json_response(status=True, msg="Successfully added")
def modify_auth_dir(self, get):
res = self._ar.modify_auth_dir(get)
if isinstance(res, str):
return json_response(status=False, msg=res)
return json_response(status=True, msg="修改成功")
def remove_auth_dir(self, get):
try:
site_name = get.site_name.strip()
name = get.name.strip()
except AttributeError:
return json_response(status=False, msg="请求参数错误")
res = self._ar.remove_auth_dir(site_name, name)
if isinstance(res, str):
return json_response(status=False, msg=res)
return json_response(status=True, msg="Successfully delete")
def create_file_deny(self, get):
res = self._ar.create_file_deny(get)
if isinstance(res, str):
return json_response(status=False, msg=res)
return json_response(status=True, msg="Successfully added")
def modify_file_deny(self, get):
res = self._ar.modify_file_deny(get)
if isinstance(res, str):
return json_response(status=False, msg=res)
return json_response(status=True, msg="修改成功")
def remove_file_deny(self, get):
try:
site_name = get.site_name.strip()
name = get.name.strip()
except AttributeError:
return json_response(status=False, msg="请求参数错误")
res = self._ar.remove_file_deny(site_name, name)
if isinstance(res, str):
return json_response(status=False, msg=res)
return json_response(status=True, msg="Successfully delete")
def site_access_restriction_info(self, get):
try:
site_name = get.site_name.strip()
except AttributeError:
return json_response(status=False, msg="请求参数错误")
data = self._ar.site_access_restriction_info(site_name)
return json_response(status=True, data=data)

View File

@@ -0,0 +1,154 @@
import os
import time
from hashlib import md5
from typing import Optional
from .util import service_reload, check_server_config, write_file, read_file
# 支持读取配置文件
# 保存并重启配置文件
# 历史文件记录
class ConfigMgr:
_vhost_path = "/www/server/panel/vhost"
def __init__(self, site_name: str, config_prefix: str = ""):
self.site_name = site_name
self.config_prefix = config_prefix
def _read_config(self, web_server: str) -> Optional[str]:
config_file = "{}/{}/{}{}.conf".format(self._vhost_path, web_server, self.config_prefix, self.site_name)
res = read_file(config_file)
if isinstance(res, str):
return res
return None
def nginx_config(self) -> Optional[str]:
return self._read_config("nginx")
def apache_config(self) -> Optional[str]:
return self._read_config("apache")
def save_config(self, conf_data: str, web_server: str):
config_file = "{}/{}/{}{}.conf".format(self._vhost_path, web_server, self.config_prefix, self.site_name)
old_config = self._read_config(web_server)
write_file(config_file, conf_data)
errmsg = check_server_config()
if errmsg:
write_file(config_file, old_config)
return errmsg
self._save_history(web_server)
service_reload()
def save_nginx_config(self, conf_data: str) -> Optional[str]:
return self.save_config(conf_data, "nginx")
def save_apache_config(self, conf_data: str) -> Optional[str]:
return self.save_config(conf_data, "apache")
def history_list(self):
his_path = '/www/backup/file_history'
nginx_config_file = "{}/nginx/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ng_save_path = "{}{}".format(his_path, nginx_config_file)
apache_config_file = "{}/apache/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ap_save_path = "{}{}".format(his_path, apache_config_file)
return {
"nginx": [] if not os.path.isdir(ng_save_path) else sorted(os.listdir(ng_save_path), reverse=True),
"apache": [] if not os.path.isdir(ap_save_path) else sorted(os.listdir(ap_save_path), reverse=True)
}
def history_conf(self, history_id: str) -> Optional[str]:
his_path = '/www/backup/file_history'
nginx_config_file = "{}/nginx/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ng_save_path = "{}{}".format(his_path, nginx_config_file)
if os.path.isdir(ng_save_path):
for i in os.listdir(ng_save_path):
if i == history_id:
return read_file(os.path.join(ng_save_path, i))
apache_config_file = "{}/apache/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ap_save_path = "{}{}".format(his_path, apache_config_file)
if os.path.isdir(ap_save_path):
for i in os.listdir(ap_save_path):
if i == history_id:
return read_file(os.path.join(ap_save_path, i))
return None
def remove_history_file(self, history_id: str) -> None:
his_path = '/www/backup/file_history'
nginx_config_file = "{}/nginx/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ng_save_path = "{}{}".format(his_path, nginx_config_file)
if os.path.isdir(ng_save_path):
for i in os.listdir(ng_save_path):
if i == history_id:
os.remove(os.path.join(ng_save_path, i))
apache_config_file = "{}/apache/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ap_save_path = "{}{}".format(his_path, apache_config_file)
if os.path.isdir(ap_save_path):
for i in os.listdir(ap_save_path):
if i == history_id:
os.remove(os.path.join(ng_save_path, i))
def clear_history_file(self) -> None:
"""
清空所有的历史文件
"""
his_path = '/www/backup/file_history'
nginx_config_file = "{}/nginx/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ng_save_path = "{}{}".format(his_path, nginx_config_file)
if os.path.isdir(ng_save_path):
for i in os.listdir(ng_save_path):
os.remove(os.path.join(ng_save_path, i))
apache_config_file = "{}/apache/{}{}.conf".format(self._vhost_path, self.config_prefix, self.site_name)
ap_save_path = "{}{}".format(his_path, apache_config_file)
if os.path.isdir(ap_save_path):
for i in os.listdir(ap_save_path):
os.remove(os.path.join(ng_save_path, i))
@staticmethod
def _file_md5(filename):
if not os.path.isfile(filename):
return False
md5_obj = md5()
with open(filename, mode="rb") as f:
while True:
b = f.read(8096)
if not b:
break
md5_obj.update(b)
return md5_obj.hexdigest()
def _save_history(self, web_server: str):
if os.path.exists('/www/server/panel/data/not_file_history.pl'):
return True
his_path = '/www/backup/file_history'
filename = "{}/{}/{}{}.conf".format(self._vhost_path, web_server, self.config_prefix, self.site_name)
save_path = "{}{}".format(his_path, filename)
if not os.path.isdir(save_path):
os.makedirs(save_path, 384)
his_list = sorted(os.listdir(save_path), reverse=True) # 倒序排列已有的历史文件
try:
num = int(read_file('data/history_num.pl'))
except (ValueError, TypeError):
num = 100
is_write = True
if len(his_list) > 0:
new_file_md5 = self._file_md5(filename)
last_file_md5 = self._file_md5(os.path.join(save_path, his_list[0]))
is_write = new_file_md5 != last_file_md5
if is_write:
new_name = str(int(time.time()))
write_file(os.path.join(save_path, new_name), read_file(filename, 'rb'), "wb")
his_list.insert(0, new_name)
# 删除多余的副本
for i in his_list[num:]:
rm_file = save_path + '/' + i
if os.path.exists(rm_file):
os.remove(rm_file)

View File

@@ -0,0 +1,136 @@
import json
import os
import re
from typing import Optional, Tuple
from .util import listen_ipv6, write_file, read_file, service_reload
def check_default():
vhost_path = "/www/server/panel/vhost"
nginx = vhost_path + '/nginx'
httpd = vhost_path + '/apache'
httpd_default = '''<VirtualHost *:80>
ServerAdmin webmaster@example.com
DocumentRoot "/www/server/apache/htdocs"
ServerName bt.default.com
<Directory "/www/server/apache/htdocs">
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
DirectoryIndex index.html
</Directory>
</VirtualHost>
'''
listen_ipv6_str = ''
if listen_ipv6():
listen_ipv6_str = "\n listen [::]:80;"
nginx_default = '''server
{
listen 80;%s
server_name _;
index index.html;
root /www/server/nginx/html;
}''' % listen_ipv6_str
if not os.path.exists(httpd + '/0.default.conf') and not os.path.exists(httpd + '/default.conf'):
write_file(httpd + '/0.default.conf', httpd_default)
if not os.path.exists(nginx + '/0.default.conf') and not os.path.exists(nginx + '/default.conf'):
write_file(nginx + '/0.default.conf', nginx_default)
def get_default_site() -> Tuple[Optional[str], Optional[str]]:
panel_path = "/www/server/panel"
old_ds_file = panel_path + "/data/defaultSite.pl"
new_ds_file = panel_path + "/data/mod_default_site.pl"
if os.path.exists(old_ds_file) and not os.path.exists(new_ds_file):
write_file(new_ds_file, json.dumps({
"name": read_file(old_ds_file).strip(),
"prefix": ''
}))
res = read_file(new_ds_file)
if not isinstance(res, str):
return None, None
data = json.loads(res)
return data["name"], data["prefix"]
# site_name 传递None的时候表示将默认站点设置给关闭
# prefix 表示配置文件前缀, 如 "net_", 默认为空字符串
# domain 站点的域名 如: "www.sss.com:8456"
def set_default_site(site_name: Optional[str], prefix="", domain: str = None) -> Optional[str]:
# 清理旧的
old_default_name, old_prefix = get_default_site()
panel_path = "/www/server/panel"
default_site_save = panel_path + '/data/mod_default_site.pl'
if old_default_name:
ng_conf_file = os.path.join(panel_path, "vhost/nginx/{}{}.conf".format(old_prefix, old_default_name))
old_conf = read_file(ng_conf_file)
if isinstance(old_conf, str):
rep_listen_ds = re.compile(r"listen\s+.*default_server.*;")
new_conf_list = []
start_idx = 0
for tmp_res in rep_listen_ds.finditer(old_conf):
new_conf_list.append(old_conf[start_idx: tmp_res.start()])
new_conf_list.append(tmp_res.group().replace("default_server", ""))
start_idx = tmp_res.end()
new_conf_list.append(old_conf[start_idx:])
write_file(ng_conf_file, "".join(new_conf_list))
path = '/www/server/apache/htdocs/.htaccess'
if os.path.exists(path):
os.remove(path)
if site_name is None:
write_file(default_site_save, json.dumps({
"name": None,
"prefix": None
}))
service_reload()
return
# 处理新的
ap_path = '/www/server/apache/htdocs'
if os.path.exists(ap_path):
conf = '''<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{{HTTP_HOST}} !^127.0.0.1 [NC]
RewriteRule (.*) http://{}/$1 [L]
</IfModule>'''.format(domain)
write_file(ap_path + '/.htaccess', conf)
ng_conf_file = os.path.join(panel_path, "vhost/nginx/{}{}.conf".format(prefix, site_name))
ng_conf = read_file(ng_conf_file)
if isinstance(ng_conf, str):
rep_listen = re.compile(r"listen[^;]*;")
new_conf_list = []
start_idx = 0
for tmp_res in rep_listen.finditer(ng_conf):
new_conf_list.append(ng_conf[start_idx: tmp_res.start()])
print(tmp_res.group())
if tmp_res.group().find("default_server") == -1:
new_conf_list.append(tmp_res.group()[:-1] + " default_server;")
else:
new_conf_list.append(tmp_res.group())
start_idx = tmp_res.end()
new_conf_list.append(ng_conf[start_idx:])
write_file(ng_conf_file, "".join(new_conf_list))
write_file(default_site_save, json.dumps({
"name": site_name,
"prefix": prefix
}))
service_reload()
return

View File

@@ -0,0 +1,252 @@
# 网站文件相关操作
import os
import re
from typing import Optional, Union, List
from .util import webserver, check_server_config, write_file, read_file, DB, service_reload, pre_re_key, ExecShell
class DirTool:
def __init__(self, conf_prefix: str = ""):
self.conf_prefix = conf_prefix
self._vhost_path = "/www/server/panel/vhost"
# 修改站点路径
def modify_site_path(self, site_name: str, old_site_path: str, new_site_path: str) -> Optional[str]:
"""
修改 站点root 路径
site_name 站点名称
old_site_path 旧的root 路径
new_site_path 新的root 路径
"""
site_info = DB("sites").where("name=?", (site_name,)).find()
if not isinstance(site_info, dict):
return "站点信息查询错误"
error_msg = check_server_config()
if error_msg:
return "服务配置无法重载,请检查配置错误再操作。\n" + error_msg
if not self._check_site_path(new_site_path):
return '请不要将网站根目录设置到以下关键目录中'
if not os.path.exists(new_site_path):
return '指定的网站根目录不存在,无法设置,请检查输入信息.'
if old_site_path[-1] == '/':
old_site_path = old_site_path[:-1]
if new_site_path[-1] == '/':
new_site_path = new_site_path[:-1]
old_run_path = self.get_site_run_path(site_name)
if old_run_path is None:
return '读取网站当前运行目录失败,请检查配置文件'
old_run_path_sub = old_run_path.replace(old_site_path, "")
new_run_path = new_site_path + old_run_path_sub
if not os.path.exists(new_site_path):
new_run_path = new_site_path
nginx_file = '{}/nginx/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
nginx_conf = read_file(nginx_file)
if nginx_conf:
rep_root = re.compile(r'\s*root\s+(.+);', re.M)
new_conf = rep_root.sub(" root {};".format(new_run_path), nginx_conf)
write_file(nginx_file, new_conf)
apache_file = '{}/apache/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
apache_conf = read_file(apache_file)
if apache_conf:
rep_doc = re.compile(r"DocumentRoot\s+.*\n")
new_conf = rep_doc.sub('DocumentRoot "' + new_run_path + '"\n', apache_conf)
rep_dir = re.compile(r'''<Directory\s+['"]%s['"]'''% pre_re_key(old_site_path))
new_conf = rep_dir.sub('<Directory "' + new_run_path + '">\n', new_conf)
write_file(apache_file, new_conf)
# 创建basedir
userIni = new_run_path + '/.user.ini'
if os.path.exists(userIni):
ExecShell("chattr -i " + userIni)
write_file(userIni, 'open_basedir=' + new_run_path + '/:/tmp/')
ExecShell('chmod 644 ' + userIni)
ExecShell('chown root:root ' + userIni)
ExecShell('chattr +i ' + userIni)
service_reload()
DB("sites").where("id=?", (site_info["id"],)).setField('path', new_site_path)
return
# 修改站点的运行路径
def modify_site_run_path(self, site_name, site_path, new_run_path_sub: str) -> Optional[str]:
"""
修改 站点运行路径
site_name 站点名称
site_path 站点路径
new_run_path_sub root路径的子运行目录
如 site_path -> /www/wwwroots/aaaa
new_run_path_sub -> bbb/ccc
new_run_path -> /www/wwwroots/aaaa/bbb/ccc
"""
# 处理Nginx
old_run_path = self.get_site_run_path(site_name)
if old_run_path is None:
return '读取网站当前运行目录失败,请检查配置文件'
if new_run_path_sub.startswith("/"):
new_run_path_sub = new_run_path_sub[1:]
new_run_path = os.path.join(site_path, new_run_path_sub)
filename = '{}/nginx/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
nginx_conf = read_file(filename)
if nginx_conf:
tmp = re.search(r'\s*root\s+(.+);', nginx_conf)
if tmp:
o_path = tmp.groups()[0]
new_conf = nginx_conf.replace(o_path, new_run_path)
write_file(filename, new_conf)
# 处理Apache
filename = '{}/apache/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
ap_conf = read_file(filename)
if ap_conf:
tmp = re.search(r'\s*DocumentRoot\s*"(.+)"\s*\n', ap_conf)
if tmp:
o_path = tmp.groups()[0]
new_conf = ap_conf.replace(o_path, new_run_path)
write_file(filename, new_conf)
s_path = old_run_path + "/.user.ini"
d_path = new_run_path + "/.user.ini"
if s_path != d_path:
ExecShell("chattr -i {}".format(s_path))
ExecShell("mv {} {}".format(s_path, d_path))
ExecShell("chattr +i {}".format(d_path))
service_reload()
# 获取站点的运行路径, 返回的路径是完整路径
def get_site_run_path(self, site_name) -> Optional[str]:
web_server = webserver()
filename = "{}/{}/{}{}.conf".format(self._vhost_path, web_server, self.conf_prefix, site_name)
if not os.path.exists(filename):
return None
run_path = None
conf = read_file(filename)
if web_server == 'nginx':
tmp1 = re.search(r'\s*root\s+(?P<path>.+);', conf)
if tmp1:
run_path = tmp1.group("path").strip()
elif web_server == 'apache':
tmp1 = re.search(r'\s*DocumentRoot\s*"(?P<path>.+)"\s*\n', conf)
if tmp1:
run_path = tmp1.group("path")
else:
tmp1 = re.search(r"vhRoot\s*(?P<path>.*)", conf)
if tmp1:
run_path = tmp1.group("path").strip()
return run_path
# 获取index 文件
def get_index_conf(self, site_name) -> Union[str, List[str]]:
web_server = webserver()
filename = "{}/{}/{}{}.conf".format(self._vhost_path, web_server, self.conf_prefix, site_name)
if not os.path.exists(filename):
return "配置文件丢失"
conf = read_file(filename)
if not conf:
return "配置文件丢失"
split_char = " "
if web_server == 'nginx':
rep = re.compile(r"\s+index\s+(?P<target>.+);", re.M)
elif web_server == 'apache':
rep = re.compile(r"DirectoryIndex\s+(?P<target>.+)", re.M)
else:
rep = re.compile(r"indexFiles\s+(?P<target>.+)", re.M)
split_char = ","
res = rep.search(conf)
if not res:
return "获取失败,配置文件中不存在默认文档"
res_list = list(filter(None, map(lambda x: x.strip(), res.group("target").split(split_char))))
return res_list
# 获取设置index 文件 可以用 filenames 参数依次传入多个, 或 通过 file_list 参数传入index 列表
def set_index_conf(self, site_name, *filenames: str, file_list: Optional[List[str]] = None):
index_list = set()
for i in filenames:
f = i.strip()
if not f:
continue
index_list.add(f)
if file_list is not None:
for i in file_list:
f = i.strip()
if not f:
continue
index_list.add(f)
# nginx
file = '{}/nginx/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
conf = read_file(file)
if conf:
rep_index = re.compile(r"\s*index\s+.+;")
new_conf = rep_index.sub(" index {};".format(" ".join(index_list)), conf)
write_file(file, new_conf)
# apache
file = '{}/apache/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
conf = read_file(file)
if conf:
rep_index = re.compile(r"\s*DirectoryIndex\s+.+\n")
new_conf = rep_index.sub(" DirectoryIndex {}\n".format(" ".join(index_list)), conf)
write_file(file, new_conf)
# openlitespeed
file = '{}/openlitespeed/detail/{}{}.conf'.format(self._vhost_path, self.conf_prefix, site_name)
conf = read_file(file)
if conf:
rep_index = re.compile(r"indexFiles\s+.+\n")
new_conf = rep_index.sub('indexFiles {}\n'.format(",".join(index_list)), conf)
write_file(file, new_conf)
service_reload()
return
def _check_site_path(self, site_path):
try:
if site_path.find('/usr/local/lighthouse/') >= 0:
return True
if site_path in ['/', '/usr', '/dev', '/home', '/media', '/mnt', '/opt', '/tmp', '/var']:
return False
whites = ['/www/server/tomcat', '/www/server/stop', '/www/server/phpmyadmin']
for w in whites:
if site_path.find(w) == 0:
return True
a, error_paths = self._get_sys_path()
site_path = site_path.strip()
if site_path[-1] == '/': site_path = site_path[:-1]
if site_path in a:
return False
site_path += '/'
for ep in error_paths:
if site_path.find(ep) == 0:
return False
return True
except:
return False
@staticmethod
def _get_sys_path():
"""
@name 关键目录
@author hwliang<2021-06-11>
@return tuple
"""
a = ['/www', '/usr', '/', '/dev', '/home', '/media', '/mnt', '/opt', '/tmp', '/var']
c = ['/www/.Recycle_bin/', '/www/backup/', '/www/php_session/', '/www/wwwlogs/', '/www/server/', '/etc/',
'/usr/', '/var/', '/boot/', '/proc/', '/sys/', '/tmp/', '/root/', '/lib/', '/bin/', '/sbin/', '/run/',
'/lib64/', '/lib32/', '/srv/']
return a, c

1682
mod/base/web_conf/dns_api.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,335 @@
import os
import re
from typing import Tuple, Optional, Union, List, Dict
from .util import webserver, check_server_config, write_file, read_file, service_reload, listen_ipv6, use_http2
def domain_to_puny_code(domain: str) -> str:
new_domain = ''
for dkey in domain.split('.'):
if dkey == '*' or dkey == "":
continue
# 匹配非ascii字符
match = re.search(u"[\x80-\xff]+", dkey)
if not match:
match = re.search(u"[\u4e00-\u9fa5]+", dkey)
if not match:
new_domain += dkey + '.'
else:
new_domain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.'
if domain.startswith('*.'):
new_domain = "*." + new_domain
return new_domain[:-1]
def check_domain(domain: str) -> Optional[str]:
domain = domain_to_puny_code(domain)
# 判断通配符域名格式
if domain.find('*') != -1 and domain.find('*.') == -1:
return None
# 判断域名格式
rep_domain = re.compile(r"^([\w\-*]{1,100}\.){1,24}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$")
if not rep_domain.match(domain):
return None
return domain
def is_domain(domain: str) -> bool:
domain_regex = re.compile(
r'(?:[A-Z0-9_](?:[A-Z0-9-_]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\Z',
re.IGNORECASE
)
return True if domain_regex.match(domain) else False
# 检查原始的域名列表,返回[(domain, port)] 的格式,并返回其中有错误的项目
def normalize_domain(*domains: str) -> Tuple[List[Tuple[str, str]], List[Dict]]:
res, error = [], []
for i in domains:
if not i.strip():
continue
d_list = [i.strip() for i in i.split(":")]
if len(d_list) > 1:
try:
p = int(d_list[1])
if not (1 < p < 65535):
error.append({
"domain": i,
"msg": "端口范围错误"
})
continue
else:
d_list[1] = str(p)
except:
error.append({
"domain": i,
"msg": "端口范围错误"
})
continue
else:
d_list.append("80")
d, p = d_list
d = check_domain(d)
if isinstance(d, str):
res.append((d, p)),
continue
error.append({
"domain": i,
"msg": "域名格式错误"
})
res = list(set(res))
return res, error
class NginxDomainTool:
ng_vhost = "/www/server/panel/vhost/nginx"
def __init__(self, conf_prefix: str = ""):
self.conf_prefix = conf_prefix
# 在给定的配置文件中添加端口
@staticmethod
def nginx_add_port_by_config(conf, *port: str, is_http3=False) -> str:
ports = set()
for p in port:
ports.add(p)
# 设置端口
rep_port = re.compile(r"\s*listen\s+[\[\]:]*(?P<port>[0-9]+)(?P<ds>\s*default_server)?.*;[^\n]*\n", re.M)
use_ipv6 = listen_ipv6()
last_port_idx = None
need_remove_port_idx = []
had_ports = set()
is_default_server = False
for tmp_res in rep_port.finditer(conf):
last_port_idx = tmp_res.end()
if tmp_res.group("ds") and tmp_res.group("ds").strip():
is_default_server = True
if tmp_res.group("port") in ports:
had_ports.add(tmp_res.group("port"))
elif tmp_res.group("port") != "443":
need_remove_port_idx.append((tmp_res.start(), tmp_res.end()))
if not last_port_idx:
last_port_idx = re.search(r"server\s*\{\s*?\n", conf).end()
need_add_ports = ports - had_ports
d_s = " default_server" if is_default_server else ""
h2 = " http2" if use_http2() else ""
if need_add_ports or is_http3:
listen_add_list = []
for p in need_add_ports:
if p == "443":
tmp = " listen 443 ssl{}{};\n".format(h2, d_s)
if use_ipv6:
tmp += " listen [::]:443 ssl{}{};\n".format(h2, d_s)
listen_add_list.append(tmp)
continue
tmp = " listen {}{};\n".format(p, d_s)
if use_ipv6:
tmp += " listen [::]:{}{};\n".format(p, d_s)
listen_add_list.append(tmp)
if is_http3 and "443" in (had_ports | had_ports):
listen_add_list.append(" listen 443 quic{};\n".format(d_s))
if use_ipv6:
listen_add_list.append(" listen [::]:443 quic{};\n".format(d_s))
new_conf = conf[:last_port_idx] + "".join(listen_add_list) + conf[last_port_idx:]
return new_conf
return conf
# 将站点配置的域名和端口,写到配置文件中
def nginx_set_domain(self, site_name, *domain: Tuple[str, str]) -> Optional[str]:
ng_file = '{}/{}{}.conf'.format(self.ng_vhost, self.conf_prefix, site_name)
ng_conf = read_file(ng_file)
if not ng_conf:
return "nginx配置文件丢失"
domains_set, ports = set(), set()
for d, p in domain:
domains_set.add(d)
ports.add(p)
# 设置域名
rep_server_name = re.compile(r"\s*server_name\s*(.*);", re.M)
new_conf = rep_server_name.sub("\n server_name {};".format(" ".join(domains_set)), ng_conf, 1)
# 设置端口
rep_port = re.compile(r"\s*listen\s+[\[\]:]*(?P<port>[0-9]+)(?P<ds>\s*default_server)?.*;[^\n]*\n", re.M)
use_ipv6 = listen_ipv6()
last_port_idx = None
need_remove_port_idx = []
had_ports = set()
is_default_server = False
for tmp_res in rep_port.finditer(new_conf):
last_port_idx = tmp_res.end()
if tmp_res.group("ds") is not None and tmp_res.group("ds").strip():
is_default_server = True
if tmp_res.group("port") in ports:
had_ports.add(tmp_res.group("port"))
elif tmp_res.group("port") != "443":
need_remove_port_idx.append((tmp_res.start(), tmp_res.end()))
if not last_port_idx:
last_port_idx = re.search(r"server\s*\{\s*?\n", new_conf).end()
ports = ports - had_ports
if ports:
d_s = " default_server" if is_default_server else ""
listen_add_list = []
for p in ports:
tmp = " listen {}{};\n".format(p, d_s)
if use_ipv6:
tmp += " listen [::]:{}{};\n".format(p, d_s)
listen_add_list.append(tmp)
new_conf = new_conf[:last_port_idx] + "".join(listen_add_list) + new_conf[last_port_idx:]
# 移除多余的port监听
# 所有遍历的索引都在 last_port_idx 之前,所有不会影响之前的修改 ↑
if need_remove_port_idx:
conf_list = []
idx = 0
for start, end in need_remove_port_idx:
conf_list.append(new_conf[idx:start])
idx = end
conf_list.append(new_conf[idx:])
new_conf = "".join(conf_list)
# 保存配置文件
write_file(ng_file, new_conf)
web_server = webserver()
if web_server == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return "配置失败"
if web_server == "nginx":
service_reload()
class ApacheDomainTool:
ap_vhost = "/www/server/panel/vhost/apache"
ap_path = "/www/server/apache"
def __init__(self, conf_prefix: str = ""):
self.conf_prefix = conf_prefix
# 将站点配置的域名和端口,写到配置文件中
def apache_set_domain(self,
site_name, # 站点名称
*domain: Tuple[str, str], # 域名列表,可以为多个
template_path: Optional[str] = None, # 在新加端口时使用一个模板作为添加内容
template_kwargs: Optional[dict] = None, # 在使用一个模板时的填充参数,
) -> Optional[str]:
"""
template_path: 在新加端口时使用一个模板作为添加内容
template_kwargs: 在使用一个模板时的填充参数
port domains server_admin server_name 四个参数会自动生成并填充
没有传入 template_path 将会复制第一个虚拟机VirtualHost配置
"""
ap_file = '{}/{}{}.conf'.format(self.ap_vhost, self.conf_prefix, site_name)
ap_conf: str = read_file(ap_file)
if not ap_conf:
return "nginx配置文件丢失"
domains, ports = set(), set()
for i in domain:
domains.add(str(i[0]))
ports.add(str(i[1]))
domains_str = " ".join(domains)
# 设置域名
rep_server_name = re.compile(r"\s*ServerAlias\s*(.*)\n", re.M)
new_conf = rep_server_name.sub("\n ServerAlias {}\n".format(domains_str), ap_conf)
tmp_template_res = re.search(r"<VirtualHost(.|\n)*?</VirtualHost>", new_conf)
if not tmp_template_res:
tmp_template = None
else:
tmp_template = tmp_template_res.group()
rep_ports = re.compile(r"<VirtualHost +.*:(?P<port>\d+)+\s*>")
need_remove_port = []
for tmp in rep_ports.finditer(new_conf):
if tmp.group("port") in ports:
ports.remove(tmp.group("port"))
elif tmp.group("port") != "443":
need_remove_port.append(tmp.group("port"))
if need_remove_port:
for i in need_remove_port:
tmp_rep = re.compile(r"<VirtualHost.*" + i + r"(.|\n)*?</VirtualHost[^\n]*\n?")
new_conf = tmp_rep.sub("", new_conf, 1)
if ports:
other_config_body_list = []
if template_path is not None:
# 添加其他的port
try:
config_body = read_file(template_path)
for p in ports:
other_config_body_list.append(config_body.format(
port=p,
server_admin="admin@{}".format(site_name),
server_name='{}.{}'.format(p, site_name),
domains=domains_str,
**template_kwargs
))
except:
raise ValueError("参数与模板不匹配")
else:
if tmp_template is None:
return "配置文件格式错误"
for p in ports:
other_config_body_list.append(rep_ports.sub("<VirtualHost *:{}>".format(p), tmp_template, 1))
new_conf += "\n" + "\n".join(other_config_body_list)
write_file(ap_file, new_conf)
# 添加端口
self.apache_add_ports(*ports)
web_server = webserver()
if web_server == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return "配置失败"
if web_server == "apache":
service_reload()
# 添加apache主配置文件中的端口监听
@classmethod
def apache_add_ports(cls, *ports: Union[str, int]) -> None:
real_ports = set()
for p in ports:
real_ports.add(str(p))
ssl_conf_file = '{}/conf/extra/httpd-ssl.conf'.format(cls.ap_path)
if os.path.isfile(ssl_conf_file):
ssl_conf = read_file(ssl_conf_file)
if isinstance(ssl_conf, str) and ssl_conf.find('Listen 443') != -1:
ssl_conf = ssl_conf.replace('Listen 443', '')
write_file(ssl_conf_file, ssl_conf)
ap_conf_file = '{}/conf/httpd.conf'.format(cls.ap_path)
if not os.path.isfile(ap_conf_file):
return
ap_conf = read_file(ap_conf_file)
if ap_conf is None:
return
rep_ports = re.compile(r"Listen\s+(?P<port>[0-9]+)\n", re.M)
last_idx = None
for key in rep_ports.finditer(ap_conf):
last_idx = key.end()
if key.group("port") in real_ports:
real_ports.remove(key.group("port"))
if not last_idx:
return
new_conf = ap_conf[:last_idx] + "\n".join(["Listen %s" % i for i in real_ports]) + "\n" + ap_conf[last_idx:]
write_file(ap_conf_file, new_conf)

View File

@@ -0,0 +1,326 @@
import os
import re
import json
from typing import Tuple, Optional, Union
from ipaddress import ip_address
from .util import webserver, check_server_config, write_file, read_file, DB, service_reload
from mod.base import json_response
class _BaseRestrict:
def __init__(self, config_file: str, site_name: str):
self._conf_file = config_file
self._conf = self._read_conf()
self.site_name = site_name
def _read_conf(self):
default_conf = {
"restrict_type": "closed",
"black_list": [],
"white_list": []
}
if not os.path.exists(self._conf_file):
return default_conf
try:
conf = json.loads(read_file(self._conf_file))
except:
conf = default_conf
return conf
def to_view(self):
return self._conf
class _IpRestrict(_BaseRestrict):
def __init__(self, site_name: str, config_prefix: str):
setup_path = "/www/server/panel"
ip_restrict_conf_dir = "{}/data/ip_restrict_data".format(setup_path)
if not os.path.exists(ip_restrict_conf_dir):
os.makedirs(ip_restrict_conf_dir)
super().__init__("{}/{}{}".format(ip_restrict_conf_dir, config_prefix, site_name), site_name)
self.config_prefix = config_prefix
self.nginx_sub_file = "{}/vhost/ip-restrict/{}{}.conf".format(setup_path, self.config_prefix, self.site_name)
@property
def restrict_type(self):
return self._conf.get("restrict_type", "black")
@restrict_type.setter
def restrict_type(self, data: str):
if data in ("black", "white", "closed"):
self._conf["restrict_type"] = data
@property
def black_list(self):
return self._conf.get("black_list", [])
@black_list.setter
def black_list(self, list_data: list):
self._conf["black_list"] = list_data
@property
def white_list(self):
return self._conf.get("white_list", [])
@white_list.setter
def white_list(self, list_data: list):
self._conf["white_list"] = list_data
def save(self) -> Tuple[bool, str]:
if not self._conf: # 没有的时候不操作
return True, "operate successfully"
write_file(self._conf_file, json.dumps(self._conf))
if self.restrict_type == "closed":
write_file(self.nginx_sub_file, "")
service_reload()
return True, "operate successfully"
tmp_conf = []
if self.restrict_type == "white":
for i in self.white_list:
tmp_conf.append("allow {};".format(i))
tmp_conf.append("deny all; # 除开上述IP外其他IP全部禁止访问")
elif self.restrict_type == "black":
for i in self.black_list:
tmp_conf.append("deny {};".format(i))
else:
raise ValueError("错误的类型,无法操作")
write_file(self.nginx_sub_file, "\n".join(tmp_conf))
error_msg = check_server_config()
if error_msg is not None:
write_file(self.nginx_sub_file, "")
return False, "操作失败"
service_reload()
return True, "operate successfully"
# 删除网站时调用,删除配置文件
def remove_config_for_remove_site(self):
if os.path.isfile(self.nginx_sub_file):
os.remove(self.nginx_sub_file)
if os.path.isfile(self._conf_file):
os.remove(self._conf_file)
class RealIpRestrict:
def __init__(self, config_prefix: str = ""):
self.config_prefix = config_prefix
self.web_server = webserver()
# 获取某个站点的IP黑白名单详情
def restrict_conf(self, site_name: str) -> Tuple[bool, Union[str, dict]]:
if self.web_server != "nginx":
return False, "不支持除nginx之外的服务器"
ip_conf = _IpRestrict(site_name, self.config_prefix)
if not self._get_status_in_nginx_conf(ip_conf):
ip_conf.restrict_type = "closed"
return True, ip_conf.to_view()
# 从配置文件中获取状态
def _get_status_in_nginx_conf(self, ip_conf: _IpRestrict) -> bool:
setup_path = "/www/server/panel"
ng_file = "{}/vhost/nginx/{}{}.conf".format(setup_path, self.config_prefix, ip_conf.site_name)
rep_include = re.compile(r"\sinclude +.*/ip-restrict/.*\.conf;", re.M)
ng_conf = read_file(ng_file)
if not isinstance(ng_conf, str):
return False
if rep_include.search(ng_conf):
return True
return False
def _set_nginx_include(self, ip_conf: _IpRestrict) -> Tuple[bool, str]:
setup_path = "/www/server/panel"
ng_file = "{}/vhost/nginx/{}{}.conf".format(setup_path, self.config_prefix, ip_conf.site_name)
if not os.path.exists(os.path.dirname(ip_conf.nginx_sub_file)):
os.makedirs(os.path.dirname(ip_conf.nginx_sub_file), 0o600)
if not os.path.isfile(ip_conf.nginx_sub_file):
write_file(ip_conf.nginx_sub_file, "")
ng_conf = read_file(ng_file)
if not isinstance(ng_conf, str):
return False, "nginx配置文件读取失败"
rep_include = re.compile(r"\s*include\s+.*/ip-restrict/.*\.conf;", re.M)
if rep_include.search(ng_conf):
return True, ""
_include_str = (
"\n #引用IP黑白名单规则注释后配置的IP黑白名单将无效\n"
" include {};"
).format(ip_conf.nginx_sub_file)
rep_redirect_include = re.compile(r"\s*include\s+.*/redirect/.*\.conf;", re.M) # 如果有重定向,添加到重定向之后
redirect_include_res = rep_redirect_include.search(ng_conf)
if redirect_include_res:
new_conf = ng_conf[:redirect_include_res.end()] + _include_str + ng_conf[redirect_include_res.end():]
else:
if "#SSL-END" not in ng_conf:
return False, "添加配置失败无法定位SSL相关配置的位置"
new_conf = ng_conf.replace("#SSL-END", "#SSL-END" + _include_str)
write_file(ng_file, new_conf)
if self.web_server == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return False, "添加配置失败"
return True, ""
def set_ip_restrict(self, site_name: str, set_type: str) -> Tuple[bool, str]:
ip_restrict = _IpRestrict(site_name, self.config_prefix)
if set_type not in ("black", "white", "closed"):
return False, "不支持的类型【{}".format(set_type)
ip_restrict.restrict_type = set_type
f, msg = self._set_nginx_include(ip_restrict)
if not f:
return False, msg
return ip_restrict.save()
def add_black_ip_restrict(self, site_name: str, *ips: str) -> Tuple[bool, str]:
try:
for ip in ips:
_ = ip_address(ip) # 引发valueError
except ValueError:
return False, "ip参数解析错误"
ip_restrict = _IpRestrict(site_name, self.config_prefix)
black_list = ip_restrict.black_list
for i in ips:
if i not in black_list:
black_list.append(i)
ip_restrict.black_list = black_list
f, msg = self._set_nginx_include(ip_restrict)
if not f:
return False, msg
return ip_restrict.save()
def remove_black_ip_restrict(self, site_name: str, *ips: str):
ip_restrict = _IpRestrict(site_name, self.config_prefix)
black_list = ip_restrict.black_list
for i in ips:
if i in black_list:
black_list.remove(i)
ip_restrict.black_list = black_list
f, msg = self._set_nginx_include(ip_restrict)
if not f:
return False, msg
return ip_restrict.save()
def add_white_ip_restrict(self, site_name: str, *ips: str) -> Tuple[bool, str]:
try:
for ip in ips:
_ = ip_address(ip) # 引发valueError
except ValueError:
return False, "ip参数解析错误"
ip_restrict = _IpRestrict(site_name, self.config_prefix)
white_list = ip_restrict.white_list
for i in ips:
if i not in white_list:
white_list.append(i)
ip_restrict.white_list = white_list
f, msg = self._set_nginx_include(ip_restrict)
if not f:
return False, msg
return ip_restrict.save()
def remove_white_ip_restrict(self, site_name: str, *ips: str) -> Tuple[bool, str]:
ip_restrict = _IpRestrict(site_name, self.config_prefix)
white_list = ip_restrict.white_list
for i in ips:
if i in white_list:
white_list.remove(i)
ip_restrict.white_list = white_list
return ip_restrict.save()
def remove_site_ip_restrict_info(self, site_name: str):
ip_restrict = _IpRestrict(site_name, self.config_prefix)
ip_restrict.remove_config_for_remove_site()
class IpRestrict:
def __init__(self, config_prefix: str = ""):
self.config_prefix = config_prefix
self._ri = RealIpRestrict(self.config_prefix)
# 获取ip控制信息
def restrict_conf(self, get):
try:
site_name = get.site_name.strip()
except (AttributeError, json.JSONDecodeError):
return json_response(status=False, msg="Parameter error")
f, d = self._ri.restrict_conf(site_name)
if not f:
return json_response(status=f, msg=d)
return json_response(status=f, data=d)
# 设置ip黑白名单状态
def set_ip_restrict(self, get):
try:
site_name = get.site_name.strip()
set_ip_restrict = get.set_type.strip()
except (AttributeError, json.JSONDecodeError):
return json_response(status=False, msg="Parameter error")
f, m = self._ri.set_ip_restrict(site_name, set_ip_restrict)
return json_response(status=f, msg=m)
# 添加黑名单
def add_black_ip_restrict(self, get):
try:
site_name = get.site_name.strip()
value = get.value.strip()
except AttributeError:
return json_response(status=False, msg="Parameter error")
f, m = self._ri.add_black_ip_restrict(site_name, value)
return json_response(status=f, msg=m)
# 移除黑名单
def remove_black_ip_restrict(self, get):
try:
site_name = get.site_name.strip()
value = get.value.strip()
except (AttributeError, json.JSONDecodeError):
return json_response(status=False, msg="Parameter error")
f, m = self._ri.remove_black_ip_restrict(site_name, value)
return json_response(status=f, msg=m)
# 添加白名单
def add_white_ip_restrict(self, get):
try:
site_name = get.site_name.strip()
value = get.value.strip()
except (AttributeError, json.JSONDecodeError):
return json_response(status=False, msg="Parameter error")
f, m = self._ri.add_white_ip_restrict(site_name, value)
return json_response(status=f, msg=m)
# 移除白名单
def remove_white_ip_restrict(self, get):
try:
site_name = get.site_name.strip()
value = get.value.strip()
except (AttributeError, json.JSONDecodeError):
return json_response(status=False, msg="Parameter error")
f, m = self._ri.remove_white_ip_restrict(site_name, value)
return json_response(status=f, msg=m)

View File

@@ -0,0 +1,238 @@
import os
import re
from typing import Tuple, Union
from .util import webserver
class LimitNet(object):
def get_limit_net(self, get) -> Union[bool, str]:
if webserver() != 'nginx':
return False, ""
try:
site_id = int(get.site_id)
except (AttributeError, TypeError, ValueError):
return public.returnMsg(False, "Parameter error")
if self.config_prefix is None:
return public.returnMsg(False, "不支持的网站类型")
# 取配置文件
site_name = public.M('sites').where("id=?", (site_id,)).getField('name')
filename = "{}/vhost/nginx/{}{}.conf".format(self.setup_path, self.config_prefix, site_name)
conf = public.readFile(filename)
if not isinstance(conf, str):
return public.returnMsg(False, "配置文件读取错误")
# 站点总并发
data = {
'perserver': 0,
'perip': 0,
'limit_rate': 0,
}
rep_per_server = re.compile(r"(?P<prefix>.*)limit_conn +perserver +(?P<target>\d+) *; *", re.M)
tmp_res = rep_per_server.search(conf)
if tmp_res is not None and tmp_res.group("prefix").find("#") == -1: # 有且不是注释
data['perserver'] = int(tmp_res.group("target"))
# IP并发限制
rep_per_ip = re.compile(r"(?P<prefix>.*)limit_conn +perip +(?P<target>\d+) *; *", re.M)
tmp_res = rep_per_ip.search(conf)
if tmp_res is not None and tmp_res.group("prefix").find("#") == -1: # 有且不是注释
data['perip'] = int(tmp_res.group("target"))
# 请求并发限制
rep_limit_rate = re.compile(r"(?P<prefix>.*)limit_rate +(?P<target>\d+)\w+ *; *", re.M)
tmp_res = rep_limit_rate.search(conf)
if tmp_res is not None and tmp_res.group("prefix").find("#") == -1: # 有且不是注释
data['limit_rate'] = int(tmp_res.group("target"))
self._show_limit_net(data)
return data
@staticmethod
def _show_limit_net(data):
values = [
[300, 25, 512],
[200, 10, 1024],
[50, 3, 2048],
[500, 10, 2048],
[400, 15, 1024],
[60, 10, 512],
[150, 4, 1024],
]
for i, c in enumerate(values):
if data["perserver"] == c[0] and data["perip"] == c[1] and data["limit_rate"] == c[2]:
data["value"] = i + 1
break
else:
data["value"] = 0
@staticmethod
def _set_nginx_conf_limit() -> Tuple[bool, str]:
# 设置共享内存
nginx_conf_file = "/www/server/nginx/conf/nginx.conf"
if not os.path.exists(nginx_conf_file):
return False, "nginx配置文件丢失"
nginx_conf = public.readFile(nginx_conf_file)
rep_perip = re.compile(r"\s+limit_conn_zone +\$binary_remote_addr +zone=perip:10m;", re.M)
rep_per_server = re.compile(r"\s+limit_conn_zone +\$server_name +zone=perserver:10m;", re.M)
perip_res = rep_perip.search(nginx_conf)
per_serve_res = rep_per_server.search(nginx_conf)
if perip_res and per_serve_res:
return True, ""
elif perip_res or per_serve_res:
tmp_res = perip_res or per_serve_res
new_conf = nginx_conf[:tmp_res.start()] + (
"\n\t\tlimit_conn_zone $binary_remote_addr zone=perip:10m;"
"\n\t\tlimit_conn_zone $server_name zone=perserver:10m;"
) + nginx_conf[tmp_res.end():]
else:
# 通过检查第一个server的位置
rep_first_server = re.compile(r"http\s*\{(.*\n)*\s*server\s*\{")
tmp_res = rep_first_server.search(nginx_conf)
if tmp_res:
old_http_conf = tmp_res.group()
# 在第一个server项前添加
server_idx = old_http_conf.rfind("server")
new_http_conf = old_http_conf[:server_idx] + (
"\n\t\tlimit_conn_zone $binary_remote_addr zone=perip:10m;"
"\n\t\tlimit_conn_zone $server_name zone=perserver:10m;\n"
) + old_http_conf[server_idx:]
new_conf = rep_first_server.sub(new_http_conf, nginx_conf, 1)
else:
# 在没有配置其他server项目时通过检查include server项目检查
# 通检查 include /www/server/panel/vhost/nginx/*.conf; 位置
rep_include = re.compile(r"http\s*\{(.*\n)*\s*include +/www/server/panel/vhost/nginx/\*\.conf;")
tmp_res = rep_include.search(nginx_conf)
if not tmp_res:
return False, "The global configuration cache configuration failed"
old_http_conf = tmp_res.group()
include_idx = old_http_conf.rfind("include ")
new_http_conf = old_http_conf[:include_idx] + (
"\n\t\tlimit_conn_zone $binary_remote_addr zone=perip:10m;"
"\n\t\tlimit_conn_zone $server_name zone=perserver:10m;\n"
) + old_http_conf[include_idx:]
new_conf = rep_first_server.sub(new_http_conf, nginx_conf, 1)
public.writeFile(nginx_conf_file, new_conf)
if public.checkWebConfig() is not True: # 检测失败,无法添加
public.writeFile(nginx_conf_file, nginx_conf)
return False, "The global configuration cache configuration failed"
return True, ""
# 设置流量限制
def set_limit_net(self, get):
if public.get_webserver() != 'nginx':
return public.returnMsg(False, 'SITE_NETLIMIT_ERR')
try:
site_id = int(get.site_id)
per_server = int(get.perserver)
perip = int(get.perip)
limit_rate = int(get.limit_rate)
except (AttributeError, TypeError, ValueError):
return public.returnMsg(False, "Parameter error")
if per_server < 1 or perip < 1 or limit_rate < 1:
return public.returnMsg(False, '并发限制IP限制流量限制必需大于0')
# 取配置文件
site_info = public.M('sites').where("id=?", (site_id,)).find()
if not isinstance(site_info, dict):
return public.returnMsg(False, "站点信息查询错误")
else:
site_name = site_info["name"]
filename = "{}/vhost/nginx/{}{}.conf".format(self.setup_path, self.config_prefix, site_name)
site_conf: str = public.readFile(filename)
if not isinstance(site_conf, str):
return public.returnMsg(False, "配置文件读取错误")
flag, msg = self._set_nginx_conf_limit()
if not flag:
return public.returnMsg(False, msg)
per_server_str = ' limit_conn perserver {};'.format(per_server)
perip_str = ' limit_conn perip {};'.format(perip)
limit_rate_str = ' limit_rate {}k;'.format(limit_rate)
# 请求并发限制
new_conf = site_conf
ssl_end_res = re.search(r"#error_page 404/404.html;[^\n]*\n", new_conf)
if ssl_end_res is None:
return public.returnMsg(False, "未定位到SSL的相关配置添加失败")
ssl_end_idx = ssl_end_res.end()
rep_limit_rate = re.compile(r"(.*)limit_rate +(\d+)\w+ *; *", re.M)
tmp_res = rep_limit_rate.search(new_conf)
if tmp_res is not None :
new_conf = rep_limit_rate.sub(limit_rate_str, new_conf)
else:
new_conf = new_conf[:ssl_end_idx] + limit_rate_str + "\n" + new_conf[ssl_end_idx:]
# IP并发限制
rep_per_ip = re.compile(r"(.*)limit_conn +perip +(\d+) *; *", re.M)
tmp_res = rep_per_ip.search(new_conf)
if tmp_res is not None:
new_conf = rep_per_ip.sub(perip_str, new_conf)
else:
new_conf = new_conf[:ssl_end_idx] + perip_str + "\n" + new_conf[ssl_end_idx:]
rep_per_server = re.compile(r"(.*)limit_conn +perserver +(\d+) *; *", re.M)
tmp_res = rep_per_server.search(site_conf)
if tmp_res is not None:
new_conf = rep_per_server.sub(per_server_str, new_conf)
else:
new_conf = new_conf[:ssl_end_idx] + per_server_str + "\n" + new_conf[ssl_end_idx:]
public.writeFile(filename, new_conf)
is_error = public.checkWebConfig()
if is_error is not True:
public.writeFile(filename, site_conf)
return public.returnMsg(False, 'ERROR:<br><a style="color:red;">' + is_error.replace("\n", '<br>') + '</a>')
public.serviceReload()
public.WriteLog('TYPE_SITE', 'SITE_NETLIMIT_OPEN_SUCCESS', (site_name,))
return public.returnMsg(True, 'Successfully set')
# 关闭流量限制
def close_limit_net(self, get):
if public.get_webserver() != 'nginx':
return public.returnMsg(False, 'SITE_NETLIMIT_ERR')
if self.config_prefix is None:
return public.returnMsg(False, "不支持的网站类型")
try:
site_id = int(get.site_id)
except (AttributeError, TypeError, ValueError):
return public.returnMsg(False, "Parameter error")
# 取回配置文件
site_info = public.M('sites').where("id=?", (site_id,)).find()
if not isinstance(site_info, dict):
return public.returnMsg(False, "站点信息查询错误")
else:
site_name = site_info["name"]
filename = "{}/vhost/nginx/{}{}.conf".format(self.setup_path, self.config_prefix, site_name)
site_conf = public.readFile(filename)
if not isinstance(site_conf, str):
return public.returnMsg(False, "配置文件读取错误")
# 清理总并发
rep_limit_rate = re.compile(r"(.*)limit_rate +(\d+)\w+ *; *\n?", re.M)
rep_per_ip = re.compile(r"(.*)limit_conn +perip +(\d+) *; *\n?", re.M)
rep_per_server = re.compile(r"(.*)limit_conn +perserver +(\d+) *; *\n?", re.M)
new_conf = site_conf
new_conf = rep_limit_rate.sub("", new_conf, 1)
new_conf = rep_per_ip.sub("", new_conf, 1)
new_conf = rep_per_server.sub("", new_conf, 1)
public.writeFile(filename, new_conf)
is_error = public.checkWebConfig()
if is_error is not True:
public.writeFile(filename, site_conf)
return public.returnMsg(False, 'ERROR:<br><a style="color:red;">' + is_error.replace("\n", '<br>') + '</a>')
public.serviceReload()
public.WriteLog('TYPE_SITE', 'SITE_NETLIMIT_CLOSE_SUCCESS', (site_name,))
return public.returnMsg(True, 'SITE_NETLIMIT_CLOSE_SUCCESS')

View File

@@ -0,0 +1,855 @@
import os
import re
import json
import sys
from typing import Tuple, Optional, Union, List
from .util import webserver, check_server_config, write_file, read_file, DB, service_reload, get_log_path, pre_re_key
from mod.base import json_response
class _BaseLogFormat:
panel_path = "/www/server/panel"
def __init__(self):
self._config_file = ""
self._config: Optional[dict] = None
self._format_dict = None
self._log_format_dir = ''
@property
def config(self) -> dict:
if self._config is None:
try:
self._config = json.loads(read_file(self._config_file))
except (json.JSONDecodeError, TypeError, ValueError):
self._config = {}
return self._config
def save_config(self):
if self._config is not None:
write_file(self._config_file, json.dumps(self._config))
@property
def log_format(self) -> dict:
raise NotImplementedError()
def check_config(self, name: str, keys: List[str], space_character=None) -> Optional[str]:
if space_character and len(space_character) > 4:
return "间隔符过长请输入小于4位的间隔符"
rep_name = re.compile(r"^\w+$")
if rep_name.match(name) is None:
return "名称只能包含数字、字母和下划线"
if name in ("combined", "main"):
return "请勿使用默认名称"
error_key = []
for k in keys:
if k not in self.log_format:
error_key.append(k)
if error_key:
return "无法识别以下日志关键字:【{}".format(",".join(error_key))
# 添加日志格式
def add_log_format(self, name: str, keys: List[str], space_character=" ") -> Optional[str]:
error_msg = self.check_config(name, keys, space_character)
if error_msg:
return error_msg
if name in self.config:
return "该名称的日志格式已存在"
error_msg = self._set_to_config(name, keys, space_character, is_modify=False)
if error_msg:
return error_msg
self.config[name] = {"keys": keys, "space_character": space_character, "sites": []}
self.save_config()
service_reload()
return None
# 修改日志格式
def modify_log_format(self, name: str, keys: List[str], space_character=None) -> Optional[str]:
error_msg = self.check_config(name, keys, space_character)
if error_msg:
return error_msg
if name not in self.config:
return "该名称的日志格式不存在"
self.config[name]["keys"] = keys
if space_character:
self.config[name]["space_character"] = space_character
else:
space_character = self.config[name]["space_character"]
error_msg = self._set_to_config(name, keys, space_character, is_modify=True)
if error_msg:
return error_msg
self.save_config()
service_reload()
return None
# 删除日志格式
def remove_log_format(self, name: str) -> Optional[str]:
if name not in self.config:
return "该名称的日志格式不存在"
if len(self.config[name].get("sites", [])) > 1:
return "该日志格式在【{}】网站中正在使用,请先移除".format(",".join(self.config[name]["sites"]))
self._remove_form_config(name)
del self.config[name]
self.save_config()
service_reload()
return None
def _set_to_config(self, name: str, keys: List[str], space_character, is_modify=False) -> Optional[str]:
raise NotImplementedError
def _remove_form_config(self, name) -> None:
conf_file = self._log_format_dir + "/{}_format.conf".format(name)
if os.path.isfile(conf_file):
os.remove(conf_file)
# 在配置文件中设置日志格式, log_format_name传入空字符串时设置会默认
def set_site_log_format_in_config(self, site_name, log_format_name, conf_prefix, mutil=False) -> Optional[str]:
"""
mutil 为True时不会自动重载配置
"""
raise NotImplementedError()
# 设置日志格式
def set_site_log_format(self, site_name, log_format_name, conf_prefix, mutil=False) -> Optional[str]:
if log_format_name not in self.config and log_format_name != "":
return "该名称的日志格式不存在"
error_msg = self.set_site_log_format_in_config(site_name, log_format_name, conf_prefix, mutil=mutil)
if error_msg is not None:
return error_msg
if "sites" not in self.config[log_format_name]:
self.config[log_format_name]["sites"] = []
for name, sub_conf in self.config.items():
if name == log_format_name:
sub_conf["sites"].append(site_name) # 记录到配置文件中
if site_name in sub_conf.get("sites", []):
sub_conf["sites"].remove(site_name) # 如果之前使用了其他的配置,则移除其他配置中的这个站点的关联
self.save_config()
class _NgLog(_BaseLogFormat):
@property
def log_format(self) -> dict:
if self._format_dict is None:
self._format_dict = {
"server_addr": {
"name": "服务器地址",
"key": "$server_addr",
},
"server_port": {
"name": "服务器端口",
"key": "$server_port",
},
"host": {
"name": "域名",
"key": "$http_host",
},
"remote_addr": {
"name": "客户端地址",
"key": "$server_addr",
},
"remote_port": {
"name": "客户端端口",
"key": "$server_addr",
},
"protocol": {
"name": "服务器协议",
"key": "$server_protocol",
},
"req_length": {
"name": "请求长度",
"key": "$request_length",
},
"method": {
"name": "请求方法",
"key": "$request_method",
},
"uri": {
"name": "请求uri",
"key": "$request_uri",
},
"status": {
"name": "状态码",
"key": "$status",
},
"sent_bytes": {
"name": "发送字节数",
"key": "$body_bytes_sent",
},
"referer": {
"name": "来源地址",
"key": "$http_referer",
},
"user_agent": {
"name": "用户代理(User-Agent)",
"key": "$http_user_agent",
},
"take_time": {
"name": "请求用时",
"key": "$request_time",
},
}
return self._format_dict
def __init__(self):
super().__init__()
self._config_file = "{}/data/ng_log_format.json".format(self.panel_path)
self._log_format_dir = "{}/vhost/nginx/log_format".format(self.panel_path)
def _set_log_format_include(self) -> Optional[str]:
config_file = "/www/server/nginx/conf/nginx.conf"
config_data = read_file(config_file)
if not config_data:
return "配置文件丢失无法操作"
if not os.path.isdir(self._log_format_dir):
os.makedirs(self._log_format_dir)
rep_include = re.compile(r"include\s+/www/server/panel/vhost/nginx/log_format/\*\.conf\s*;")
if rep_include.search(config_data):
return
rep_http = re.compile(r"\s*http\s*\{[^\n]*\n")
res = rep_http.search(config_data)
if not res:
return "主配置文件中缺少http配置项无法添加"
include_str = "include {}/*.conf;\n".format(self._log_format_dir)
new_conf = config_data[:res.end()] + include_str + config_data[res.end():]
write_file(config_file, new_conf)
def _set_to_config(self, name: str, keys: List[str], space_character, is_modify=False) -> Optional[str]:
error_msg = self._set_log_format_include()
if error_msg:
return error_msg
conf_file = self._log_format_dir + "/{}_format.conf".format(name)
write_file(conf_file, (
"log_format {} '{}';".format(name, space_character.join(map(lambda x: self.log_format[x]["key"], keys)))
))
def set_site_log_format_in_config(self, site_name, log_format_name, conf_prefix, mutil=False) -> Optional[str]:
"""
mutil 为True时不会自动重载配置
"""
config_file = "{}/vhost/nginx/{}{}.conf".format(self.panel_path, conf_prefix, site_name)
config_data = read_file(config_file)
if not config_data:
return "配置文件丢失无法操作"
start_idx, end_idx = self.get_first_server_log_idx(config_data)
if start_idx:
rep_access_log = re.compile(r"\s*access_log\s+(?P<path>[^;\s]*)(\s+(?P<name>\w+))?;")
res = rep_access_log.search(config_data[start_idx: end_idx])
if res.group("name") == log_format_name:
return
new_access_log = "\n access_log {} {};".format(res.group("path"), log_format_name)
new_conf = config_data[:start_idx] + new_access_log + config_data[end_idx:]
else:
last_server_idx = config_data.rfind("}") # server 范围内最后一个}的位置
if last_server_idx == -1:
return "配置文件格式错误无法操作"
log_path = "{}/{}.log".format(get_log_path(), site_name)
new_access_log = "\n access_log {} {};\n".format(log_path, log_format_name)
new_conf = config_data[:last_server_idx] + new_access_log + config_data[last_server_idx:]
write_file(config_file, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(config_file, config_data)
return "配置修改失败"
if webserver() == "nginx" and not mutil:
service_reload()
# 获取配置文件中server等级的第一个access_log的位置
@staticmethod
def get_first_server_log_idx(config_data) -> Tuple[Optional[int], Optional[int]]:
rep_server = re.compile(r"\s*server\s*\{")
res = rep_server.search(config_data)
if res is None:
return None, None
rep_log = re.compile(r"\s*access_log\s+(?P<path>[^;\s]*)(\s+(?P<name>\w+))?;", re.M)
s_idx = res.end()
l_n = 1
length = len(config_data)
while l_n > 0:
next_l = config_data[s_idx:].find("{")
next_r = config_data[s_idx:].find("}")
if next_l == -1 and next_r == -1: # 都没有了跳过
return None, None
if next_r == -1 and next_l != -1: # 还剩 { 但是没有 } ,跳过
return None, None
if next_l == -1:
next_l = length
if next_l < next_r:
if l_n == 1:
res = rep_log.search(config_data[s_idx: s_idx + next_l])
if res:
return s_idx + res.start(), s_idx + res.end()
l_n += 1
else:
l_n -= 1
if l_n == 0:
res = rep_log.search(config_data[s_idx: s_idx + next_l])
if res:
return s_idx + res.start(), s_idx + res.end()
s_idx += min(next_l, next_r) + 1
return None, None
# 设置站点的日志路径
def set_site_log_path(self, site_name, site_log_path, conf_prefix, mutil=False) -> Optional[str]:
if not os.path.isdir(site_log_path):
return "不是一个存在的文件夹路径"
if site_log_path[-1] == "/":
site_log_path = site_log_path[:-1]
# nginx
nginx_config_path = '/www/server/panel/vhost/nginx/{}{}.conf'.format(conf_prefix, site_name)
nginx_config = read_file(nginx_config_path)
if not nginx_config:
return "网站配置文件丢失,无法配置"
# nginx
old_log_file = self.nginx_get_log_file_path(nginx_config, site_name, is_error_log=False)
old_error_log_file = self.nginx_get_log_file_path(nginx_config, site_name, is_error_log=True)
if old_log_file and old_error_log_file:
new_nginx_conf = nginx_config
log_file_rep = re.compile(r"access_log +" + pre_re_key(old_log_file))
error_log_file_rep = re.compile(r"error_log +" + pre_re_key(old_error_log_file))
if log_file_rep.search(nginx_config):
new_nginx_conf = log_file_rep.sub("access_log {}/{}.log".format(site_log_path, site_name),
new_nginx_conf, 1)
if error_log_file_rep.search(nginx_config):
new_nginx_conf = error_log_file_rep.sub("error_log {}/{}.error.log".format(site_log_path, site_name),
new_nginx_conf, 1)
write_file(nginx_config_path, new_nginx_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(nginx_config_path, nginx_config)
return "配置修改失败"
if webserver() == "nginx" and not mutil:
service_reload()
else:
return "未找到日志配置,无法操作"
@staticmethod
def nginx_get_log_file_path(nginx_config: str, site_name: str, is_error_log: bool = False):
log_file = None
if is_error_log:
re_data = re.findall(r"error_log +(/(\S+/?)+) ?(.*?);", nginx_config)
else:
re_data = re.findall(r"access_log +(/(\S+/?)+) ?(.*?);", nginx_config)
if re_data is None:
log_file = None
else:
for i in re_data:
file_path = i[0].strip(";")
if file_path != "/dev/null" and not file_path.endswith("purge_cache.log"):
if os.path.isdir(os.path.dirname(file_path)):
log_file = file_path
break
logsPath = '/www/wwwlogs/'
if log_file is None:
if is_error_log:
log_file = logsPath + site_name + '.log'
else:
log_file = logsPath + site_name + '.error.log'
if not os.path.isfile(log_file):
log_file = None
return log_file
def get_site_log_path(self, site_name, conf_prefix) -> Union[str, dict]:
config_path = '/www/server/panel/vhost/nginx/{}{}.conf'.format(conf_prefix, site_name)
config = read_file(config_path)
if not config:
return "站点配置文件丢失"
log_file = self.nginx_get_log_file_path(config, site_name, is_error_log=False)
error_log_file = self.nginx_get_log_file_path(config, site_name, is_error_log=False)
if not (error_log_file and log_file):
return "获取失败"
return {
"log_file": log_file,
"error_log_file": error_log_file,
}
def close_access_log(self, site_name, conf_prefix) -> Optional[str]:
nginx_config_path = '/www/server/panel/vhost/nginx/{}{}.conf'.format(conf_prefix, site_name)
nginx_config = read_file(nginx_config_path)
if not nginx_config:
return "网站配置文件丢失,无法配置"
start_idx, end_idx = self.get_first_server_log_idx(nginx_config)
if not start_idx:
return None
new_conf = nginx_config
while start_idx is not None:
new_conf = new_conf[:start_idx] + '# ' + new_conf[start_idx:]
start_idx, end_idx = self.get_first_server_log_idx(new_conf)
write_file(nginx_config_path, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(nginx_config_path, nginx_config)
return "配置修改失败"
return None
# 未完成
def open_access_log(self, site_name, conf_prefix) -> Optional[str]:
nginx_config_path = '/www/server/panel/vhost/nginx/{}{}.conf'.format(conf_prefix, site_name)
nginx_config = read_file(nginx_config_path)
if not nginx_config:
return "网站配置文件丢失,无法配置"
new_conf = nginx_config.replace("#")
write_file(nginx_config_path, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(nginx_config_path, nginx_config)
return "配置修改失败"
return None
def access_log_is_open(self, site_name, conf_prefix) -> bool:
nginx_config_path = '/www/server/panel/vhost/nginx/{}{}.conf'.format(conf_prefix, site_name)
nginx_config = read_file(nginx_config_path)
if not nginx_config:
return False
start_idx, end_idx = self.get_first_server_log_idx(nginx_config)
return start_idx is not None
class _ApLog(_BaseLogFormat):
def set_site_log_format_in_config(self, site_name, log_format_name, conf_prefix, mutil=False) -> Optional[str]:
if log_format_name == "":
log_format_name = "combined"
config_file = "{}/vhost/apache/{}{}.conf".format(self.panel_path, conf_prefix, site_name)
config_data = read_file(config_file)
if not config_data:
return "配置文件丢失无法操作"
custom_log_rep = re.compile(r'''\s*CustomLog\s+['"](?P<path>.*)['"](\s+(?P<name>.*))?''', re.M)
new_custom_log = '\n CustomLog "{}" %s\n' % log_format_name
new_conf_list = []
idx = 0
for tmp_res in custom_log_rep.finditer(config_data):
new_conf_list.append(config_data[idx:tmp_res.start()])
new_conf_list.append(new_custom_log.format(tmp_res.group("path")))
idx = tmp_res.end()
new_conf_list.append(config_data[idx:])
new_conf = "".join(new_conf_list)
write_file(config_file, new_conf)
if webserver() == "apache" and check_server_config() is not None:
write_file(config_file, config_data)
return "配置修改失败"
if webserver() == "apache" and not mutil:
service_reload()
# 设置站点的日志路径
def set_site_log_path(self, site_name, site_log_path, conf_prefix, mutil=False) -> Optional[str]:
if not os.path.isdir(site_log_path):
return "不是一个存在的文件夹路径"
if site_log_path[-1] == "/":
site_log_path = site_log_path[:-1]
# apache
apache_config_path = '/www/server/panel/vhost/apache/{}{}.conf'.format(conf_prefix, site_name)
apache_config = read_file(apache_config_path)
if not apache_config:
return "网站配置文件丢失,无法配置"
# apache
old_log_file = self.apache_get_log_file_path(apache_config, site_name, is_error_log=False)
old_error_log_file = self.apache_get_log_file_path(apache_config, site_name, is_error_log=True)
if old_log_file and old_error_log_file:
new_apache_conf = apache_config
log_file_rep = re.compile(r'''CustomLog +['"]?''' + pre_re_key(old_log_file) + '''['"]?''')
error_log_file_rep = re.compile(r'''ErrorLog +['"]?''' + pre_re_key(old_error_log_file) + '''['"]?''')
if log_file_rep.search(apache_config):
new_apache_conf = log_file_rep.sub('CustomLog "{}/{}-access_log"'.format(site_log_path, site_name),
new_apache_conf)
if error_log_file_rep.search(apache_config):
new_apache_conf = error_log_file_rep.sub('ErrorLog "{}/{}.-error_log"'.format(site_log_path, site_name),
new_apache_conf)
write_file(apache_config_path, new_apache_conf)
print(new_apache_conf)
if webserver() == "apache" and check_server_config() is not None:
write_file(apache_config_path, apache_config)
return "配置修改失败"
if webserver() == "apache" and not mutil:
service_reload()
else:
return "未找到日志配置,无法操作"
@staticmethod
def apache_get_log_file_path(apache_config: str, site_name: str, is_error_log: bool = False):
log_file = None
if is_error_log:
re_data = re.findall(r'''ErrorLog +['"]?(/(\S+/?)+)['"]? ?(.*?)\n''', apache_config)
else:
re_data = re.findall(r'''CustomLog +['"]?(/(\S+/?)+)['"]? ?(.*?)\n''', apache_config)
if re_data is None:
log_file = None
else:
for i in re_data:
file_path = i[0].strip('"').strip("'")
if file_path != "/dev/null":
if os.path.isdir(os.path.dirname(file_path)):
log_file = file_path
break
logsPath = '/www/wwwlogs/'
if log_file is None:
if is_error_log:
log_file = logsPath + site_name + '-access_log'
else:
log_file = logsPath + site_name + '-error_log'
if not os.path.isfile(log_file):
log_file = None
return log_file
@staticmethod
def close_access_log(site_name, conf_prefix) -> Optional[str]:
apache_config_path = '/www/server/panel/vhost/apache/{}{}.conf'.format(conf_prefix, site_name)
apache_config = read_file(apache_config_path)
if not apache_config:
return "网站配置文件丢失,无法配置"
custom_log_rep = re.compile(r'''CustomLog +['"]?(/(\S+/?)+)['"]?(\s*.*)?''', re.M)
new_conf_list = []
idx = 0
for tmp_res in custom_log_rep.finditer(apache_config):
new_conf_list.append(apache_config[idx:tmp_res.start()])
new_conf_list.append("# " + tmp_res.group())
idx = tmp_res.end()
new_conf_list.append(apache_config[idx:])
new_conf = "".join(new_conf_list)
write_file(apache_config_path, new_conf)
if webserver() == "apache" and check_server_config() is not None:
write_file(apache_config_path, apache_config)
return "配置修改失败"
return None
@staticmethod
def open_access_log(site_name, conf_prefix) -> Optional[str]:
apache_config_path = '/www/server/panel/vhost/apache/{}{}.conf'.format(conf_prefix, site_name)
apache_config = read_file(apache_config_path)
if not apache_config:
return "网站配置文件丢失,无法配置"
new_conf = apache_config.replace("#CustomLog", "CustomLog")
write_file(apache_config_path, new_conf)
if webserver() == "apache" and check_server_config() is not None:
write_file(apache_config_path, apache_config)
return "配置修改失败"
return None
@staticmethod
def access_log_is_open(site_name, conf_prefix) -> bool:
apache_config_path = '/www/server/panel/vhost/apache/{}{}.conf'.format(conf_prefix, site_name)
apache_config = read_file(apache_config_path)
if not apache_config:
return False
if apache_config.find("#CustomLog") != -1:
return False
return True
def get_site_log_path(self, site_name, conf_prefix) -> Union[str, dict]:
config_path = '/www/server/panel/vhost/apache/{}{}.conf'.format(conf_prefix, site_name)
config = read_file(config_path)
if not config:
return "站点配置文件丢失"
log_file = self.apache_get_log_file_path(config, site_name, is_error_log=False)
error_log_file = self.apache_get_log_file_path(config, site_name, is_error_log=False)
if not (error_log_file and log_file):
return "获取失败"
return {
"log_file": log_file,
"error_log_file": error_log_file,
}
@property
def log_format(self) -> dict:
if self._format_dict is None:
self._format_dict = {
"server_addr": {
"name": "服务器地址",
"key": "%A",
},
"server_port": {
"name": "服务器端口",
"key": "%p",
},
"host": {
"name": "域名",
"key": "%V",
},
"remote_addr": {
"name": "客户端地址",
"key": "%{c}a",
},
"remote_port": {
"name": "客户端端口",
"key": "%{remote}p",
},
"protocol": {
"name": "服务器协议",
"key": "%H",
},
"method": {
"name": "请求方法",
"key": "%m",
},
"uri": {
"name": "请求uri",
"key": r"\"%U\"",
},
"status": {
"name": "状态码",
"key": "%>s",
},
"sent_bytes": {
"name": "发送字节数",
"key": "%B",
},
"referer": {
"name": "来源地址",
"key": r"\"%{Referer}i\"",
},
"user_agent": {
"name": "用户代理(User-Agent)",
"key": r"\"%{User-Agent}i\"",
},
"take_time": {
"name": "请求用时",
"key": "%{ms}T",
},
}
return self._format_dict
def __init__(self):
super().__init__()
self._config_file = "{}/data/ap_log_format.json".format(self.panel_path)
self._log_format_dir = "{}/vhost/apache/log_format".format(self.panel_path)
def _set_log_format_include(self) -> Optional[str]:
config_file = "/www/server/apache/conf/httpd.conf"
config_data = read_file(config_file)
if not config_data:
return "配置文件丢失无法操作"
if not os.path.isdir(self._log_format_dir):
os.makedirs(self._log_format_dir)
rep_include = re.compile(r"IncludeOptional\s+/www/server/panel/vhost/apache/log_format/\*\.conf")
if rep_include.search(config_data):
return
new_conf = config_data + """
<IfModule log_config_module>
IncludeOptional /www/server/panel/vhost/apache/log_format/*.conf
</IfModule>
"""
write_file(config_file, new_conf)
def _set_to_config(self, name: str, keys: List[str], space_character, is_modify=False) -> Optional[str]:
error_msg = self._set_log_format_include()
if error_msg:
return error_msg
conf_file = self._log_format_dir + "/{}_format.conf".format(name)
write_file(conf_file, (
'LogFormat "{}" {}'.format(space_character.join(map(lambda x: self.log_format[x]["key"], keys)), name)
))
class RealLogMgr:
def __init__(self, conf_prefix: str = ""):
self.conf_prefix = conf_prefix
if webserver() == "nginx":
self._log_format_tool = _NgLog()
else:
self._log_format_tool = _ApLog()
@staticmethod
def remove_site_log_format_info(site_name: str):
for logtool in (_NgLog(), _ApLog()):
for _, conf in logtool.config.items():
if site_name in conf.get("sites", []):
conf["sites"].remove(site_name)
logtool.save_config()
def log_format_data(self, site_name: str):
log_format_data = None
for name, data in self._log_format_tool.config.items():
if site_name in data.get("sites", []):
log_format_data = data
log_format_data.update(name=name)
return {
"log_format": log_format_data,
"rule": self._log_format_tool.log_format,
"all_log_format": self._log_format_tool.config
}
def add_log_format(self, name: str, keys: List[str], space_character=" ") -> Optional[str]:
return self._log_format_tool.add_log_format(name, keys, space_character)
def modify_log_format(self, name: str, keys: List[str], space_character=None) -> Optional[str]:
return self._log_format_tool.modify_log_format(name, keys, space_character)
def remove_log_format(self, name: str) -> Optional[str]:
return self._log_format_tool.remove_log_format(name)
# log_format_name 为空字符串时表示恢复成默认的日志格式
def set_site_log_format(self, site_name, log_format_name, mutil=False) -> Optional[str]:
return self._log_format_tool.set_site_log_format(site_name, log_format_name, self.conf_prefix, mutil)
def set_site_log_path(self, site_name, site_log_path, mutil=False) -> Optional[str]:
return self._log_format_tool.set_site_log_path(site_name, site_log_path, self.conf_prefix, mutil)
def get_site_log_path(self, site_name) -> Union[str, dict]:
return self._log_format_tool.get_site_log_path(site_name, self.conf_prefix)
@staticmethod
def site_crontab_log(site_name: str, hour: int, minute: int, save: int) -> bool:
if DB("crontab").where("sName =? and sType = ?", ("ALL", "logs")).find():
return True
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import crontab
crontabs = crontab.crontab()
args = {
"name": "切割日志[{}]".format(site_name),
"type": 'day',
"where1": '',
"hour": hour,
"minute": minute,
"sName": site_name,
"sType": 'logs',
"notice": '',
"notice_channel": '',
"save": save,
"save_local": '1',
"backupTo": '',
"sBody": '',
"urladdress": ''
}
res = crontabs.AddCrontab(args)
if res and "id" in res.keys():
return True
return False
class LogMgr:
def __init__(self, conf_prefix: str = ""):
self.conf_prefix = conf_prefix
self._real_log_mgr = RealLogMgr(self.conf_prefix)
def log_format_data(self, get):
try:
site_name = get.site_name.strip()
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
data = self._real_log_mgr.log_format_data(site_name)
return json_response(status=True, data=data)
def add_log_format(self, get):
try:
space_character = " "
format_name = get.format_name.strip()
keys = json.loads(get.keys.strip())
if "space_character" in get:
space_character = get.space_character
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.add_log_format(format_name, keys, space_character)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, msg="添加成功")
def modify_log_format(self, get):
try:
space_character = None
format_name = get.format_name.strip()
keys = json.loads(get.keys.strip())
if "space_character" in get:
space_character = get.space_character
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.modify_log_format(format_name, keys, space_character)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, msg="修改成功")
def remove_log_format(self, get):
try:
format_name = get.format_name.strip()
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.remove_log_format(format_name)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, msg="Successfully delete")
def set_site_log_format(self, get):
try:
format_name = get.format_name.strip()
site_name = get.site_name.strip()
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.set_site_log_format(site_name, log_format_name=format_name)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, msg="添加成功")
def set_site_log_path(self, get):
try:
log_path = get.log_path.strip()
site_name = get.site_name.strip()
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.set_site_log_path(site_name, site_log_path=log_path)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, msg="修改路径成功")
def get_site_log_path(self, get):
try:
site_name = get.site_name.strip()
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.get_site_log_path(site_name)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, data=msg)
def site_crontab_log(self, get):
try:
site_name = get.site_name.strip()
hour = int(get.hour.strip())
minute = int(get.minute.strip())
save = int(get.save.strip())
except (AttributeError, json.JSONDecodeError, TypeError, ValueError):
return json_response(status=False, msg="参数类型错误")
msg = self._real_log_mgr.site_crontab_log(site_name, hour=hour, minute=minute, save=save)
if isinstance(msg, str):
return json_response(status=False, msg=msg)
return json_response(status=True, data=msg)

View File

@@ -0,0 +1,90 @@
import ipaddress
import os
from typing import Optional, List, Dict, Any
from .util import webserver, check_server_config, write_file, read_file, service_reload
class NginxRealIP:
def __init__(self):
pass
def set_real_ip(self, site_name: str, ip_header: str, allow_ip: List[str], recursive: bool = False) -> Optional[
str]:
if not webserver() == 'nginx':
return "only nginx web server is supported"
res = check_server_config()
if res:
return "conifg error, please fix it first. ERROR: %s".format(res)
self._set_ext_real_ip_file(site_name, status=True, ip_header=ip_header, allow_ip=allow_ip, recursive=recursive)
res = check_server_config()
if res:
self._set_ext_real_ip_file(site_name, status=False, ip_header="", allow_ip=[], recursive=False)
return "配置失败:{}".format(res)
else:
service_reload()
def close_real_ip(self, site_name: str):
self._set_ext_real_ip_file(site_name, status=False, ip_header="", allow_ip=[], recursive=False)
service_reload()
return
def get_real_ip(self, site_name: str) -> Dict[str, Any]:
return self._read_ext_real_ip_file(site_name)
def _set_ext_real_ip_file(self, site_name: str, status: bool, ip_header: str, allow_ip: List[str],
recursive: bool = False):
ext_file = "/www/server/panel/vhost/nginx/extension/{}/proxy_real_ip.conf".format(site_name)
if not status:
if os.path.exists(ext_file):
os.remove(ext_file)
return
if not os.path.exists(os.path.dirname(ext_file)):
os.makedirs(os.path.dirname(ext_file))
real_ip_from = ""
for ip in allow_ip:
tmp_ip = self.formatted_ip(ip)
if tmp_ip:
real_ip_from += " set_real_ip_from {};\n".format(ip)
if not real_ip_from:
real_ip_from = "set_real_ip_from 0.0.0.0/0;\nset_real_ip_from ::/0;\n"
conf_data = "{}real_ip_header {};\nreal_ip_recursive {};\n".format(
real_ip_from, ip_header, "on" if recursive else "off"
)
write_file(ext_file, conf_data)
@staticmethod
def _read_ext_real_ip_file(site_name: str) -> Dict[str, Any]:
ret = {
"ip_header": "",
"allow_ip": [],
"recursive": False
}
ext_file = "/www/server/panel/vhost/nginx/extension/{}/proxy_real_ip.conf".format(site_name)
if os.path.exists(ext_file):
data = read_file(ext_file)
if data:
for line in data.split("\n"):
line = line.strip("; ")
if line.startswith("real_ip_header"):
ret["ip_header"] = line.split()[1]
elif line.startswith("set_real_ip_from"):
ret["allow_ip"].append(line.split()[1])
elif line.startswith("real_ip_recursive"):
ret["recursive"] = True if line.split()[1] == "on" else False
return ret
@staticmethod
def formatted_ip(ip: str) -> str:
try:
ip = ipaddress.ip_address(ip)
return ip.compressed
except:
try:
ip = ipaddress.ip_network(ip)
return ip.compressed
except:
pass
return ""

639
mod/base/web_conf/proxy.py Normal file
View File

@@ -0,0 +1,639 @@
import os
import re
import json
import shutil
import warnings
import sys
import traceback
from hashlib import md5
from typing import Tuple, Optional, Union, List, Dict, Any
from .util import webserver, check_server_config, write_file, read_file, DB, service_reload, get_log_path, pre_re_key
from mod.base import json_response
warnings.filterwarnings("ignore", category=SyntaxWarning)
class RealProxy:
panel_path = "/www/server/panel"
_proxy_conf_file = "{}/data/mod_proxy_file.conf".format(panel_path)
def __init__(self, config_prefix: str):
self.config_prefix: str = config_prefix
self._config: Optional[List[dict]] = None
# {
# "proxyname": "yyy",
# "sitename": "www.12345test.com",
# "proxydir": "/",
# "proxysite": "http://www.baidu.com",
# "todomain": "www.baidu.com",
# "type": 0,
# "cache": 0,
# "subfilter": [
# {"sub1": "", "sub2": ""},
# {"sub1": "", "sub2": ""},
# {"sub1": "", "sub2": ""}],
# "advanced": 1,
# "cachetime": 1
# }
@property
def config(self) -> List[dict]:
if self._config is None:
try:
self._config = json.loads(read_file(self._proxy_conf_file))
except (json.JSONDecodeError, TypeError, ValueError):
self._config = []
return self._config
def save_config(self):
if self._config is not None:
write_file(self._proxy_conf_file, json.dumps(self._config))
# 检查代理是否存在
def _check_even(self, proxy_conf: dict, is_modify) -> bool:
for i in self.config:
if i["sitename"] == proxy_conf["sitename"]:
if is_modify is False:
if i["proxydir"] == proxy_conf["proxydir"] or i["proxyname"] == proxy_conf["proxyname"]:
return True
else:
if i["proxyname"] != proxy_conf["proxyname"] and i["proxydir"] == proxy_conf["proxydir"]:
return True
# 检测全局代理和目录代理是否同时存在
def _check_proxy_even(self, proxy_conf: dict, is_modify) -> bool:
n = 0
if is_modify:
for i in self.config:
if i["sitename"] == proxy_conf["sitename"]:
n += 1
if n == 1:
return False
for i in self.config:
if i["sitename"] == proxy_conf["sitename"]:
if i["advanced"] != proxy_conf["advanced"]:
return True
return False
def check_args(self, get, is_modify=False) -> Union[str, dict]:
if check_server_config():
return '配置文件出错请先排查配置'
data = {
"advanced": 0,
"proxydir": "",
"cache": 0,
"cachetime": 1,
"type": 0,
"todomain": "$host",
}
try:
data["proxyname"] = get.proxyname.strip()
data["sitename"] = get.sitename.strip()
if "proxydir" in get:
data["proxydir"] = get.proxydir.strip()
data["proxysite"] = get.proxysite.strip()
if "todomain" in get:
data["todomain"] = get.todomain.strip()
data["type"] = int(get.type.strip())
data["cache"] = int(get.cache.strip())
data["subfilter"] = json.loads(get.subfilter.strip())
data["advanced"] = int(get.advanced.strip())
data["cachetime"] = int(get.cachetime.strip())
except:
return "Parameter error"
if is_modify is False:
if len(data["proxyname"]) < 3 or len(data["proxyname"]) > 40:
return '名称必须大于3小于40个字符串'
if self._check_even(data, is_modify):
return '指定反向代理名称或代理文件夹已存在'
# 判断代理,只能有全局代理或目录代理
if self._check_proxy_even(data, is_modify):
return '不能同时设置目录代理和全局代理'
# 判断cachetime类型
if data["cachetime"] < 1:
return "缓存时间不能为空"
rep = r"http(s)?\:\/\/"
rep_re_key = re.compile(r'''[?=\[\])(*&^%$#@!~`{}><,'"\\]+''')
# 检测代理目录格式
if rep_re_key.search(data["proxydir"]):
return "The agency directory cannot contain the following special symbols ?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]"
# 检测发送域名格式
if get.todomain:
if re.search("[}{#;\"\']+", data["todomain"]):
return '发送域名格式错误:' + data["todomain"] + '<br>不能存在以下特殊字符【 } { # ; \" \''
if webserver() != 'openlitespeed' and not get.todomain:
data["todomain"] = "$host"
# 检测目标URL格式
if not re.match(rep, data["proxysite"]):
return '域名格式错误 ' + data["proxysite"]
if rep_re_key.search(data["proxysite"]):
return "目标URL不能有以下特殊符号 ?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\\,',\"]"
if not data["proxysite"].split('//')[-1]:
return '目标URL不能为[http://或https://],请填写完整URLhttps://www.yakpanel.com'
for s in data["subfilter"]:
if not s["sub1"]:
continue
if not s["sub1"] and s["sub2"]:
return '请输入被替换的内容'
elif s["sub1"] == s["sub2"]:
return '替换内容与被替换内容不能一致'
return data
def check_location(self, site_name, proxy_dir: str) -> Optional[str]:
# 伪静态文件路径
rewrite_conf_path = "%s/vhost/rewrite/%s%s.conf" % (self.panel_path, self.config_prefix, site_name)
# vhost文件
vhost_path = "%s/vhost/nginx/%s%s.conf" % (self.panel_path, self.config_prefix, site_name)
rep_location = re.compile(r"location\s+(\^~\s*)?%s\s*{" % proxy_dir)
for i in [rewrite_conf_path, vhost_path]:
conf = read_file(i)
if isinstance(conf, str) and rep_location.search(conf):
return '伪静态/站点主配置文件已经存在全局反向代理'
@staticmethod
def _set_nginx_proxy_base():
file = "/www/server/nginx/conf/proxy.conf"
setup_path = "/www/server"
if not os.path.exists(file):
conf = '''proxy_temp_path %s/nginx/proxy_temp_dir;
proxy_cache_path %s/nginx/proxy_cache_dir levels=1:2 keys_zone=cache_one:10m inactive=1d max_size=5g;
client_body_buffer_size 512k;
proxy_connect_timeout 60;
proxy_read_timeout 60;
proxy_send_timeout 60;
proxy_buffer_size 32k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
proxy_cache cache_one;''' % (setup_path, setup_path)
write_file(file, conf)
conf = read_file(file)
if conf and conf.find('include proxy.conf;') == -1:
rep = r"include\s+mime.types;"
conf = re.sub(rep, "include mime.types;\n\tinclude proxy.conf;", conf)
write_file(file, conf)
def set_nginx_proxy_include(self, site_name) -> Optional[str]:
self._set_nginx_proxy_base()
ng_file = "{}/vhost/nginx/{}{}.conf".format(self.panel_path, self.config_prefix, site_name)
ng_conf = read_file(ng_file)
if not ng_conf:
return "配置文件丢失"
cure_cache = '''location ~ /purge(/.*) {
proxy_cache_purge cache_one $host$1$is_args$args;
#access_log /www/wwwlogs/%s_purge_cache.log;
}''' % site_name
proxy_dir = "{}/vhost/nginx/proxy/{}".format(self.panel_path, site_name)
if not os.path.isdir(os.path.dirname(proxy_dir)):
os.makedirs(os.path.dirname(proxy_dir))
if not os.path.isdir(proxy_dir):
os.makedirs(proxy_dir)
include_conf = (
"\n #清理缓存规则\n"
" %s\n"
" #引用反向代理规则,注释后配置的反向代理将无效\n"
" include /www/server/panel/vhost/nginx/proxy/%s/*.conf;\n"
) % (cure_cache, site_name)
rep_include = re.compile(r"\s*include.*/proxy/.*/\*\.conf\s*;", re.M)
if rep_include.search(ng_conf):
return
# 添加 引入
rep_list = [
(re.compile(r"\s*include\s+.*/rewrite/.*\.conf;(\s*#REWRITE-END)?"), False), # 先匹配伪静态,有伪静态就加到伪静态下
(re.compile(r"#PHP-INFO-END"), False), # 匹配PHP配置, 加到php配置下
(re.compile(r"\sinclude +.*/ip-restrict/.*\*\.conf;", re.M), False), # 匹配IP配置, 加其下
(re.compile(r"#SECURITY-END"), False), # 匹配Referer配置, 加其下
]
# 使用正则匹配确定插入位置
def set_by_rep_idx(tmp_rep: re.Pattern, use_start: bool) -> bool:
tmp_res = tmp_rep.search(ng_conf)
if not tmp_res:
return False
if use_start:
new_conf = ng_conf[:tmp_res.start()] + include_conf + tmp_res.group() + ng_conf[tmp_res.end():]
else:
new_conf = ng_conf[:tmp_res.start()] + tmp_res.group() + include_conf + ng_conf[tmp_res.end():]
write_file(ng_file, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return False
return True
for r, s in rep_list:
if set_by_rep_idx(r, s):
break
else:
return "无法在配置文件中定位到需要添加的项目"
now_ng_conf = read_file(ng_file)
# 清理文件缓存
rep_location = re.compile(r"location\s+~\s+\.\*\\\.[^{]*{(\s*(expires|error_log|access_log).*;){3}\s*}\s*")
new__ng_conf = rep_location.sub("", now_ng_conf)
write_file(ng_file, new__ng_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(ng_file, now_ng_conf)
def un_set_nginx_proxy_include(self, site_name) -> Optional[str]:
ng_file = "{}/vhost/nginx/{}{}.conf".format(self.panel_path, self.config_prefix, site_name)
ng_conf = read_file(ng_file)
if not ng_conf:
return "配置文件丢失"
rep_list = [
re.compile(r"\s*#清理缓存规则\n"),
re.compile(r"\s*location\s+~\s+/purge[^{]*{[^}]*}\s*"),
re.compile(r"(#[^#\n]*\n)?\s*include.*/proxy/.*/\*\.conf\s*;[^\n]*\n"),
]
new_conf = ng_conf
for rep in rep_list:
new_conf = rep.sub("", new_conf, 1)
write_file(ng_file, new_conf)
if webserver() == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return "配置移除失败"
def set_apache_proxy_include(self, site_name):
ap_file = "{}/vhost/apache/{}{}.conf".format(self.panel_path, self.config_prefix, site_name)
ap_conf = read_file(ap_file)
if not ap_conf:
return "配置文件丢失"
proxy_dir = "{}/vhost/apache/proxy/{}".format(self.panel_path, site_name)
if not os.path.isdir(os.path.dirname(proxy_dir)):
os.makedirs(os.path.dirname(proxy_dir))
if not os.path.isdir(proxy_dir):
os.makedirs(proxy_dir)
include_conf = (
" #引用反向代理规则,注释后配置的反向代理将无效\n"
" IncludeOptional /www/server/panel/vhost/apache/proxy/%s/*.conf\n"
) % site_name
rep_include = re.compile(r"\s*IncludeOptional.*/proxy/.*/\*\.conf\s*;", re.M)
if rep_include.search(ap_conf):
return
# 添加 引入
rep_list = [
(re.compile(r"<FilesMatch \\\.php\$>(.|\n)*?</FilesMatch>[^\n]*\n"), False), # 匹配PHP配置, 加到php配置下
(re.compile(r"CustomLog[^\n]*\n"), False), # 匹配Referer配置, 加其下
]
# 使用正则匹配确定插入位置
def set_by_rep_idx(rep: re.Pattern, use_start: bool) -> bool:
new_conf_list = []
last_idx = 0
for tmp in rep.finditer(ap_conf):
new_conf_list.append(ap_conf[last_idx:tmp.start()])
if use_start:
new_conf_list.append(include_conf)
new_conf_list.append(tmp.group())
else:
new_conf_list.append(tmp.group())
new_conf_list.append(include_conf)
last_idx = tmp.end()
if last_idx == 0:
return False
new_conf_list.append(ap_conf[last_idx:])
new_conf = "".join(new_conf_list)
write_file(ap_file, new_conf)
if webserver() == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return False
return True
for r, s in rep_list:
if set_by_rep_idx(r, s):
break
else:
return "无法在配置文件中定位到需要添加的项目"
def un_set_apache_proxy_include(self, site_name) -> Optional[str]:
ng_file = "{}/vhost/apache/{}{}.conf".format(self.panel_path, self.config_prefix, site_name)
ap_conf = read_file(ng_file)
if not ap_conf:
return "配置文件丢失"
rep_include = re.compile(r"(#.*\n)?\s*IncludeOptiona.*/proxy/.*/\*\.conf\s*[^\n]\n")
new_conf = rep_include.sub("", ap_conf)
write_file(ng_file, new_conf)
if webserver() == "apache" and check_server_config() is not None:
write_file(ng_file, ap_conf)
return "配置移除失败"
def set_nginx_proxy(self, proxy_data: dict) -> Optional[str]:
proxy_name_md5 = self._calc_proxy_name_md5(proxy_data["proxyname"])
ng_proxy_file = "%s/vhost/nginx/proxy/%s/%s_%s.conf" % (
self.panel_path, proxy_data["sitename"], proxy_name_md5, proxy_data["sitename"])
if proxy_data["type"] == 0:
if os.path.isfile(ng_proxy_file):
os.remove(ng_proxy_file)
return
random_string = self._random_string()
# websocket前置map
map_file = "{}/vhost/nginx/0.websocket.conf".format(self.panel_path)
if not os.path.exists(map_file):
write_file(map_file, '''
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}''')
# 构造缓存配置
ng_cache = r"""
if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" )
{
expires 1m;
}
proxy_ignore_headers Set-Cookie Cache-Control expires;
proxy_cache cache_one;
proxy_cache_key $host$uri$is_args$args;
proxy_cache_valid 200 304 301 302 %sm;""" % proxy_data["cachetime"]
no_cache = r"""
set $static_file%s 0;
if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" )
{
set $static_file%s 1;
expires 1m;
}
if ( $static_file%s = 0 )
{
add_header Cache-Control no-cache;
}""" % (random_string, random_string, random_string)
ng_proxy = '''
#PROXY-START%s
location ^~ %s
{
proxy_pass %s;
proxy_set_header Host %s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
# proxy_hide_header Upgrade;
add_header X-Cache $upstream_cache_status;
#Set Nginx Cache
%s
%s
}
#PROXY-END%s'''
# 构造替换字符串
ng_sub_data_list = []
for s in proxy_data["subfilter"]:
if not s["sub1"]:
continue
if '"' in s["sub1"]:
s["sub1"] = s["sub1"].replace('"', '\\"')
if '"' in s["sub2"]:
s["sub2"] = s["sub2"].replace('"', '\\"')
ng_sub_data_list.append(' sub_filter "%s" "%s";' % (s["sub1"], s["sub2"]))
if ng_sub_data_list:
ng_sub_filter = '''
proxy_set_header Accept-Encoding "";
%s
sub_filter_once off;''' % "\n".join(ng_sub_data_list)
else:
ng_sub_filter = ''
if proxy_data["proxydir"][-1] != '/':
proxy_dir = proxy_data["proxydir"] + "/"
else:
proxy_dir = proxy_data["proxydir"]
if proxy_data["proxysite"][-1] != '/':
proxy_site = proxy_data["proxysite"] + "/"
else:
proxy_site = proxy_data["proxysite"]
# 构造反向代理
if proxy_data["cache"] == 1:
ng_proxy_cache = ng_proxy % (
proxy_dir, proxy_dir, proxy_site, proxy_data["todomain"], ng_sub_filter, ng_cache, proxy_dir)
else:
ng_proxy_cache = ng_proxy % (
proxy_dir, proxy_dir, proxy_site, proxy_data["todomain"], ng_sub_filter, no_cache, proxy_dir)
write_file(ng_proxy_file, ng_proxy_cache)
if webserver() == "nginx" and check_server_config() is not None:
import public
public.print_log(check_server_config())
os.remove(ng_proxy_file)
return "配置添加失败"
def set_apache_proxy(self, proxy_data: dict):
proxy_name_md5 = self._calc_proxy_name_md5(proxy_data["proxyname"])
ap_proxy_file = "%s/vhost/apache/proxy/%s/%s_%s.conf" % (
self.panel_path, proxy_data["sitename"], proxy_name_md5, proxy_data["sitename"])
if proxy_data["type"] == 0:
if os.path.isfile(ap_proxy_file):
os.remove(ap_proxy_file)
return
ap_proxy = '''#PROXY-START%s
<IfModule mod_proxy.c>
ProxyRequests Off
SSLProxyEngine on
ProxyPass %s %s/
ProxyPassReverse %s %s/
</IfModule>
#PROXY-END%s''' % (proxy_data["proxydir"], proxy_data["proxydir"], proxy_data["proxysite"],
proxy_data["proxydir"],proxy_data["proxysite"], proxy_data["proxydir"])
write_file(ap_proxy_file, ap_proxy)
@staticmethod
def _random_string() -> str:
from uuid import uuid4
return "bt" + uuid4().hex[:6]
@staticmethod
def _calc_proxy_name_md5(data: str) -> str:
m = md5()
m.update(data.encode("utf-8"))
return m.hexdigest()
def create_proxy(self, get) -> Optional[str]:
proxy_data = self.check_args(get, is_modify=False)
if isinstance(proxy_data, str):
return proxy_data
if webserver() == "nginx":
error_msg = self.check_location(proxy_data["sitename"], proxy_data["proxydir"])
if error_msg:
return error_msg
error_msg = self.set_nginx_proxy_include(proxy_data["sitename"])
if webserver() == "nginx" and error_msg:
return error_msg
error_msg = self.set_apache_proxy_include(proxy_data["sitename"])
if webserver() == "apache" and error_msg:
return error_msg
error_msg = self.set_nginx_proxy(proxy_data)
if webserver() == "nginx" and error_msg:
return error_msg
self.set_apache_proxy(proxy_data)
self.config.append(proxy_data)
self.save_config()
service_reload()
def modify_proxy(self, get) -> Optional[str]:
proxy_data = self.check_args(get, is_modify=True)
if isinstance(proxy_data, str):
return proxy_data
idx = None
for index, i in enumerate(self.config):
if i["proxyname"] == proxy_data["proxyname"] and i["sitename"] == proxy_data["sitename"]:
idx = index
break
if idx is None:
return "未找到该名称的反向代理配置"
if webserver() == "nginx" and proxy_data["proxydir"] != self.config[idx]["proxydir"]:
error_msg = self.check_location(proxy_data["sitename"], proxy_data["proxydir"])
if error_msg:
return error_msg
error_msg = self.set_nginx_proxy_include(proxy_data["sitename"])
if webserver() == "nginx" and error_msg:
return error_msg
error_msg = self.set_apache_proxy_include(proxy_data["sitename"])
if webserver() == "apache" and error_msg:
return error_msg
error_msg = self.set_nginx_proxy(proxy_data)
if webserver() == "nginx" and error_msg:
return error_msg
self.set_apache_proxy(proxy_data)
self.config[idx] = proxy_data
self.save_config()
service_reload()
def remove_proxy(self, site_name, proxy_name, multiple=False) -> Optional[str]:
idx = None
site_other = False
for index, i in enumerate(self.config):
if i["proxyname"] == proxy_name and i["sitename"] == site_name:
idx = index
if i["sitename"] == site_name and i["proxyname"] != proxy_name:
site_other = True
if idx is None:
return "未找到该名称的反向代理配置"
proxy_name_md5 = self._calc_proxy_name_md5(proxy_name)
ng_proxy_file = "%s/vhost/nginx/proxy/%s/%s_%s.conf" % (
self.panel_path, site_name, proxy_name_md5, site_name)
ap_proxy_file = "%s/vhost/apache/proxy/%s/%s_%s.conf" % (
self.panel_path, site_name, proxy_name_md5, site_name)
if os.path.isfile(ap_proxy_file):
os.remove(ap_proxy_file)
if os.path.isfile(ng_proxy_file):
os.remove(ng_proxy_file)
del self.config[idx]
self.save_config()
if not site_other:
self.un_set_apache_proxy_include(site_name)
self.un_set_nginx_proxy_include(site_name)
if not multiple:
service_reload()
def get_proxy_list(self, get) -> Union[str, List[Dict[str, Any]]]:
try:
site_name = get.sitename.strip()
except (AttributeError, ValueError, TypeError):
return "Parameter error"
proxy_list = []
web_server = webserver()
for conf in self.config:
if conf["sitename"] != site_name:
continue
md5_name = self._calc_proxy_name_md5(conf['proxyname'])
conf["proxy_conf_file"] = "%s/vhost/%s/proxy/%s/%s_%s.conf" % (
self.panel_path, web_server, site_name, md5_name, site_name)
proxy_list.append(conf)
return proxy_list
def remove_site_proxy_info(self, site_name):
idx_list = []
for index, i in enumerate(self.config):
if i["sitename"] == site_name:
idx_list.append(index)
for idx in idx_list[::-1]:
del self.config[idx]
self.save_config()
ng_proxy_dir = "%s/vhost/nginx/proxy/%s" % (self.panel_path, site_name)
ap_proxy_dir = "%s/vhost/apache/proxy/%s" % (self.panel_path, site_name)
if os.path.isdir(ng_proxy_dir):
shutil.rmtree(ng_proxy_dir)
if os.path.isdir(ap_proxy_dir):
shutil.rmtree(ap_proxy_dir)
class Proxy(object):
def __init__(self, config_prefix=""):
self.config_prefix = config_prefix
self._p = RealProxy(self.config_prefix)
def create_proxy(self, get):
msg = self._p.create_proxy(get)
if msg:
return json_response(status=False, msg=msg)
return json_response(status=True, msg="Successfully added")
def modify_proxy(self, get):
msg = self._p.modify_proxy(get)
if msg:
return json_response(status=False, msg=msg)
return json_response(status=True, msg="修改成功")
def remove_proxy(self, get):
try:
site_name = get.sitename.strip()
proxy_name = get.proxyname.strip()
except:
return json_response(status=False, msg="Parameter error")
msg = self._p.remove_proxy(site_name, proxy_name)
if msg:
return json_response(status=False, msg=msg)
return json_response(status=True, msg="Successfully delete")
def get_proxy_list(self, get):
data = self._p.get_proxy_list(get)
if isinstance(data, str):
return json_response(status=False, msg=data)
else:
return json_response(status=True, data=data)

View File

@@ -0,0 +1,737 @@
import os
import re
import json
import hashlib
import shutil
import time
from typing import Tuple, Optional, Union, Dict, List, Any
from urllib import parse
from itertools import product
from .util import webserver, check_server_config, write_file, read_file, DB, service_reload
from mod.base import json_response
class RealRedirect:
panel_path = "/www/server/panel"
_redirect_conf_file = "{}/data/redirect.conf".format(panel_path)
_ng_redirect_domain_format = """
if ($host ~ '^%s'){
return %s %s%s;
}
"""
_ng_redirect_path_format = """
rewrite ^%s(.*) %s%s %s;
"""
_ap_redirect_domain_format = """
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %%{HTTP_HOST} ^%s [NC]
RewriteRule ^(.*) %s%s [L,R=%s]
</IfModule>
"""
_ap_redirect_path_format = """
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^%s(.*) %s%s [L,R=%s]
</IfModule>
"""
def __init__(self, config_prefix: str):
self._config: Optional[List[Dict[str, Union[str, int]]]] = None
self.config_prefix = config_prefix
self._webserver = None
@property
def webserver(self) -> str:
if self._webserver is not None:
return self._webserver
self._webserver = webserver()
return self._webserver
@property
def config(self) -> List[Dict[str, Union[str, int, List]]]:
if self._config is not None:
return self._config
try:
self._config = json.loads(read_file(self._redirect_conf_file))
except (json.JSONDecodeError, TypeError, ValueError):
self._config = []
if not isinstance(self._config, list):
self._config = []
return self._config
def save_config(self):
if self._config is not None:
return write_file(self._redirect_conf_file, json.dumps(self._config))
def _check_redirect_domain_exist(self, site_name,
redirect_domain: list,
redirect_name: str = None,
is_modify=False) -> Optional[List[str]]:
res = set()
redirect_domain_set = set(redirect_domain)
for c in self.config:
if c["sitename"] != site_name:
continue
if is_modify:
if c["redirectname"] != redirect_name:
res |= set(c["redirectdomain"]) & redirect_domain_set
else:
res |= set(c["redirectdomain"]) & redirect_domain_set
return list(res) if res else None
def _check_redirect_path_exist(self, site_name,
redirect_path: str,
redirect_name: str = None) -> bool:
for c in self.config:
if c["sitename"] == site_name:
if c["redirectname"] != redirect_name and c["redirectpath"] == redirect_path:
return True
return False
@staticmethod
def _parse_url_domain(url: str):
return parse.urlparse(url).netloc
@staticmethod
def _parse_url_path(url: str):
return parse.urlparse(url).path
# 计算name md5
@staticmethod
def _calc_redirect_name_md5(redirect_name) -> str:
md5 = hashlib.md5()
md5.update(redirect_name.encode('utf-8'))
return md5.hexdigest()
def _check_redirect(self, site_name, redirect_name, is_error=False):
for i in self.config:
if i["sitename"] != site_name:
continue
if is_error and "errorpage" in i and i["errorpage"] in [1, '1']:
return i
if i["redirectname"] == redirect_name:
return i
return None
# 创建修改配置检测
def _check_redirect_args(self, get, is_modify=False) -> Union[str, Dict]:
if check_server_config() is not None:
return '配置文件出错请先排查配置'
try:
site_name = get.sitename.strip()
redirect_path = get.redirectpath.strip()
redirect_type = get.redirecttype.strip()
domain_or_path = get.domainorpath.strip()
hold_path = int(get.holdpath)
to_url = ""
to_path = ""
error_page = 0
redirect_domain = []
redirect_name = ""
status_type = 1
if "redirectname" in get and get.redirectname.strip():
redirect_name = get.redirectname.strip()
if "tourl" in get:
to_url = get.tourl.strip()
if "topath" in get:
to_path = get.topath.strip()
if "redirectdomain" in get:
redirect_domain = json.loads(get.redirectdomain.strip())
if "type" in get:
status_type = int(get.type)
if "errorpage" in get:
error_page = int(get.errorpage)
except (AttributeError, ValueError):
return '参数错误'
if not is_modify:
if not redirect_name:
return "Parameter error, configuration name cannot be empty"
# 检测名称是否重复
if not (3 <= len(redirect_name) < 15):
return '名称必须大于2小于15个字符串'
if self._check_redirect(site_name, redirect_name, error_page == 1):
return '指定重定向名称已存在'
site_info = DB('sites').where("name=?", (site_name,)).find()
if not isinstance(site_info, dict):
return "站点信息查询错误"
else:
site_name = site_info["name"]
# 检测目标URL格式
rep = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?"
if to_url and not re.match(rep, to_url):
return '目标URL格式不对【%s' % to_url
# 非404页面de重定向检测项
if error_page != 1:
# 检测是否选择域名
if domain_or_path == "domain":
if not redirect_domain:
return '请选择重定向域名'
# 检测域名是否已经存在配置文件
repeat_domain = self._check_redirect_domain_exist(site_name, redirect_domain, redirect_name, is_modify)
if repeat_domain:
return '重定向域名重复 %s' % repeat_domain
# 检查目标URL的域名和被重定向的域名是否一样
tu = self._parse_url_domain(to_url)
for d in redirect_domain:
if d == tu:
return '域名 "%s" 和目标域名一致请取消选择' % d
else:
if not redirect_path:
return '请输入重定向路径'
if redirect_path[0] != "/":
return "路径格式不正确,格式为/xxx"
# 检测路径是否有存在配置文件
if self._check_redirect_path_exist(site_name, redirect_path, redirect_name):
return '重定向路径重复 %s' % redirect_path
to_url_path = self._parse_url_path(to_url)
if to_url_path.startswith(redirect_path):
return '目标URL[%s]以被重定向的路径[%s]开头,会导致循环匹配' % (to_url_path, redirect_path)
# 404页面重定向检测项
else:
if not to_url and not to_path:
return '首页或自定义页面必须二选一'
if to_path:
to_path = "/"
return {
"tourl": to_url,
"topath": to_path,
"errorpage": error_page,
"redirectdomain": redirect_domain,
"redirectname": redirect_name if redirect_name else str(int(time.time())),
"type": status_type,
"sitename": site_name,
"redirectpath": redirect_path,
"redirecttype": redirect_type,
"domainorpath": domain_or_path,
"holdpath": hold_path,
}
def create_redirect(self, get) -> Tuple[bool, str]:
res_conf = self._check_redirect_args(get, is_modify=False)
if isinstance(res_conf, str):
return False, res_conf
res = self._set_include(res_conf)
if res is not None:
return False, res
res = self._write_config(res_conf)
if res is not None:
return False, res
self.config.append(res_conf)
self.save_config()
service_reload()
return True, '创建成功'
def _set_include(self, res_conf) -> Optional[str]:
flag, msg = self._set_nginx_redirect_include(res_conf)
if not flag and webserver() == "nginx":
return msg
flag, msg = self._set_apache_redirect_include(res_conf)
if not flag and webserver() == "apache":
return msg
def _write_config(self, res_conf) -> Optional[str]:
if res_conf["errorpage"] != 1:
res = self.write_nginx_redirect_file(res_conf)
if res is not None:
return res
res = self.write_apache_redirect_file(res_conf)
if res is not None:
return res
else:
self.unset_nginx_404_conf(res_conf["sitename"])
res = self.write_nginx_404_redirect_file(res_conf)
if res is not None:
return res
res = self.write_apache_404_redirect_file(res_conf)
if res is not None:
return res
def modify_redirect(self, get) -> Tuple[bool, str]:
"""
@name 修改、启用、禁用重定向
@author hezhihong
@param get.sitename 站点名称
@param get.redirectname 重定向名称
@param get.tourl 目标URL
@param get.redirectdomain 重定向域名
@param get.redirectpath 重定向路径
@param get.redirecttype 重定向类型
@param get.type 重定向状态 0禁用 1启用
@param get.domainorpath 重定向类型 domain 域名重定向 path 路径重定向
@param get.holdpath 保留路径 0不保留 1保留
@return json
"""
# 基本信息检查
res_conf = self._check_redirect_args(get, is_modify=True)
if isinstance(res_conf, str):
return False, res_conf
old_idx = None
for i, conf in enumerate(self.config):
if conf["redirectname"] == res_conf["redirectname"] and conf["sitename"] == res_conf["sitename"]:
old_idx = i
res = self._set_include(res_conf)
if res is not None:
return False, res
res = self._write_config(res_conf)
if res is not None:
return False, res
if old_idx is not None:
self.config[old_idx].update(res_conf)
else:
self.config.append(res_conf)
self.save_config()
service_reload()
return True, '修改成功'
def _set_nginx_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ng_redirect_dir = "%s/vhost/nginx/redirect/%s" % (self.panel_path, redirect_conf["sitename"])
ng_file = "{}/vhost/nginx/{}{}.conf".format(self.panel_path, self.config_prefix, redirect_conf["sitename"])
if not os.path.exists(ng_redirect_dir):
os.makedirs(ng_redirect_dir, 0o600)
ng_conf = read_file(ng_file)
if not isinstance(ng_conf, str):
return False, "nginx配置文件读取失败"
rep_include = re.compile(r"\sinclude +.*/redirect/.*\*\.conf;", re.M)
if rep_include.search(ng_conf):
return True, ""
redirect_include = (
"#SSL-END\n"
" #引用重定向规则,注释后配置的重定向代理将无效\n"
" include {}/*.conf;"
).format(ng_redirect_dir)
if "#SSL-END" not in ng_conf:
return False, "添加配置失败无法定位SSL相关配置的位置"
new_conf = ng_conf.replace("#SSL-END", redirect_include)
write_file(ng_file, new_conf)
if self.webserver == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return False, "添加配置失败"
return True, ""
def _un_set_nginx_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ng_file = "{}/vhost/nginx/{}{}.conf".format(self.panel_path, self.config_prefix, redirect_conf["sitename"])
ng_conf = read_file(ng_file)
if not isinstance(ng_conf, str):
return False, "nginx配置文件读取失败"
rep_include = re.compile(r"(#(.*)\n)?\s*include +.*/redirect/.*\*\.conf;")
if not rep_include.search(ng_conf):
return True, ""
new_conf = rep_include.sub("", ng_conf, 1)
write_file(ng_file, new_conf)
if self.webserver == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return False, "移除配置失败"
return True, ""
def _set_apache_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ap_redirect_dir = "%s/vhost/apache/redirect/%s" % (self.panel_path, redirect_conf["sitename"])
ap_file = "{}/vhost/apache/{}{}.conf".format(self.panel_path, self.config_prefix, redirect_conf["sitename"])
if not os.path.exists(ap_redirect_dir):
os.makedirs(ap_redirect_dir, 0o600)
ap_conf = read_file(ap_file)
if not isinstance(ap_conf, str):
return False, "apache配置文件读取失败"
rep_include = re.compile(r"\sIncludeOptional +.*/redirect/.*\*\.conf", re.M)
include_count = len(list(rep_include.finditer(ap_conf)))
if ap_conf.count("</VirtualHost>") == include_count:
return True, ""
if include_count > 0:
# 先清除已有的配置
self._un_set_apache_redirect_include(redirect_conf)
rep_custom_log = re.compile(r"CustomLog .*\n")
rep_deny_files = re.compile(r"\n\s*#DENY FILES")
include_conf = (
"\n # 引用重定向规则,注释后配置的重定向代理将无效\n"
" IncludeOptional {}/*.conf\n"
).format(ap_redirect_dir)
new_conf = None
def set_by_rep_idx(rep: re.Pattern, use_start: bool) -> bool:
new_conf_list = []
last_idx = 0
for tmp in rep.finditer(ap_conf):
new_conf_list.append(ap_conf[last_idx:tmp.start()])
if use_start:
new_conf_list.append(include_conf)
new_conf_list.append(tmp.group())
else:
new_conf_list.append(tmp.group())
new_conf_list.append(include_conf)
last_idx = tmp.end()
new_conf_list.append(ap_conf[last_idx:])
nonlocal new_conf
new_conf = "".join(new_conf_list)
write_file(ap_file, new_conf)
if self.webserver == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return False
return True
if set_by_rep_idx(rep_custom_log, False) and rep_include.search(new_conf):
return True, ""
if set_by_rep_idx(rep_deny_files, True) and rep_include.search(new_conf):
return True, ""
return False, "设置失败"
def _un_set_apache_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ap_file = "{}/vhost/apache/{}{}.conf".format(self.panel_path, self.config_prefix, redirect_conf["sitename"])
ap_conf = read_file(ap_file)
if not isinstance(ap_conf, str):
return False, "apache配置文件读取失败"
rep_include = re.compile(r"(#(.*)\n)?\s*IncludeOptional +.*/redirect/.*\*\.conf")
if not rep_include.search(ap_conf):
return True, ""
new_conf = rep_include.sub("", ap_conf)
write_file(ap_file, new_conf)
if self.webserver == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return False, "移除配置失败"
return True, ""
def write_nginx_redirect_file(self, redirect_conf: dict) -> Optional[str]:
conf_file = "{}/vhost/nginx/redirect/{}/{}_{}.conf".format(
self.panel_path, redirect_conf["sitename"], self._calc_redirect_name_md5(redirect_conf["redirectname"]),
redirect_conf["sitename"]
)
if redirect_conf["type"] == 1:
to_url = redirect_conf["tourl"]
conf_list = ["#REWRITE-START"]
if redirect_conf["domainorpath"] == "domain":
hold_path = "$request_uri" if redirect_conf["holdpath"] == 1 else ""
for sd in redirect_conf["redirectdomain"]:
if sd.startswith("*."):
sd = r"[\w.]+\." + sd[2:]
conf_list.append(self._ng_redirect_domain_format % (
sd, redirect_conf["redirecttype"], to_url, hold_path
))
else:
redirect_path = redirect_conf["redirectpath"]
if redirect_conf["redirecttype"] == "301":
redirect_type = "permanent"
else:
redirect_type = "redirect"
hold_path = "$1" if redirect_conf["holdpath"] == 1 else ""
conf_list.append(self._ng_redirect_path_format % (redirect_path, to_url, hold_path, redirect_type))
conf_list.append("#REWRITE-END")
conf_data = "\n".join(conf_list)
write_file(conf_file, conf_data)
if self.webserver == "nginx":
error_msg = check_server_config()
if error_msg is not None:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + error_msg.replace("\n", '<br>') + '</a>'
else:
if os.path.exists(conf_file):
os.remove(conf_file)
def write_apache_redirect_file(self, redirect_conf: dict) -> Optional[str]:
conf_file = "{}/vhost/apache/redirect/{}/{}_{}.conf".format(
self.panel_path, redirect_conf["sitename"], self._calc_redirect_name_md5(redirect_conf["redirectname"]),
redirect_conf["sitename"]
)
if redirect_conf["type"] != 1:
if os.path.exists(conf_file):
os.remove(conf_file)
return
to_url = redirect_conf["tourl"]
conf_list = ["#REWRITE-START"]
hold_path = "$1" if redirect_conf["holdpath"] == 1 else ""
if redirect_conf["domainorpath"] == "domain":
for sd in redirect_conf["redirectdomain"]:
if sd.startswith("*."):
sd = r"[\w.]+\." + sd[2:]
conf_list.append(self._ap_redirect_domain_format % (
sd, to_url, hold_path, redirect_conf["redirecttype"]
))
else:
redirect_path = redirect_conf["redirectpath"]
conf_list.append(self._ap_redirect_path_format % (redirect_path, to_url, hold_path, redirect_conf["redirecttype"]))
conf_list.append("#REWRITE-END")
write_file(conf_file, "\n".join(conf_list))
if self.webserver == "apache":
error_msg = check_server_config()
if error_msg is not None:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + error_msg.replace("\n", '<br>') + '</a>'
def unset_nginx_404_conf(self, site_name):
"""
清理已有的 404 页面 配置
"""
need_clear_files = [
"{}/vhost/nginx/{}{}.conf".format(self.panel_path, self.config_prefix, site_name),
"{}/vhost/nginx/rewrite/{}{}.conf".format(self.panel_path, self.config_prefix, site_name),
]
rep_error_page = re.compile(r'(?P<prefix>.*)error_page +404 +/404\.html[^\n]*\n', re.M)
rep_location_404 = re.compile(r'(?P<prefix>.*)location += +/404\.html[^}]*}')
clear_files = [
{
"data": read_file(i),
"path": i,
} for i in need_clear_files
]
for file_info, rep in product(clear_files, (rep_error_page, rep_location_404)):
if not isinstance(file_info["data"], str):
continue
tmp_res = rep.search(file_info["data"])
if not tmp_res or tmp_res.group("prefix").find("#") != -1:
continue
file_info["data"] = rep.sub("", file_info["data"])
for i in clear_files:
if not isinstance(i["data"], str):
continue
write_file(i["path"], i["data"])
def write_nginx_404_redirect_file(self, redirect_conf: dict) -> Optional[str]:
"""
设置nginx 404重定向
"""
r_name_md5 = self._calc_redirect_name_md5(redirect_conf["redirectname"])
file_path = "{}/vhost/nginx/redirect/{}".format(self.panel_path, redirect_conf["sitename"])
file_name = '%s_%s.conf' % (r_name_md5, redirect_conf["sitename"])
conf_file = os.path.join(file_path, file_name)
if redirect_conf["type"] != 1:
if os.path.exists(conf_file):
os.remove(conf_file)
return
_path = redirect_conf["tourl"] if redirect_conf["tourl"] else redirect_conf["topath"]
conf_data = (
'#REWRITE-START\n'
'error_page 404 = @notfound;\n'
'location @notfound {{\n'
' return {} {};\n'
'}}\n#REWRITE-END'
).format(redirect_conf["redirecttype"], _path)
write_file(conf_file, conf_data)
if self.webserver == "nginx":
error_msg = check_server_config()
if error_msg is not None:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + error_msg.replace("\n", '<br>') + '</a>'
def write_apache_404_redirect_file(self, redirect_conf: dict) -> Optional[str]:
"""
设置apache 404重定向
"""
r_name_md5 = self._calc_redirect_name_md5(redirect_conf["redirectname"])
conf_file = "{}/vhost/apache/redirect/{}/{}_{}.conf".format(
self.panel_path, redirect_conf["sitename"], r_name_md5, redirect_conf["sitename"]
)
if redirect_conf["type"] != 1:
if os.path.exists(conf_file):
os.remove(conf_file)
return
_path = redirect_conf["tourl"] if redirect_conf["tourl"] else redirect_conf["topath"]
conf_data = """
#REWRITE-START
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{{REQUEST_FILENAME}} !-f
RewriteCond %{{REQUEST_FILENAME}} !-d
RewriteRule . {} [L,R={}]
</IfModule>
#REWRITE-END
""".format(_path, redirect_conf["redirecttype"])
write_file(conf_file, conf_data)
if self.webserver == "apache":
error_msg = check_server_config()
if error_msg is not None:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + error_msg.replace("\n", '<br>') + '</a>'
def remove_redirect(self, get, multiple=None) -> Tuple[bool, str]:
try:
site_name = get.sitename.strip()
redirect_name = get.redirectname.strip()
except AttributeError:
return False, "Parameter error"
target_idx = None
have_other_redirect = False
target_conf = None
for i, conf in enumerate(self.config):
if conf["redirectname"] != redirect_name and conf["sitename"] == site_name:
have_other_redirect = True
if conf["redirectname"] == redirect_name and conf["sitename"] == site_name:
target_idx = i
target_conf = conf
if target_idx is None:
return False, '没有指定的配置'
r_md5_name = self._calc_redirect_name_md5(target_conf["redirectname"])
ng_conf_file = "%s/vhost/nginx/redirect/%s/%s_%s.conf" % (
self.panel_path, site_name, r_md5_name, site_name)
if os.path.exists(ng_conf_file):
os.remove(ng_conf_file)
ap_conf_file = "%s/vhost/nginx/apache/%s/%s_%s.conf" % (
self.panel_path, site_name, r_md5_name, site_name)
if os.path.exists(ap_conf_file):
os.remove(ap_conf_file)
if not have_other_redirect:
self._un_set_apache_redirect_include(target_conf)
self._un_set_nginx_redirect_include(target_conf)
del self.config[target_idx]
self.save_config()
if not multiple:
service_reload()
return True, '删除成功'
def mutil_remove_redirect(self, get):
try:
redirect_names = json.loads(get.redirectnames.strip())
site_name = get.sitename.strip()
except (AttributeError, json.JSONDecodeError, TypeError):
return False, "Parameter error"
del_successfully = []
del_failed = []
get_obj = type(get)()
for redirect_name in redirect_names:
get_obj.redirectname = redirect_name
get_obj.sitename = site_name
try:
flag, msg = self.remove_redirect(get, multiple=1)
if flag:
del_failed[redirect_name] = msg
continue
del_successfully.append(redirect_name)
except:
del_failed.append(redirect_name)
service_reload()
if not del_failed:
return True, '删除重定向【{}】成功'.format(','.join(del_successfully))
else:
return True, '重定向【{}】删除成功,【{}】删除失败'.format(
','.join(del_successfully), ','.join(del_failed)
)
def get_redirect_list(self, get) -> Tuple[bool, Union[str, List[Dict[str, Any]]]]:
try:
error_page = None
site_name = get.sitename.strip()
if "errorpage" in get:
error_page = int(get.errorpage)
except (AttributeError, ValueError, TypeError):
return False, "Parameter error"
redirect_list = []
web_server = self.webserver
if self.webserver == 'openlitespeed':
web_server = 'apache'
for conf in self.config:
if conf["sitename"] != site_name:
continue
if error_page is not None and error_page != int(conf['errorpage']):
continue
if 'errorpage' in conf and conf['errorpage'] in [1, '1']:
conf['redirectdomain'] = ['404页面']
md5_name = self._calc_redirect_name_md5(conf['redirectname'])
conf["redirect_conf_file"] = "%s/vhost/%s/redirect/%s/%s_%s.conf" % (
self.panel_path, web_server, site_name, md5_name, site_name)
conf["type"] = 1 if os.path.isfile(conf["redirect_conf_file"]) else 0
redirect_list.append(conf)
return True, redirect_list
def remove_site_redirect_info(self, site_name):
for i in range(len(self.config) - 1, -1, -1):
if self.config[i]["sitename"] == site_name:
del self.config[i]
self.save_config()
m_path = self.panel_path + '/vhost/nginx/redirect/' + site_name
if os.path.exists(m_path):
shutil.rmtree(m_path)
m_path = self.panel_path + '/vhost/apache/redirect/' + site_name
if os.path.exists(m_path):
shutil.rmtree(m_path)
class Redirect(RealRedirect):
def __init__(self, config_prefix: str = ""):
super().__init__(config_prefix)
self.config_prefix = config_prefix
def remove_redirect_by_project_name(self, project_name):
return self.remove_site_redirect_info(project_name)
def create_project_redirect(self, get):
flag, msg = self.create_redirect(get)
return json_response(status=flag, msg=msg)
def modify_project_redirect(self, get):
flag, msg = self.modify_redirect(get)
return json_response(status=flag, msg=msg)
def remove_project_redirect(self, get):
flag, msg = self.remove_redirect(get)
return json_response(status=flag, msg=msg)
def mutil_remove_project_redirect(self, get):
flag, msg = self.mutil_remove_redirect(get)
return json_response(status=flag, msg=msg)
def get_project_redirect_list(self, get):
flag, data = self.get_redirect_list(get)
if not flag:
return json_response(status=flag, msg=data)
else:
return json_response(status=flag, data=data)

View File

@@ -0,0 +1,363 @@
import os
import re
import json
from dataclasses import dataclass
from typing import Tuple, Optional, Union, Dict
from .util import webserver, check_server_config, DB, \
write_file, read_file, GET_CLASS, service_reload, pre_re_key
from mod.base import json_response
@dataclass
class _RefererConf:
name: str
fix: str
domains: str
status: str
return_rule: str
http_status: str
def __str__(self):
return '{"name"="%s","fix"="%s","domains"="%s","status"="%s","http_status"="%s","return_rule"="%s"}' % (
self.name, self.fix, self.domains, self.status, self.http_status, self.return_rule
)
class RealReferer:
_referer_conf_dir = '/www/server/panel/vhost/config' # 防盗链配置
_ng_referer_conf_format = r''' #SECURITY-START 防盗链配置
location ~ .*\.(%s)$ {
expires 30d;
access_log /dev/null;
valid_referers %s;
if ($invalid_referer){
%s;
}
}
#SECURITY-END'''
def __init__(self, config_prefix: str):
if not os.path.isdir(self._referer_conf_dir):
os.makedirs(self._referer_conf_dir)
self.config_prefix: str = config_prefix
self._webserver = None
@property
def webserver(self) -> str:
if self._webserver is not None:
return self._webserver
self._webserver = webserver()
return self._webserver
def get_config(self, site_name: str) -> Optional[_RefererConf]:
try:
config = json.loads(read_file("{}/{}{}_door_chain.json".format(self._referer_conf_dir, self.config_prefix, site_name)))
except (json.JSONDecodeError, TypeError, ValueError):
config = None
if isinstance(config, dict):
return _RefererConf(**config)
return None
def save_config(self, site_name: str, data: Union[dict, str, _RefererConf]) -> bool:
if isinstance(data, dict):
c = json.dumps(data)
elif isinstance(data, _RefererConf):
c = json.dumps(str(data))
else:
c = data
file_path = "{}/{}{}_door_chain.json".format(self._referer_conf_dir, self.config_prefix, site_name)
return write_file(file_path, c)
# 检测参数,如果正确则返回 配置数据类型的值,否则返回错误信息
@staticmethod
def check_args(get: Union[Dict, GET_CLASS]) -> Union[_RefererConf, str]:
res = {}
if isinstance(get, GET_CLASS):
try:
res["status"] = "true" if not hasattr(get, "status") else get.status.strip()
res["http_status"] = "false" if not hasattr(get, "http_status") else get.http_status.strip()
res["name"] = get.name.strip()
res["fix"] = get.fix.strip()
res["domains"] = get.domains.strip()
res["return_rule"] = get.return_rule.strip()
except AttributeError:
return "Parameter error"
else:
try:
res["status"] = "true" if "status" not in get else get["status"].strip()
res["http_status"] = "false" if "http_status" not in get else get["http_status"].strip()
res["name"] = get["name"].strip()
res["fix"] = get["fix"].strip()
res["domains"] = get["domains"].strip()
res["return_rule"] = get["return_rule"].strip()
except KeyError:
return "Parameter error"
rconf = _RefererConf(**res)
if rconf.status not in ("true", "false") and rconf.return_rule not in ("true", "false"):
return "状态参数只能使用【true,false】"
if rconf.return_rule not in ('404', '403', '200', '301', '302', '401') and rconf.return_rule[0] != "/":
return "响应资源应使用URI路径或HTTP状态码/test.png 或 404"
if len(rconf.domains) < 3:
return "防盗链域名不能为空"
if len(rconf.fix) < 2:
return 'URL后缀不能为空!'
return rconf
def set_referer_security(self, rc: _RefererConf) -> Tuple[bool, str]:
error_msg = self._set_nginx_referer_security(rc)
if error_msg and self.webserver == "nginx":
return False, error_msg
error_msg = self._set_apache_referer_security(rc)
if error_msg and self.webserver == "apache":
return False, error_msg
service_reload()
self.save_config(rc.name, rc)
return True, "设置成功"
def _set_nginx_referer_security(self, rc: _RefererConf) -> Optional[str]:
ng_file = '/www/server/panel/vhost/nginx/{}{}.conf'.format(self.config_prefix, rc.name)
ng_conf = read_file(ng_file)
if not isinstance(ng_conf, str):
return "nginx配置文件丢失无法设置"
start_idx, end_idx = self._get_nginx_referer_security_idx(ng_conf)
if rc.status == "true":
if rc.return_rule[0] == "/":
return_rule = "rewrite /.* {} break".format(rc.return_rule)
else:
return_rule = 'return {}'.format(rc.return_rule)
valid_args_list = []
if rc.http_status == "true":
valid_args_list.extend(("none", "blocked"))
valid_args_list.extend(map(lambda x: x.strip(), rc.domains.split(",")))
valid_args = " ".join(valid_args_list)
location_args = "|".join(map(lambda x: pre_re_key(x.strip()), rc.fix.split(",")))
if start_idx is not None:
new_conf = ng_conf[:start_idx] + "\n" + (
self._ng_referer_conf_format % (location_args, valid_args, return_rule)
) + "\n" + ng_conf[end_idx:]
else:
rep_redirect_include = re.compile(r"\sinclude +.*/redirect/.*\*\.conf;", re.M)
redirect_include_res = rep_redirect_include.search(ng_conf)
if redirect_include_res:
new_conf = ng_conf[:redirect_include_res.end()] + "\n" + (
self._ng_referer_conf_format % (location_args, valid_args, return_rule)
) + ng_conf[redirect_include_res.end():]
else:
if "#SSL-END" not in ng_conf:
return "添加配置失败无法定位SSL相关配置的位置"
new_conf = ng_conf.replace("#SSL-END", "#SSL-END\n" + self._ng_referer_conf_format % (
location_args, valid_args, return_rule))
else:
if start_idx is None:
return
new_conf = ng_conf[:start_idx] + "\n" + ng_conf[end_idx:]
write_file(ng_file, new_conf)
if self.webserver == "nginx" and check_server_config() is not None:
write_file(ng_file, ng_conf)
return "配置失败"
@staticmethod
def _get_nginx_referer_security_idx(ng_conf: str) -> Tuple[Optional[int], Optional[int]]:
rep_security = re.compile(
r"(\s*#\s*SECURITY-START.*\n)?\s*location\s+~\s+\.\*\\\.\(.*(\|.*)?\)\$\s*\{[^}]*valid_referers"
)
res = rep_security.search(ng_conf)
if res is None:
return None, None
start_idx = res.start()
s_idx = start_idx + ng_conf[start_idx:].find("{") + 1 # 起始位置
l_n = 1
max_idx = len(ng_conf)
while l_n > 0:
next_l = ng_conf[s_idx:].find("{")
next_r = ng_conf[s_idx:].find("}") # 可能存在报错
if next_r == -1:
return None, None
if next_l == -1:
next_l = max_idx
if next_l < next_r:
l_n += 1
else:
l_n -= 1
s_idx += min(next_l, next_r) + 1
rep_comment = re.search(r"^\s*#\s*SECURITY-END[^\n]*\n", ng_conf[s_idx:])
if rep_comment is not None:
end_idx = s_idx + rep_comment.end()
else:
end_idx = s_idx
return start_idx, end_idx
@staticmethod
def _build_apache_referer_security_conf(rc: _RefererConf) -> str:
r_conf_list = ["#SECURITY-START 防盗链配置"]
cond_format = " RewriteCond %{{HTTP_REFERER}} !{} [NC]"
if rc.http_status == "false":
r_conf_list.append(cond_format.format("^$"))
r_conf_list.extend(map(lambda x: cond_format.format(x.strip()), rc.domains.split(",")))
rule_format = " RewriteRule .({}) {} "
if rc.return_rule[0] == "/":
r_conf_list.append(rule_format.format(
"|".join(map(lambda x: x.strip(), rc.fix.split(","))),
rc.return_rule
))
else:
r_conf_list.append(rule_format.format(
"|".join(map(lambda x: x.strip(), rc.fix.split(","))),
"/{s}.html [R={s},NC,L]".format(s=rc.return_rule)
))
r_conf_list.append(" #SECURITY-END")
return "\n".join(r_conf_list)
# 根据配置正则确定位置 并将配置文件添加进去 use_start 参数指定添加的前后
def _add_apache_referer_security_by_rep_idx(self,
rep: re.Pattern,
use_start: bool,
ap_conf, ap_file, r_conf) -> bool:
tmp_conf_list = []
last_idx = 0
for tmp in rep.finditer(ap_conf):
tmp_conf_list.append(ap_conf[last_idx:tmp.start()])
if use_start:
tmp_conf_list.append("\n" + r_conf + "\n")
tmp_conf_list.append(tmp.group())
else:
tmp_conf_list.append(tmp.group())
tmp_conf_list.append("\n" + r_conf + "\n")
last_idx = tmp.end()
if last_idx == 0:
return False
tmp_conf_list.append(ap_conf[last_idx:])
_conf = "".join(tmp_conf_list)
write_file(ap_file, _conf)
if self.webserver == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return False
return True
def _set_apache_referer_security(self, rc: _RefererConf) -> Optional[str]:
ap_file = '/www/server/panel/vhost/apache/{}{}.conf'.format(self.config_prefix, rc.name)
ap_conf = read_file(ap_file)
if not isinstance(ap_conf, str):
return "nginx配置文件丢失无法设置"
rep_security = re.compile(r"#\s*SECURITY-START(.|\n)#SECURITY-END.*\n")
res = rep_security.search(ap_conf)
if rc.status == "true":
r_conf = self._build_apache_referer_security_conf(rc)
if res is not None:
new_conf_list = []
_idx = 0
for tmp_res in rep_security.finditer(ap_conf):
new_conf_list.append(ap_conf[_idx:tmp_res.start()])
new_conf_list.append("\n" + r_conf + "\n")
_idx = tmp_res.end()
new_conf_list.append(ap_conf[_idx:])
new_conf = "".join(new_conf_list)
write_file(ap_file, new_conf)
if self.webserver == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return "配置修改失败"
rep_redirect_include = re.compile(r"IncludeOptional +.*/redirect/.*\*\.conf.*\n", re.M)
rep_custom_log = re.compile(r"CustomLog .*\n")
rep_deny_files = re.compile(r"\n\s*#DENY FILES")
if self._add_apache_referer_security_by_rep_idx(rep_redirect_include, False, ap_conf, ap_file, r_conf):
return
if self._add_apache_referer_security_by_rep_idx(rep_custom_log, False, ap_conf, ap_file, r_conf):
return
if self._add_apache_referer_security_by_rep_idx(rep_deny_files, True, ap_conf, ap_file, r_conf):
return
return "设置添加失败"
else:
if res is None:
return
new_conf_list = []
_idx = 0
for tmp_res in rep_security.finditer(ap_conf):
new_conf_list.append(ap_conf[_idx:tmp_res.start()])
_idx = tmp_res.end()
new_conf_list.append(ap_conf[_idx:])
new_conf = "".join(new_conf_list)
write_file(ap_file, new_conf)
if self.webserver == "apache" and check_server_config() is not None:
write_file(ap_file, ap_conf)
return "配置修改失败"
def get_referer_security(self, site_name) -> Optional[dict]:
r = self.get_config(site_name)
if r is None:
return None
return json.loads(str(r))
def remove_site_referer_info(self, site_name):
file_path = "{}/{}{}_door_chain.json".format(self._referer_conf_dir, self.config_prefix, site_name)
if os.path.exists(file_path):
os.remove(file_path)
# 从配置文件中获取referer配置信息
# 暂时不实现,意义不大
def _get_referer_security_by_conf(self, site_name):
if self.webserver == "nginx":
self._get_nginx_referer_security()
else:
self._get_apache_referer_security()
class Referer:
def __init__(self, config_prefix: str):
self.config_prefix: str = config_prefix
self._r = RealReferer(self.config_prefix)
def get_referer_security(self, get):
try:
site_name = get.site_name.strip()
except AttributeError:
return json_response(status=False, msg="Parameter error")
data = self._r.get_referer_security(site_name)
if data is None:
default_conf = {
"name": site_name,
"fix": "jpg,jpeg,gif,png,js,css",
"domains": "",
"status": "false",
"return_rule": "404",
"http_status": "false",
}
site_info = DB("sites").where("name=?", (site_name,)).field('id').find()
if not isinstance(site_info, dict):
return json_response(status=False, msg="Site query error")
domains_info = DB("domain").where("pid=?", (site_info["id"],)).field('name').select()
if not isinstance(domains_info, list):
return json_response(status=False, msg="Site query error")
default_conf["domains"] = ",".join(map(lambda x: x["name"], domains_info))
return json_response(status=True, data=default_conf)
return json_response(status=True, data=data)
def set_referer_security(self, get):
r = self._r.check_args(get)
if isinstance(r, str):
return json_response(status=False, msg=r)
flag, msg = self._r.set_referer_security(r)
return json_response(status=flag, msg=msg)

View File

@@ -0,0 +1,103 @@
import os
import re
import shutil
import public
class NginxExtension:
_EXTENSION_DIR = "{}/vhost/nginx/extension".format(public.get_panel_path())
@classmethod
def set_extension(cls, site_name: str, config_path: str) -> str:
config_data = public.readFile(config_path)
if not config_data:
return ""
return cls.set_extension_by_config(site_name, config_data)
@classmethod
def set_extension_by_config(cls, site_name: str, config_data: str) -> str:
ext_path = "{}/{}".format(cls._EXTENSION_DIR, site_name)
if os.path.exists(ext_path):
if not os.path.isdir(ext_path):
os.remove(ext_path)
os.makedirs(ext_path)
else:
os.makedirs(ext_path)
rep_exp_list=[
re.compile(r"(?<!#)server\s*{(([^{\n]*\n)|(\s*#.*\n)){0,20}\s*root\s+/[^\n]*\n"),
re.compile(r"(?<!#)server\s*{(([^{\n]*\n)|(\s*#.*\n)){0,20}\s*index\s+[^\n]*\n"),
re.compile(r"(?<!#)server\s*{(([^{\n]*\n)|(\s*#.*\n)){0,20}\s*server_name\s+[^\n]*\n"),
]
insert_ext = " include {}/*.conf;\n".format(ext_path)
for rep_exp in rep_exp_list:
find_list = list(re.finditer(rep_exp, config_data))
if not find_list:
continue
for tmp in find_list[::-1]:
config_data = config_data[:tmp.end()] + insert_ext + config_data[tmp.end():]
break
return config_data
@classmethod
def remove_extension(cls, site_name: str, config_path: str):
ext_path = "{}/{}".format(cls._EXTENSION_DIR, site_name)
if os.path.isdir(ext_path):
shutil.rmtree(ext_path)
config_data= public.readFile(config_path)
if not config_data:
return None
return cls.remove_extension_from_config(site_name, config_data)
@staticmethod
def remove_extension_from_config(site_name: str, config_data: str):
regexp = re.compile(r"\s*include\s+/.*extension/.*/\*\.conf;[^\n]*\n")
return re.sub(regexp, "\n", config_data)
@staticmethod
def has_extension(conf_data: str) -> bool:
regexp = re.compile(r"\s*include\s+/.*extension/.*/\*\.conf;[^\n]*\n")
return bool(re.search(regexp, conf_data))
class ApacheExtension(NginxExtension):
_EXTENSION_DIR = "{}/vhost/apache/extension".format(public.get_panel_path())
@classmethod
def set_extension_by_config(cls, site_name: str, config_data: str) -> str:
ext_path = "{}/{}".format(cls._EXTENSION_DIR, site_name)
if not os.path.exists(ext_path):
os.makedirs(ext_path)
else:
if not os.path.isdir(ext_path):
os.remove(ext_path)
os.makedirs(ext_path)
rep_exp_list=[
re.compile(r"<VirtualHost\s+\S+:\d+>\s(.*\n){0,8}\s*ServerAlias\s+[^\n]*\n"),
re.compile(r"<VirtualHost\s+\S+:\d+>\s(.*\n){0,6}\s*DocumentRoot\s+[^\n]*\n"),
]
insert_ext = " IncludeOptional {}/*.conf\n".format(ext_path)
for rep_exp in rep_exp_list:
find_list = list(re.finditer(rep_exp, config_data))
if not find_list:
continue
for tmp in find_list[::-1]:
config_data = config_data[:tmp.end()] + insert_ext + config_data[tmp.end():]
break
return config_data
@staticmethod
def remove_extension_from_config(site_name: str, config_data: str):
regexp = re.compile(r"\s*IncludeOptional\s+/.*extension/.*/\*\.conf[^\n]*\n")
return re.sub(regexp, "\n", config_data)
@staticmethod
def has_extension(conf_data: str) -> bool:
regexp = re.compile(r"\s*IncludeOptional\s+/.*extension/.*/\*\.conf[^\n]*\n")
return bool(re.search(regexp, conf_data))

1335
mod/base/web_conf/ssl.py Normal file

File diff suppressed because it is too large Load Diff

200
mod/base/web_conf/util.py Normal file
View File

@@ -0,0 +1,200 @@
import os
import sys
from typing import Optional, Tuple, Callable
if "/www/server/panel/class" not in sys.path:
sys.path.insert(0, "/www/server/panel/class")
import public
def webserver() -> Optional[str]:
if os.path.exists('/www/server/nginx/sbin/nginx'):
web_server = 'nginx'
elif os.path.exists('/www/server/apache/bin/apachectl'):
web_server = 'apache'
elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
web_server = 'openlitespeed'
else:
web_server = None
return web_server
def check_server_config() -> Optional[str]:
w_s = webserver()
setup_path = "/www/server"
if w_s == 'nginx':
shell_str = (
"ulimit -n 8192; "
"{setup_path}/nginx/sbin/nginx -t -c {setup_path}/nginx/conf/nginx.conf"
).format(setup_path=setup_path)
result: Tuple[str, str] = public.ExecShell(shell_str)
searchStr = 'successful'
elif w_s == 'apache':
shell_str = (
"ulimit -n 8192; "
"{setup_path}/apache/bin/apachectl -t"
).format(setup_path=setup_path)
result: Tuple[str, str] = public.ExecShell(shell_str)
searchStr = 'Syntax OK'
else:
return None
if result[1].find(searchStr) == -1:
public.WriteLog("TYPE_SOFT", 'CONF_CHECK_ERR', (result[1],))
return result[1]
def read_file(filename, mode='r') -> Optional[str]:
"""
读取文件内容
@filename 文件名
return string(bin) 若文件不存在则返回None
"""
import os
if not os.path.exists(filename):
return None
fp = None
try:
fp = open(filename, mode=mode)
f_body = fp.read()
except:
return None
finally:
if fp and not fp.closed:
fp.close()
return f_body
def write_file(filename: str, s_body: str, mode='w+') -> bool:
"""
写入文件内容
@filename 文件名
@s_body 欲写入的内容
return bool 若文件不存在则尝试自动创建
"""
try:
fp = open(filename, mode=mode)
fp.write(s_body)
fp.close()
return True
except:
try:
fp = open(filename, mode=mode, encoding="utf-8")
fp.write(s_body)
fp.close()
return True
except:
return False
def debug_api_warp(fn):
def inner(*args, **kwargs):
try:
return fn(*args, **kwargs)
except:
public.print_log(public.get_error_info())
return {
}
return inner
# 重载Web服务配置
def service_reload():
setup_path = "/www/server"
if os.path.exists('{}/nginx/sbin/nginx'.format(setup_path)):
result = public.ExecShell('/etc/init.d/nginx reload')
if result[1].find('nginx.pid') != -1:
public.ExecShell('pkill -9 nginx && sleep 1')
public.ExecShell('/etc/init.d/nginx start')
elif os.path.exists('{}/apache/bin/apachectl'.format(setup_path)):
result = public.ExecShell('/etc/init.d/httpd reload')
else:
result = public.ExecShell('rm -f /tmp/lshttpd/*.sock* && /usr/local/lsws/bin/lswsctrl restart')
return result
# 防正则转译
def pre_re_key(input_str: str) -> str:
re_char = ['$', '(', ')', '*', '+', '.', '[', ']', '{', '}', '?', '^', '|', '\\']
res = []
for i in input_str:
if i in re_char:
res.append("\\" + i)
else:
res.append(i)
return "".join(res)
def get_log_path() -> str:
log_path = public.readFile("{}/data/sites_log_path.pl".format(public.get_panel_path()))
if isinstance(log_path, str) and os.path.isdir(log_path):
return log_path
return public.GetConfigValue('logs_path')
# 2024/4/18 上午9:44 域名编码转换
def to_puny_code(domain):
try:
try:
import idna
except:
os.system("btpip install idna -I")
import idna
import re
match = re.search(u"[^u\0000-u\001f]+", domain)
if not match:
return domain
try:
if domain.startswith("*."):
return "*." + idna.encode(domain[2:]).decode("utf8")
else:
return idna.encode(domain).decode("utf8")
except:
return domain
except:
return domain
# 2024/4/18 下午5:48 中文路径处理
def to_puny_code_path(path):
if sys.version_info[0] == 2: path = path.encode('utf-8')
if os.path.exists(path): return path
import re
match = re.search(u"[\x80-\xff]+", path)
if not match: match = re.search(u"[\u4e00-\u9fa5]+", path)
if not match: return path
npath = ''
for ph in path.split('/'):
npath += '/' + to_puny_code(ph)
return npath.replace('//', '/')
class _DB:
def __call__(self, table: str):
import db
with db.Sql() as t:
t.table(table)
return t
DB = _DB()
GET_CLASS = public.dict_obj
listen_ipv6: Callable[[], bool] = public.listen_ipv6
ExecShell: Callable = public.ExecShell
def use_http2() -> bool:
versionStr = public.readFile('/www/server/nginx/version.pl')
if isinstance(versionStr, str):
if versionStr.find('1.8.1') == -1:
return True
return False