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,8 @@
from .limit_net import LimitNet
from .redirect import Redirect
__all__ = [
"LimitNet",
"Redirect"
]

View File

@@ -0,0 +1,47 @@
from typing import Optional
class BaseProjectCommon:
setup_path = "/www/server/panel"
_allow_mod_name = {
"go", "java", "net", "nodejs", "other", "python", "proxy",
}
def get_project_mod_type(self) -> Optional[str]:
_mod_name = self.__class__.__module__
# "projectModel/javaModel.py" 的格式
if "/" in _mod_name:
_mod_name = _mod_name.replace("/", ".")
if _mod_name.endswith(".py"):
mod_name = _mod_name[:-3]
else:
mod_name = _mod_name
# "projectModel.javaModel" 的格式
if "." in mod_name:
mod_name = mod_name.rsplit(".", 1)[1]
if mod_name.endswith("Model"):
return mod_name[:-5]
if mod_name in self._allow_mod_name:
return mod_name
return None
@property
def config_prefix(self) -> Optional[str]:
if getattr(self, "_config_prefix_cache", None) is not None:
return getattr(self, "_config_prefix_cache")
p_name = self.get_project_mod_type()
if p_name == "nodejs":
p_name = "node"
if isinstance(p_name, str):
p_name = p_name + "_"
setattr(self, "_config_prefix_cache", p_name)
return p_name
@config_prefix.setter
def config_prefix(self, prefix: str):
setattr(self, "_config_prefix_cache", prefix)

View File

@@ -0,0 +1,239 @@
import os
import re
from typing import Tuple
import public
from .base import BaseProjectCommon
class LimitNet(BaseProjectCommon):
def get_limit_net(self, get):
if public.get_webserver() != 'nginx':
return public.returnMsg(False, 'SITE_NETLIMIT_ERR')
try:
site_id = int(get.site_id)
except (AttributeError, TypeError, ValueError):
return public.returnMsg(False, "The parameter is incorrect")
if self.config_prefix is None:
return public.returnMsg(False, "Unsupported website types")
# 取配置文件
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, "Configuration file read error")
# 站点总并发
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, "The parameter is incorrect")
if per_server < 1 or perip < 1 or limit_rate < 1:
return public.returnMsg(False, 'The concurrency limit, IP limit, and traffic limit must be greater than 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, "参数错误")
# 取回配置文件
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,832 @@
import os,sys
import re
import json
import hashlib
import time
from typing import Tuple, Optional, Union, Dict, List
from urllib import parse
from itertools import product
import public
from public.validate import Param
# from .base import BaseProjectCommon
class _RealRedirect:
setup_path = "/www/server/panel"
_redirect_conf_file = "{}/data/redirect.conf".format(setup_path)
_ng_domain_format = """
if ($host ~ '^%s'){
return %s %s%s;
}
"""
_ng_path_format = """
rewrite ^%s(.*) %s%s %s;
"""
_ap_domain_format = """
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %%{HTTP_HOST} ^%s [NC]
RewriteRule ^(.*) %s%s [L,R=%s]
</IfModule>
"""
_ap_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 = public.get_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(public.readFile(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 public.writeFile(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 public.checkWebConfig() is not True:
return public.lang("Config file error; please check the configuration first.")
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 'The parameter is incorrect'
if not is_modify:
if not redirect_name:
return public.lang("Parameter error: configuration name cannot be empty")
# 检测名称是否重复
if not (3 < len(redirect_name) < 15):
return public.lang("Name length must be greater than 3 and less than 15 characters")
if self._check_redirect(site_name, redirect_name, error_page == 1):
return public.lang("The specified redirect name already exists")
site_info = public.M('sites').where("name=?", (site_name,)).find()
if not isinstance(site_info, dict):
return public.lang("Failed to query site information")
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 public.lang("Invalid target URL format: [%s]") % to_url
# 非404页面de重定向检测项
if error_page != 1:
# 检测是否选择域名
if domain_or_path == "domain":
if not redirect_domain:
return public.lang("Please select a redirect domain")
# 检测域名是否已经存在配置文件
repeat_domain = self._check_redirect_domain_exist(site_name, redirect_domain, redirect_name, is_modify)
if repeat_domain:
return public.lang("Redirect domain already exists: %s") % repeat_domain
# 检查目标URL的域名和被重定向的域名是否一样
tu = self._parse_url_domain(to_url)
for d in redirect_domain:
if d == tu:
return public.lang("Domain \"%s\" matches the target domain; please deselect it") % d
else:
if not redirect_path:
return public.lang("Please enter a redirect path")
if redirect_path[0] != "/":
return public.lang("Invalid path format; expected /xxx")
# 检测路径是否有存在配置文件
if self._check_redirect_path_exist(site_name, redirect_path, redirect_name):
return public.lang("Redirect path already exists: %s") % redirect_path
to_url_path = self._parse_url_path(to_url)
if to_url_path.startswith(redirect_path):
return public.lang("Target URL [%s] starts with the redirect path [%s], which will cause a loop") % (to_url_path, redirect_path)
# 404页面重定向检测项
else:
if not to_url and not to_path:
return public.lang("You must choose either the homepage or a custom page")
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):
res_conf = self._check_redirect_args(get, is_modify=False)
if isinstance(res_conf, str):
return public.returnMsg(False, res_conf)
res = self._set_include(res_conf)
if res is not None:
return public.returnMsg(False, res)
res = self._write_config(res_conf)
if res is not None:
return public.returnMsg(False, res)
self.config.append(res_conf)
self.save_config()
public.serviceReload()
return public.returnMsg(True, public.lang("Created successfully"))
def _set_include(self, res_conf) -> Optional[str]:
flag, msg = self._set_nginx_redirect_include(res_conf)
if not flag:
return msg
flag, msg = self._set_apache_redirect_include(res_conf)
if not flag:
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):
"""
@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 public.returnMsg(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 public.returnMsg(False, res)
res = self._write_config(res_conf)
if res is not None:
return public.returnMsg(False, res)
if old_idx:
self.config[old_idx].update(res_conf)
else:
self.config.append(res_conf)
self.save_config()
public.serviceReload()
return public.returnMsg(True, public.lang("Modification successful"))
def _set_nginx_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ng_redirect_dir = "%s/vhost/nginx/redirect/%s" % (self.setup_path, redirect_conf["sitename"])
ng_file = "{}/vhost/nginx/{}{}.conf".format(self.setup_path, self.config_prefix, redirect_conf["sitename"])
if not os.path.exists(ng_redirect_dir):
os.makedirs(ng_redirect_dir, 0o600)
ng_conf = public.readFile(ng_file)
if not isinstance(ng_conf, str):
return False, public.lang("Failed to read nginx config file")
rep_include = re.compile(r"\sinclude +.*/redirect/.*\*\.conf;", re.M)
if rep_include.search(ng_conf):
return True, ""
redirect_include = (
"#SSL-END\n"
" # Include redirect rules, commenting out will disable the configured redirect proxy\n"
" include {}/*.conf;"
).format(ng_redirect_dir)
if "#SSL-END" not in ng_conf:
return False, public.lang("Failed to add config: cannot locate SSL config marker")
new_conf = ng_conf.replace("#SSL-END", redirect_include)
public.writeFile(ng_file, new_conf)
if self.webserver == "nginx" and public.checkWebConfig() is not True:
public.writeFile(ng_file, ng_conf)
return False, public.lang("Failed to add config")
return True, ""
def _un_set_nginx_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ng_file = "{}/vhost/nginx/{}{}.conf".format(self.setup_path, self.config_prefix, redirect_conf["sitename"])
ng_conf = public.readFile(ng_file)
if not isinstance(ng_conf, str):
return False, public.lang("Failed to read nginx config file")
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)
public.writeFile(ng_file, new_conf)
if self.webserver == "nginx" and public.checkWebConfig() is not True:
public.writeFile(ng_file, ng_conf)
return False, public.lang("Failed to remove config")
return True, ""
def _set_apache_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ap_redirect_dir = "%s/vhost/apache/redirect/%s" % (self.setup_path, redirect_conf["sitename"])
ap_file = "{}/vhost/apache/{}{}.conf".format(self.setup_path, self.config_prefix, redirect_conf["sitename"])
if not os.path.exists(ap_redirect_dir):
os.makedirs(ap_redirect_dir, 0o600)
ap_conf = public.readFile(ap_file)
if not isinstance(ap_conf, str):
return False, public.lang("Failed to read apache config file")
rep_include = re.compile(r"\sIncludeOptional +.*/redirect/.*\*\.conf", re.M)
# public.print_log(list(rep_include.finditer(ap_conf)))
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 # Include redirect rules, commenting out will disable the configured redirect proxy\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)
public.writeFile(ap_file, new_conf)
if self.webserver == "apache" and public.checkWebConfig() is not True:
public.writeFile(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, public.lang("Failed to set config")
def _un_set_apache_redirect_include(self, redirect_conf: dict) -> Tuple[bool, str]:
ap_file = "{}/vhost/apache/{}{}.conf".format(self.setup_path, self.config_prefix, redirect_conf["sitename"])
ap_conf = public.readFile(ap_file)
if not isinstance(ap_conf, str):
return False, public.lang("Failed to read apache config file")
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)
public.writeFile(ap_file, new_conf)
if self.webserver == "apache" and public.checkWebConfig() is not True:
public.writeFile(ap_file, ap_conf)
return False, public.lang("Failed to remove config")
return True, ""
def write_nginx_redirect_file(self, redirect_conf: dict) -> Optional[str]:
conf_file = "{}/vhost/nginx/redirect/{}/{}_{}.conf".format(
self.setup_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_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_path_format % (redirect_path, to_url, hold_path, redirect_type))
conf_list.append("#REWRITE-END")
conf_data = "\n".join(conf_list)
public.writeFile(conf_file, conf_data)
if self.webserver == "nginx":
isError = public.checkWebConfig()
if isError is not True:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + isError.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.setup_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_domain_format % (
sd, to_url, hold_path, redirect_conf["redirecttype"]
))
else:
redirect_path = redirect_conf["redirectpath"]
conf_list.append(self._ap_path_format % (redirect_path, to_url, hold_path, redirect_conf["redirecttype"]))
conf_list.append("#REWRITE-END")
public.writeFile(conf_file, "\n".join(conf_list))
if self.webserver == "apache":
isError = public.checkWebConfig()
if isError is not True:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>'
def unset_nginx_404_conf(self, site_name):
"""
清理已有的 404 页面 配置
"""
need_clear_files = [
"{}/vhost/nginx/{}{}.conf".format(self.setup_path, self.config_prefix, site_name),
"{}/vhost/nginx/rewrite/{}{}.conf".format(self.setup_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": public.readFile(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
public.writeFile(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.setup_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)
public.writeFile(conf_file, conf_data)
if self.webserver == "nginx":
isError = public.checkWebConfig()
if isError is not True:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + isError.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.setup_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"])
public.writeFile(conf_file, conf_data)
if self.webserver == "apache":
isError = public.checkWebConfig()
if isError is not True:
if os.path.exists(conf_file):
os.remove(conf_file)
return 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>'
def remove_redirect(self, get, multiple=None):
try:
site_name = get.sitename.strip()
redirect_name = get.redirectname.strip()
except AttributeError:
return public.returnMsg(False, public.lang("Parameter error"))
target_idx = None
have_other_redirect = False
target_conf = None
for i, conf in enumerate(self.config): # index i
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: # target_idx 可以为0
return public.returnMsg(False, public.lang("No matching configuration found"))
r_md5_name = self._calc_redirect_name_md5(target_conf["redirectname"])
public.ExecShell("rm -f %s/vhost/nginx/redirect/%s/%s_%s.conf" % (
self.setup_path, site_name, r_md5_name, site_name))
public.ExecShell("rm -f %s/vhost/apache/redirect/%s/%s_%s.conf" % (
self.setup_path, site_name, r_md5_name, site_name))
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:
public.serviceReload()
return public.returnMsg(True, public.lang("Deleted successfully"))
def mutil_remove_redirect(self, get):
try:
redirect_names = json.loads(get.redirectnames.strip())
site_name = json.loads(get.sitename.strip())
except (AttributeError, json.JSONDecodeError, TypeError):
return public.returnMsg(False, public.lang("Parameter error"))
del_successfully = []
del_failed = {}
get_obj = public.dict_obj()
for redirect_name in redirect_names:
get_obj.redirectname = redirect_name
get_obj.sitename = site_name
try:
result = self.remove_redirect(get, multiple=1)
if not result['status']:
del_failed[redirect_name] = result['msg']
continue
del_successfully.append(redirect_name)
except:
del_failed[redirect_name] = public.lang("An error occurred while deleting; please try again")
public.serviceReload()
msg = 'Successfully deleted redirects [ {} ]'.format(','.join(del_successfully))
if del_failed:
msg += '; Failed to delete redirects: '
for k in del_failed:
msg += ' {} => {}; '.format(k, del_failed[k])
return {
'status': True,
'msg': msg,
}
def get_redirect_list(self, get):
try:
error_page = None
site_name = get.sitename.strip()
if "errorpage" in get:
error_page = int(get.errorpage)
except Exception:
return public.return_message(-1,0, "parameter error")
redirect_list = []
webserver = public.get_webserver()
if webserver == 'openlitespeed':
webserver = '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 page']
md5_name = self._calc_redirect_name_md5(conf['redirectname'])
conf["redirect_conf_file"] = "%s/vhost/%s/redirect/%s/%s_%s.conf" % (
self.setup_path, webserver, site_name, md5_name, site_name)
conf["type"] = 1 if os.path.isfile(conf["redirect_conf_file"]) else 0
redirect_list.append(conf)
return public.returnMsg(True, redirect_list)
def remove_redirect_by_project_name(self, project_name):
for i in range(len(self.config) - 1, -1, -1):
if self.config[i]["sitename"] == project_name:
del self.config[i]
self.save_config()
m_path = self.setup_path + '/vhost/nginx/redirect/' + project_name
if os.path.exists(m_path):
public.ExecShell("rm -rf %s" % m_path)
m_path = self.setup_path + '/vhost/apache/redirect/' + project_name
if os.path.exists(m_path):
public.ExecShell("rm -rf %s" % m_path)
def test_api_warp(fn):
def inner(*args, **kwargs):
try:
return fn(*args, **kwargs)
except:
public.print_log(public.get_error_info())
return inner
class BaseProjectCommon:
setup_path = "/www/server/panel"
_allow_mod_name = {
"go", "java", "net", "nodejs", "other", "python", "proxy",
}
def get_project_mod_type(self) -> Optional[str]:
_mod_name = self.__class__.__module__
# "projectModel/javaModel.py" 的格式
if "/" in _mod_name:
_mod_name = _mod_name.replace("/", ".")
if _mod_name.endswith(".py"):
mod_name = _mod_name[:-3]
else:
mod_name = _mod_name
# "projectModel.javaModel" 的格式
if "." in mod_name:
mod_name = mod_name.rsplit(".", 1)[1]
if mod_name.endswith("Model"):
return mod_name[:-5]
if mod_name in self._allow_mod_name:
return mod_name
return None
@property
def config_prefix(self) -> Optional[str]:
if getattr(self, "_config_prefix_cache", None) is not None:
return getattr(self, "_config_prefix_cache")
p_name = self.get_project_mod_type()
if p_name == "nodejs":
p_name = "node"
if isinstance(p_name, str):
p_name = p_name + "_"
setattr(self, "_config_prefix_cache", p_name)
return p_name
@config_prefix.setter
def config_prefix(self, prefix: str):
setattr(self, "_config_prefix_cache", prefix)
class Redirect(BaseProjectCommon):
"""项目重定向管理"""
@staticmethod
def aa_return(fun):
"""统一返回格式"""
def inner(*args, **kwargs):
try:
res = fun(*args, **kwargs)
if res and isinstance(res, dict):
msg = res["msg"] # 故意抛异常
msg = public.gettext_msg(msg) if isinstance(msg, str) else msg
return public.return_message(
0 if res.get("status", False) else -1, 0, msg
)
else:
public.print_log("Error: Unexpected return value: {}".format(res))
return public.return_message(0, 0, res)
except Exception as e:
public.print_log("Redirect return format Error: {}".format(e))
return fun(*args, **kwargs)
return inner
@aa_return
def remove_redirect_by_project_name(self, project_name):
if not isinstance(self.config_prefix, str):
return None
return _RealRedirect(self.config_prefix).remove_redirect_by_project_name(project_name)
@aa_return
def create_project_redirect(self, get):
if not isinstance(self.config_prefix, str):
return public.returnMsg(False, "Unsupported website type")
return _RealRedirect(self.config_prefix).create_redirect(get)
@aa_return
def modify_project_redirect(self, get):
# 批量走site老接口
if not isinstance(self.config_prefix, str):
return public.returnMsg(False, "Unsupported website type")
return _RealRedirect(self.config_prefix).modify_redirect(get)
@aa_return
def remove_project_redirect(self, get):
if not isinstance(self.config_prefix, str):
return public.returnMsg(False, "Unsupported website type")
return _RealRedirect(self.config_prefix).remove_redirect(get)
@aa_return
def mutil_remove_project_redirect(self, get):
if not isinstance(self.config_prefix, str):
return public.returnMsg(False, "Unsupported website type")
return _RealRedirect(self.config_prefix).mutil_remove_redirect(get)
@aa_return
def get_project_redirect_list(self, get):
# 校验参数
try:
get.validate([
Param('sitename').String(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
self.config_prefix='proxy_'
if not isinstance(self.config_prefix, str):
return public.return_message(-1,0, "Unsupported website type")
return _RealRedirect(self.config_prefix).get_redirect_list(get)