# coding: utf-8 # ------------------------------------------------------------------- # yakpanel # ------------------------------------------------------------------- # Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved. # ------------------------------------------------------------------- # Author: wzz # ------------------------------------------------------------------- import json import os # ------------------------------ # 反向代理模型 # ------------------------------ import sys if "/www/server/panel/class" not in sys.path: sys.path.insert(0, "/www/server/panel/class") os.chdir("/www/server/panel") import public from public.validate import Param class main(): def __init__(self): self._proxy_path = '/www/server/proxy_project' self._proxy_config_path = self._proxy_path + '/sites' self._site_proxy_conf_path = "" if not os.path.exists(self._proxy_config_path): public.ExecShell('mkdir -p {}'.format(self._proxy_config_path)) public.ExecShell('chown -R www:www {}'.format(self._proxy_config_path)) public.ExecShell('chmod -R 755 {}'.format(self._proxy_config_path)) self._init_proxy_conf = { "site_name": "", "domain_list": [], "site_port": [], "https_port": "443", "ipv4_port_conf": "listen {listen_port};", "ipv6_port_conf": "listen [::]:{listen_port};", "port_conf": "listen {listen_port};{listen_ipv6}", "ipv4_ssl_port_conf": "{ipv4_port_conf}\n listen {https_port} ssl http2 ;", "ipv6_ssl_port_conf": "{ipv6_port_conf}\n listen [::]:{https_port} ssl http2 ;", "ipv4_http3_ssl_port_conf": "{ipv4_port_conf}\n listen {https_port} quic;\n listen {https_port} ssl;", "ipv6_http3_ssl_port_conf": "{ipv6_port_conf}\n listen [::]:{https_port} quic;\n listen [::]:{https_port} ssl ;", "site_path": "", "ssl_info": { "ssl_status": False, "ssl_default_conf": "#error_page 404/404.html;", "ssl_conf": '#error_page 404/404.html;\n ssl_certificate /www/server/panel/vhost/cert/{site_name}/fullchain.pem;\n ssl_certificate_key /www/server/panel/vhost/cert/{site_name}/privkey.pem;\n ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;\n ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;\n ssl_prefer_server_ciphers on;\n ssl_session_cache shared:SSL:10m;\n ssl_session_timeout 10m;\n add_header Strict-Transport-Security "max-age=31536000";\n error_page 497 https://$host$request_uri;', "force_ssl_conf": '#error_page 404/404.html;{force_conf}\n ssl_certificate /www/server/panel/vhost/cert/{site_name}/fullchain.pem;\n ssl_certificate_key /www/server/panel/vhost/cert/{site_name}/privkey.pem;\n ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;\n ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;\n ssl_prefer_server_ciphers on;\n ssl_session_cache shared:SSL:10m;\n ssl_session_timeout 10m;\n add_header Strict-Transport-Security "max-age=31536000";\n error_page 497 https://$host$request_uri;', "force_https": False, "force_conf": " #HTTP_TO_HTTPS_START\n if ($server_port !~ 443){\n rewrite ^(/.*)$ https://$host$1 permanent;\n }\n #HTTP_TO_HTTPS_END", }, "err_age_404": "", "err_age_502": "", "ip_limit": { "ip_black": [], "ip_white": [], }, "basic_auth": [], "proxy_cache": { "cache_status": False, "cache_zone": "", "static_cache": "", "expires": "1d", "cache_conf": "", }, "gzip": { "gzip_status": False, "gzip_min_length": "1k", "gzip_comp_level": "6", "gzip_types": "text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/json image/jpeg image/gif image/png font/ttf font/otf image/svg+xml application/xml+rss text/x-js", "gzip_conf": "gzip on;\n gzip_min_length 10k;\n gzip_buffers 4 16k;\n gzip_http_version 1.1;\n gzip_comp_level 2;\n gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;\n gzip_vary on;\n gzip_proxied expired no-cache no-store private auth;\n gzip_disable \"MSIE [1-6]\\.\";", }, "subs_filter": False, "sub_filter": { "sub_filter_str": [], }, "rewritedir":[], "websocket": { "websocket_status": True, "websocket_conf": "proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";", }, "security": { "security_status": False, "static_resource": "jpg|jpeg|gif|png|js|css", "return_resource": "404", "http_status": False, "domains": "", "security_conf": " #SECURITY-START Anti theft chain configuration" "\n location ~ .*\\.({static_resource})$" "\n {{\n expires {expires};" "\n access_log /dev/null;" "\n valid_referers {domains};" "\n if ($invalid_referer){{" "\n return {return_resource};" "\n }}" "\n }}\n #SECURITY-END", }, "redirect": { "redirect_status": False, "redirect_conf": " #Referencing redirection rules, the redirection proxy configured after annotation will be invalid\n include /www/server/panel/vhost/nginx/redirect/{site_name}/*.conf;", }, "proxy_log": { "log_type": "default", "server_port": "", "log_path": "", "log_conf": "\naccess_log {log_path}/{site_name}.log;\n error_log {log_path}/{site_name}.error.log;", }, "default_cache": "proxy_cache_path /www/wwwroot/{site_name}/proxy_cache_dir levels=1:2 keys_zone={cache_name}_cache:20m inactive=1d max_size=5g;", "default_describe": "# If there is abnormal access to the reverse proxy website and the content has already been configured here, please prioritize checking if the configuration here is correct\n", "http_block": "", "server_block": "", "remark": "", "proxy_info": [], } self._template_conf = r'''{http_block} server {{ {port_conf} server_name {domains}; index index.php index.html index.htm default.php default.htm default.html; root {site_path}; #CERT-APPLY-CHECK--START # Configuration related to file verification for SSL certificate application - Do not delete include /www/server/panel/vhost/nginx/well-known/{site_name}.conf; #CERT-APPLY-CHECK--END #SSL-START {ssl_start_msg} {ssl_info} #SSL-END #REDIRECT START {redirect_conf} #REDIRECT END #ERROR-PAGE-START {err_page_msg} {err_age_404} {err_age_502} #ERROR-PAGE-END #PHP-INFO-START PHP reference configuration, can be annotated or modified {security_conf} include enable-php-00.conf; #PHP-INFO-END #IP-RESTRICT-START Restrict access to IP configuration, IP blacklist and whitelist {ip_limit_conf} #IP-RESTRICT-END #BASICAUTH START {auth_conf} #BASICAUTH END #SUB_FILTER START {sub_filter} #SUB_FILTER END #GZIP START {gzip_conf} #GZIP END #GLOBAL-CACHE START {proxy_cache} #GLOBAL-CACHE END #WEBSOCKET-SUPPORT START {websocket_support} #WEBSOCKET-SUPPORT END #PROXY-CONF-START {proxy_conf} #PROXY-CONF-END #SERVER-BLOCK START {server_block} #SERVER-BLOCK END #Prohibited access to files or directories location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {{ return 404; }} #One click application for SSL certificate verification directory related settings location /.well-known{{ allow all; root /www/wwwroot/{site_name}; }} #Prohibit placing sensitive files in the certificate verification directory if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {{ return 403; }} #LOG START {server_log} {monitor_conf} #LOG END }}''' self._template_proxy_conf = '''location ^~ {proxy_path} {{ {ip_limit} {basic_auth} proxy_pass {proxy_pass}; proxy_set_header Host {proxy_host}; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-Port $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; {SNI} {timeout_conf} {websocket_support} {custom_conf} {proxy_cache} {gzip} {sub_filter} {server_log} }}''' def structure_proxy_conf(self, get): ''' @name @author wzz <2024/4/19 下午4:29> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' sni_conf = "" if get.proxy_pass.startswith("https://"): sni_conf = "proxy_ssl_server_name on;" get.proxy_conf = self._template_proxy_conf.format( ip_limit="", gzip="", proxy_cache="", sub_filter="", server_log="", basic_auth="", proxy_pass=get.proxy_pass, proxy_host=get.proxy_host, proxy_path=get.proxy_path, SNI=sni_conf, custom_conf="", timeout_conf=get.proxy_timeout, websocket_support=self._init_proxy_conf["websocket"]["websocket_conf"], rewrite_direct=self.setRewritedir(get.get("rewritedir",'[{"dir1":"","dir2":""}]')), ) get.proxy_info = { "proxy_type": get.proxy_type, "proxy_path": get.proxy_path, "proxy_pass": get.proxy_pass, "proxy_host": get.proxy_host, "keepuri": get.keepuri, "ip_limit": { "ip_black": [], "ip_white": [], }, "basic_auth": {}, "proxy_cache": { "cache_status": False, "cache_zone": get.site_name.replace(".", "_") + "_cache", "static_cache": "", "expires": "1d", "cache_conf": "", }, "gzip": { "gzip_status": False, "gzip_min_length": "1k", "gzip_comp_level": "6", "gzip_types": "text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/json image/jpeg image/gif image/png font/ttf font/otf image/svg+xml application/xml+rss text/x-js", "gzip_conf": "gzip on;\n gzip_min_length 10k;\n gzip_buffers 4 16k;\n gzip_http_version 1.1;\n gzip_comp_level 2;\n gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;\n gzip_vary on;\n gzip_proxied expired no-cache no-store private auth;\n gzip_disable \"MSIE [1-6]\\.\";", }, "sub_filter": { "sub_filter_str": [], }, "rewritedir": json.loads(get.get("rewritedir", '[{"dir1":"","dir2":""}]')), "websocket": { "websocket_status": True, "websocket_conf": "proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";", }, "proxy_log": { "log_type": "off", "log_conf": get.server_log, }, "timeout": { "proxy_connect_timeout": "60", "proxy_send_timeout": "600", "proxy_read_timeout": "600", "timeout_conf": "proxy_connect_timeout 60s;\n proxy_send_timeout 600s;\n proxy_read_timeout 600s;", }, "custom_conf": "", "proxy_conf": get.proxy_conf, "remark": "", "template_proxy_conf": self._template_proxy_conf, } ## 修改重定向 def setRewritedir(self, get): rewriteconf = "" if "rewritedir" not in get: return rewriteconf for d in json.loads(get.get("rewritedir", '[{"dir1":"","dir2":""}]')): if not d["dir1"] or not d["dir2"] or d["dir1"] == d["dir2"] or d["dir1"] == "/": continue rewriteconf += '\trewrite ^{0}/(.*)$ {1}/$1 break;'.format(d["dir1"], d["dir2"]) return rewriteconf # 2024/4/18 上午10:53 构造反向代理的配置文件 def structure_nginx(self, get): ''' @name 构造反向代理的配置文件 @author wzz <2024/4/18 上午10:54> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.err_age_404 = get.get("err_age_404", "#error_page 404 /404.html;") get.err_age_502 = get.get("err_age_502", "#error_page 502 /502.html;") get.proxy_info = get.get("proxy_info", "") get.server_log = get.get( "server_log", self._init_proxy_conf["proxy_log"]["log_conf"].format( log_path=public.get_logs_path(), site_name=get.site_name ) ) get.remark = get.get("remark", "") get.server_block = get.get("server_block", "") get.websocket_status = get.get("websocket_status", True) get.proxy_timeout = "proxy_connect_timeout 60s;\n proxy_send_timeout 600s;\n proxy_read_timeout 600s;" get.rewrite_direct_conf=self.setRewritedir(get) self.structure_proxy_conf(get) is_subs = public.ExecShell("nginx -V 2>&1|grep 'ngx_http_substitutions_filter' -o")[0] self._init_proxy_conf["subs_filter"] = True if is_subs != "" else False self._init_proxy_conf["site_name"] = get.site_name self._init_proxy_conf["domain_list"] = get.domain_list self._init_proxy_conf["site_port"] = get.port_list self._init_proxy_conf["site_path"] = get.site_path self._init_proxy_conf["err_age_404"] = get.err_age_404 self._init_proxy_conf["err_age_502"] = get.err_age_502 self._init_proxy_conf["proxy_log"]["log_conf"] = get.server_log self._init_proxy_conf["remark"] = get.remark self._init_proxy_conf["http_block"] = "" self._init_proxy_conf["proxy_info"].append(get.proxy_info) self._init_proxy_conf["proxy_cache"]["cache_zone"] = get.site_name.replace(".", "_") + "_cache" self._init_proxy_conf["rewritedir"] = json.loads(get.get("rewritedir", '[{"dir1":"","dir2":""}]')) # 2024/4/18 上午10:35 写入Nginx配置文件 def write_nginx_conf(self, get): ''' @name 写入Nginx配置文件 @author wzz <2024/4/18 上午10:36> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 这里可能会报错 self.structure_nginx(get) listen_port = " ".join(get.port_list) if len(get.port_list) > 1 else get.site_port # listen_ipv6 = "\n listen [::]:{};".format( # " ".join(get.port_list) if len(get.port_list) > 1 else get.site_port) # port_conf = self._init_proxy_conf["port_conf"].format(listen_port=listen_port, listen_ipv6=listen_ipv6) if type(listen_port) == list: ipv4_port_conf = "" ipv6_port_conf = "" for p in listen_port: ipv4_port_conf += self._init_proxy_conf["ipv4_port_conf"].format(listen_port=p) + "\n " ipv6_port_conf += self._init_proxy_conf["ipv6_port_conf"].format(listen_port=p) + "\n " else: ipv4_port_conf = self._init_proxy_conf["ipv4_port_conf"].format(listen_port=listen_port) ipv6_port_conf = self._init_proxy_conf["ipv6_port_conf"].format(listen_port=listen_port) port_conf = ipv4_port_conf + "\n" + ipv6_port_conf # 2024/6/4 下午4:20 兼容新版监控报表的配置 monitor_conf = "" if os.path.exists("/www/server/panel/plugin/monitor/monitor_main.py"): monitor_conf = '''#Monitor-Config-Start monitor log sending configuration access_log syslog:server=unix:/tmp/bt-monitor.sock,nohostname,tag={pid}__access monitor; error_log syslog:server=unix:/tmp/bt-monitor.sock,nohostname,tag={pid}__error; #Monitor-Config-End'''.format(pid=get.pid) conf = self._template_conf.format( http_block=get.http_block, server_block="", port_conf=port_conf, ssl_start_msg=public.getMsg('NGINX_CONF_MSG1'), err_page_msg=public.getMsg('NGINX_CONF_MSG2'), php_info_start=public.getMsg('NGINX_CONF_MSG3'), rewrite_start_msg=public.getMsg('NGINX_CONF_MSG4'), log_path=public.get_logs_path(), domains=' '.join(get.domain_list) if len(get.domain_list) > 1 else get.site_name, site_name=get.site_name, ssl_info="#error_page 404/404.html;", err_age_404=get.err_age_404, err_age_502=get.err_age_502, ip_limit_conf="", auth_conf="", sub_filter="", gzip_conf="", redirect_conf="", security_conf="", proxy_conf=get.proxy_conf, server_log=get.server_log, site_path=get.site_path, proxy_cache="", websocket_support=self._init_proxy_conf["websocket"]["websocket_conf"], monitor_conf=monitor_conf, ) rewriteconf=self.setRewritedir(get) if conf.find("rewrite ") == -1 and rewriteconf != "": rewrite_conf="{}\n\tproxy_pass".format(rewriteconf) conf=conf.replace("proxy_pass",rewrite_conf) # 写配置文件 well_known_path = "{}/vhost/nginx/well-known".format(public.get_panel_path()) if not os.path.exists(well_known_path): os.makedirs(well_known_path, 0o600) public.writeFile("{}/{}.conf".format(well_known_path, get.site_name), "") get.filename = public.get_setup_path() + '/panel/vhost/nginx/' + get.site_name + '.conf' return public.writeFile(get.filename, conf) # 2024/4/25 上午11:11 检查nginx是否支持http3 def check_http3_support(self): ''' @name 检查nginx是否支持http3 @author wzz <2024/4/25 上午11:13> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' return public.ExecShell("nginx -V 2>&1| grep 'http_v3_module' -o")[0] #检测重写路径 def CheckRewriteDirArgs(self, get): #检测重写路径 rewritedir=json.loads(get.get("rewritedir",'[{"dir1":"","dir2":""}]')) check_dirs=[] for i in rewritedir: if (i.get('dir1', None) and not i.get('dir2', None)) or (not i.get('dir1', None) and i.get('dir2', None)): return public.lang("Rewrite source directory and rewrite target directory must be filled in at the same time") #检测路径是否相同 if i.get('dir1', None) and i.get('dir2', None) and i.get('dir1', None) == i.get('dir2', None): return public.lang("Rewrite source directory and rewrite target directory cannot be the same") #检测重写的路径是否存在 if i.get('dir1', None): check_dirs.append(i.get('dir1', None)) #检测路径是否以/开头 if i.get('dir1', None) and not i.get('dir1', None).startswith('/'): return public.lang("Rewrite source directory must start with /") if i.get('dir2', None) and not i.get('dir2', None).startswith('/'): return public.lang("Rewrite target directory must start with /") #检测重写路径是否重复 if len(check_dirs) != len(set(check_dirs)): return public.lang("Rewrite source directory and rewrite target directory cannot be the same") return "" # 2024/4/18 上午9:26 创建反向代理 def create(self, get): ''' @name 创建反向代理 @author wzz <2024/4/18 上午9:27> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('remark').String(), Param('proxy_pass').String(), Param('domains').String(), Param('proxy_host').String(), # Param('rewritedir').String(), Param('keepuri').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) # 2024/4/18 上午9:41 前置处理开始 from mod.base.web_conf import util webserver = util.webserver() if webserver != "nginx" or webserver is None: return public.return_message(-1, 0, public.lang("Only Nginx is supported, please install Nginx before use!")) from panelSite import panelSite site_obj = panelSite() site_obj.check_default() wc_err = public.checkWebConfig() if not wc_err: # return public.returnResult( # status=False, # msg='ERROR: Detected an error in the configuration file, please troubleshoot before proceeding

' + # wc_err.replace("\n", '
') + '
' # ) return public.return_message(-1, 0, 'ERROR: Detected an error in the configuration file, please troubleshoot before proceeding

' + wc_err.replace("\n", '
') + '
') # 2024/4/18 上午9:45 参数处理 get.domains = get.get("domains", "") get.proxy_path = get.get("proxy_path", "/") get.keepuri = get.get("keepuri", 1) get.proxy_pass = get.get("proxy_pass", "") get.proxy_host = get.get("proxy_host", "$http_host") get.remark = get.get("remark", "") get.proxy_type = get.get("proxy_type", "http") #去掉路径最后的/ get.proxy_pass = get.proxy_pass.rstrip("/") # 2024/4/18 上午9:45 参数校验 if not get.domains: return public.return_message(-1, 0, public.lang("The domain name cannot be empty, please enter at least one domain name!")) if not get.proxy_pass: return public.return_message(-1, 0, public.lang("The proxy target cannot be empty!")) if get.remark != "": get.remark = public.xssencode2(get.remark) if get.proxy_type == "unix": if not get.proxy_pass.startswith("/"): return public.return_message(-1, 0, public.lang("The Unix file path must start with/, such as/tmp/flash.app.sock!")) if not get.proxy_pass.endswith(".sock"): return public.return_message(-1, 0, public.lang("Unix files must end with. lock, such as/tmp/flash.app. lock!")) if not os.path.exists(get.proxy_pass): return public.return_message(-1, 0, public.lang("The proxy target does not exist!")) get.proxy_pass = "http://unix:{}".format(get.proxy_pass) elif get.proxy_type == "http": if not get.proxy_pass.startswith("http://") and not get.proxy_pass.startswith("https://"): return public.return_message(-1, 0, public.lang("The proxy target must start with http://or https://!")) #检测重写路径 checkRewriteDirArgs=self.CheckRewriteDirArgs(get) if checkRewriteDirArgs !="": return public.return_message(-1, 0, checkRewriteDirArgs) #/目录不支持关闭保持uri if get.proxy_path == "/" and int(get.keepuri) == 0: return public.return_message(-1, 0, public.lang("Proxy_path is root directory, cannot close Show Proxy Path!")) # 2024/4/18 上午9:45 创建反向代理 get.domain_list = get.domains.split("\n") get.site_name = util.to_puny_code(get.domain_list[0].strip().split(":")[0]).strip().lower() get.site_path = "/www/wwwroot/" + get.site_name get.site_port = get.domain_list[0].strip().split(":")[1] if ":" in get.domain_list[0] else "80" get.port_list = [get.site_port] if not public.checkPort(get.site_port): return public.return_message(-1, 0, public.lang("Port [{}] is illegal!", get.site_port)) if len(get.domain_list) > 1: for domain in get.domain_list[1:]: if not ":" in domain.strip(): continue d_port = domain.strip().split(":")[1] if not public.checkPort(d_port): return public.return_message(-1, 0, public.lang("Port [{}] is illegal!", d_port)) if not d_port in get.port_list: get.port_list.append(d_port) # 2024/4/18 上午10:06 检查域名是否存在 main_domain = get.site_name opid = public.M('domain').where("name=? and port=?", (main_domain, int(get.site_port))).getField('pid') if opid: if public.M('sites').where('id=?', (opid,)).count(): return public.return_message(-1, 0, public.lang("The website [{}] already exists, please do not add it again!", main_domain)) public.M('domain').where('pid=?', (opid,)).delete() if public.M('binding').where('domain=?', (main_domain,)).count(): return public.return_message(-1, 0, public.lang("The website [{}] already exists, please do not add it again!", main_domain)) # 2024/4/18 上午10:06 检查网站是否存在 sql = public.M('sites') if sql.where("name=?", (get.site_name,)).count(): if public.is_ipv4(get.site_name): get.site_name = get.site_name + "_" + str(get.site_port) else: return public.return_message(-1, 0, public.lang("The website [{}] already exists, please do not add it again!", main_domain)) # 2024/4/18 上午10:21 添加端口到系统防火墙 from firewallModelV2.comModel import main as comModel firewall_com = comModel() get.port = get.site_port firewall_com.set_port_rule(get) # 2024/4/18 上午9:41 前置处理结束 # 2024/4/18 上午10:46 写入网站配置文件 get.http_block = "proxy_cache_path /www/wwwroot/{site_name}/proxy_cache_dir levels=1:2 keys_zone={cache_name}_cache:20m inactive=1d max_size=5g;".format( site_name=get.site_name, cache_name=get.site_name.replace(".", "_") ) self._site_path = self._proxy_config_path + '/' + get.site_name if not os.path.exists(self._site_path): public.ExecShell('mkdir -p {}'.format(self._site_path)) public.ExecShell('chown -R www:www {}'.format(self._site_path)) public.ExecShell('chmod -R 755 {}'.format(self._site_path)) if not os.path.exists(get.site_path): public.ExecShell('mkdir -p {}'.format(get.site_path)) public.ExecShell('chown -R www:www {}'.format(get.site_path)) public.ExecShell('chmod -R 755 {}'.format(get.site_path)) if not os.path.exists(get.site_path + "/proxy_cache_dir"): public.ExecShell('mkdir -p {}'.format(get.site_path + "/proxy_cache_dir")) public.ExecShell('chown -R www:www {}'.format(get.site_path + "/proxy_cache_dir")) public.ExecShell('chmod -R 755 {}'.format(get.site_path + "/proxy_cache_dir")) self._site_proxy_conf_path = '{}/{}.json'.format(self._site_path, get.site_name) # 2024/4/18 上午10:22 写入数据库 pdata = { 'name': get.site_name, 'path': "/www/wwwroot/" + get.site_name, 'ps': get.remark, 'status': 1, 'type_id': 0, 'project_type': 'proxy', 'project_config': json.dumps(self._init_proxy_conf), 'addtime': public.getDate() } get.pid = public.M('sites').insert(pdata) public.M('domain').add('pid,name,port,addtime', (get.pid, main_domain, get.site_port, public.getDate())) for domain in get.domain_list: get.domain = domain get.webname = get.site_name get.id = str(get.pid) from panelSite import panelSite panelSite().AddDomain(get) # 2024/6/4 下午4:30 写nginx配置文件 self.write_nginx_conf(get) public.writeFile(self._site_proxy_conf_path, json.dumps(self._init_proxy_conf)) wc_err = public.checkWebConfig() if not wc_err: public.ExecShell("rm -f {}".format(self._site_proxy_conf_path)) public.ExecShell("rm -rf {}".format(get.filename)) public.M('sites').where('id=?', (get.pid,)).delete() public.M('domain').where('pid=?', (get.pid,)).delete() return public.return_message(-1, 0,'ERROR: Detected an error in the configuration file, please troubleshoot before proceeding

' + wc_err.replace("\n", '
') + '
' ) if type(wc_err) != bool and "test failed" in wc_err: public.ExecShell("rm -f {}".format(self._site_proxy_conf_path)) public.ExecShell("rm -rf {}".format(get.filename)) public.M('sites').where('id=?', (get.pid,)).delete() public.M('domain').where('pid=?', (get.pid,)).delete() return public.return_message(-1, 0,'ERROR: Detected an error in the configuration file, please troubleshoot before proceeding

' + wc_err.replace("\n", '
') + '
' ) public.WriteLog('TYPE_SITE', 'SITE_ADD_SUCCESS', (get.site_name,)) public.set_module_logs('site_proxy', 'create', 1) public.serviceReload() return public.return_message(0, 0, public.lang("Reverse proxy project added successfully!")) def read_json_conf(self, get): ''' @name @author wzz <2024/4/18 下午9:53> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) try: proxy_json_conf = json.loads(public.readFile(conf_path)) conf_file = public.get_setup_path() + '/panel/vhost/nginx/' + get.site_name + '.conf' conf_string =public.readFile(conf_file) if 'ssl_certificate_key' in conf_string: proxy_json_conf['ssl_info']['ssl_status']=True else: proxy_json_conf['ssl_info']['ssl_status']=False except Exception as e: proxy_json_conf = {} return public.return_message(0,0,proxy_json_conf) # 2024/4/18 下午9:58 设置全局日志 def set_global_log(self, get): ''' @name @author wzz <2024/4/18 下午9:58> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('log_type').String(), Param('log_path').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.log_type = get.get("log_type", "default") if not get.log_type in ["default", "file", "rsyslog", "off"]: return public.return_message(-1, 0, public.lang("The log type is incorrect. Please pass in default/file/rsyslog/off!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["proxy_log"]["log_type"] = get.log_type if get.log_type == "file": get.log_path = get.get("log_path", "") if get.log_path == "": return public.return_message(-1, 0, public.lang("The log path cannot be empty!")) if not get.log_path.startswith("/"): return public.return_message(-1, 0, public.lang("The log path must start with/")) get.proxy_json_conf["proxy_log"]["log_path"] = get.log_path get.proxy_json_conf["proxy_log"]["log_conf"] = self._init_proxy_conf["proxy_log"]["log_conf"].format( log_path=get.log_path, site_name=get.site_name ) elif get.log_type == "rsyslog": get.log_path = get.get("log_path", "") if get.log_path == "": return public.return_message(-1, 0, public.lang("The log path cannot be empty!")) site_name = get.site_name.replace(".", "_") get.proxy_json_conf["proxy_log"]["log_conf"] = ( "\n access_log syslog:server={server_host},nohostname,tag=nginx_{site_name}_access;" "\n error_log syslog:server={server_host},nohostname,tag=nginx_{site_name}_error;" .format( server_host=get.log_path, site_name=site_name )) get.proxy_json_conf["proxy_log"]["rsyslog_host"] = get.log_path elif get.log_type == "off": get.proxy_json_conf["proxy_log"]["log_conf"] = "\n access_log off;\n error_log off;" else: get.proxy_json_conf["proxy_log"]["log_conf"] = " " + self._init_proxy_conf["proxy_log"][ "log_conf"].format( log_path=public.get_logs_path(), site_name=get.site_name ) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result public.serviceReload() return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/18 下午10:21 设置basic_auth def set_dir_auth(self, get): ''' @name 设置basic_auth @param auth_type: add/edit auth_path: /api username: admin password: admin @return: '''# 校验参数 try: get.validate([ Param('site_name').String(), Param('auth_path').String(), Param('username').String(), Param('password').String(), Param('name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.name = get.get("name", "") if get.name == "": return public.return_message(-1, 0, public.lang("Name cannot be empty!")) get.auth_path = get.get("auth_path", "") if get.auth_path == "": return public.return_message(-1, 0, public.lang("Auth_path cannot be empty!")) if not get.auth_path.startswith("/"): return public.return_message(-1, 0, public.lang("Auth_path must start with/!")) get.username = get.get("username", "") get.password = get.get("password", "") if get.username == "" or get.password == "": return public.return_message(-1, 0, public.lang("The username and password cannot be empty!")) get.password = public.hasPwd(get.password) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if len(get.proxy_json_conf["basic_auth"]) == 0: return public.return_message(-1, 0, public.lang("[{}] does not exist in HTTP authentication, please add it first!", get.auth_path)) for i in range(len(get.proxy_json_conf["basic_auth"])): if get.proxy_json_conf["basic_auth"][i]["auth_path"] == get.auth_path: if get.proxy_json_conf["basic_auth"][i]["auth_name"] == get.name: get.proxy_json_conf["basic_auth"][i]["username"] = get.username get.proxy_json_conf["basic_auth"][i]["password"] = get.password break auth_file = "/www/server/pass/{site_name}/{name}.htpasswd".format(site_name=get.site_name, name=get.name) public.writeFile(auth_file, "{}:{}".format(get.username, get.password)) self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) public.serviceReload() return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/22 下午4:17 添加指定网站的basic_auth def add_dir_auth(self, get): ''' @name 添加指定网站的basic_auth @author wzz <2024/4/22 下午4:17> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('auth_path').String(), Param('username').String(), Param('password').String(), Param('name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.auth_path = get.get("auth_path", "") if get.auth_path == "": return public.return_message(-1, 0, public.lang("Auth_path cannot be empty!")) if not get.auth_path.startswith("/"): return public.return_message(-1, 0, public.lang("Auth_path must start with/!")) get.name = get.get("name", "") if get.name == "": return public.return_message(-1, 0, public.lang("Name cannot be empty!")) get.username = get.get("username", "") if get.username == "": return public.return_message(-1, 0, public.lang("Username cannot be empty!")) get.password = get.get("password", "") if get.password == "": return public.return_message(-1, 0, public.lang("Password cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) auth_file = "/www/server/pass/{site_name}/{name}.htpasswd".format(site_name=get.site_name, name=get.name) auth_conf = { "auth_status": True, "auth_path": get.auth_path, "auth_name": get.name, "username": get.username, "password": public.hasPwd(get.password), "auth_file": auth_file, } if len(get.proxy_json_conf["basic_auth"]) != 0: for i in range(len(get.proxy_json_conf["basic_auth"])): if get.proxy_json_conf["basic_auth"][i]["auth_path"] == get.auth_path: return public.return_message(-1, 0, public.lang("[{}] already exists in HTTP authentication and cannot be added again!", get.auth_path)) if not os.path.exists("/www/server/pass"): public.ExecShell("mkdir -p /www/server/pass") if not os.path.exists("/www/server/pass/{}".format(get.site_name)): public.ExecShell("mkdir -p /www/server/pass/{}".format(get.site_name)) public.writeFile(auth_file, "{}:{}".format(get.username, public.hasPwd(get.password))) get.proxy_json_conf["basic_auth"].append(auth_conf) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Added successfully!")) # 2024/4/23 上午9:34 删除指定网站的basic_auth def del_dir_auth(self, get): ''' @name 删除指定网站的basic_auth @author wzz <2024/4/23 上午9:35> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('auth_path').String(), Param('name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.auth_path = get.get("auth_path", "") if get.auth_path == "": return public.return_message(-1, 0, public.lang("Auth_path cannot be empty!")) get.name = get.get("name", "") if get.name == "": return public.return_message(-1, 0, public.lang("Name cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) auth_file = "/www/server/pass/{site_name}/{name}.htpasswd".format(site_name=get.site_name, name=get.name) panel_port = public.readFile('/www/server/panel/data/port.pl') proxy_result = self.get_proxy_list(get) if proxy_result['status']==-1:return proxy_result proxy_list=proxy_result['message'] if proxy_list[0]["proxy_pass"] == "https://127.0.0.1:{}".format(panel_port.strip()) and get.auth_path.strip() == "/": return public.return_message(-1, 0, public.lang("[{}] is a reverse representation of the panel, and the HTTP authentication of [/] cannot be deleted!", get.site_name)) if len(get.proxy_json_conf["basic_auth"]) != 0: for i in range(len(get.proxy_json_conf["basic_auth"])): if get.proxy_json_conf["basic_auth"][i]["auth_path"] == get.auth_path: if get.proxy_json_conf["basic_auth"][i]["auth_name"] == get.name: get.proxy_json_conf["basic_auth"].pop(i) break public.ExecShell("rm -f {}".format(auth_file)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Delete successful!")) # 2024/4/18 下午10:26 设置全局gzip def set_global_gzip(self, get): ''' @name 设置全局gzip @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('gzip_min_length').String(), Param('gzip_types').String(), Param('gzip_status').Integer(), Param('gzip_comp_level').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.gzip_status = get.get("gzip_status/d", 999) if get.gzip_status == 999: return public.return_message(-1, 0, public.lang("Gzip_status cannot be empty, please pass number 1 or 0!")) get.gzip_min_length = get.get("gzip_min_length", "10k") get.gzip_comp_level = get.get("gzip_comp_level", "6") if get.gzip_min_length[0] == "0" or get.gzip_min_length.startswith("-"): return public.return_message(-1, 0, public.lang("The gzip_min_length parameter is invalid. Please enter a number greater than 0!")) if get.gzip_comp_level == "0" or get.gzip_comp_level.startswith("-"): return public.return_message(-1, 0, public.lang("The gzip_comp_level parameter is invalid. Please enter a number greater than 0!")) get.gzip_types = get.get( "gzip_types", "text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/json image/jpeg image/gif image/png font/ttf font/otf image/svg+xml application/xml+rss text/x-js" ) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["gzip"]["gzip_status"] = True if get.gzip_status == 1 else False if get.proxy_json_conf["gzip"]["gzip_status"]: get.proxy_json_conf["gzip"]["gzip_status"] = True get.proxy_json_conf["gzip"]["gzip_min_length"] = get.gzip_min_length get.proxy_json_conf["gzip"]["gzip_comp_level"] = get.gzip_comp_level get.proxy_json_conf["gzip"]["gzip_types"] = get.gzip_types get.gzip_conf = ("gzip on;" "\n gzip_min_length {gzip_min_length};" "\n gzip_buffers 4 16k;" "\n gzip_http_version 1.1;" "\n gzip_comp_level {gzip_comp_level};" "\n gzip_types {gzip_types};" "\n gzip_vary on;" "\n gzip_proxied expired no-cache no-store private auth;" "\n gzip_disable \"MSIE [1-6]\\.\";").format( gzip_min_length=get.gzip_min_length, gzip_comp_level=get.gzip_comp_level, gzip_types=get.gzip_types ) get.proxy_json_conf["gzip"]["gzip_conf"] = get.gzip_conf else: get.proxy_json_conf["gzip"]["gzip_conf"] = "" update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/18 下午10:27 设置全局缓存 def set_global_cache(self, get): ''' @name 设置全局缓存 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('expires').String(), Param('cache_status').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.cache_status = get.get("cache_status/d", 999) if get.cache_status == 999: return public.return_message(-1, 0, public.lang("Cache_status cannot be empty, please pass number 1 or 0!")) get.expires = get.get("expires", "1d") if get.expires[0] == "0" or get.expires.startswith("-"): return public.return_message(-1, 0, public.lang("The expires parameter is illegal. Please enter a number greater than 0!")) expires = "expires {}".format(get.expires) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) static_cache = ("\n location ~ .*\\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\\.map|js\\.map)$" "\n {{" "\n {expires};" "\n error_log /dev/null;" "\n access_log /dev/null;" "\n }}").format( expires=expires, ) cache_conf = ("\n proxy_cache {cache_zone};" "\n proxy_cache_key $host$uri$is_args$args;" "\n proxy_ignore_headers Set-Cookie Cache-Control expires X-Accel-Expires;" "\n proxy_cache_valid 200 304 301 302 {expires};" "\n proxy_cache_valid 404 1m;" "{static_cache}").format( cache_zone=get.proxy_json_conf["proxy_cache"]["cache_zone"], expires=get.expires, static_cache=get.proxy_json_conf["proxy_cache"]["static_cache"] if get.proxy_json_conf["proxy_cache"][ "static_cache"] != "" else static_cache ) get.proxy_json_conf["proxy_cache"]["cache_status"] = True if get.cache_status == 1 else False if get.proxy_json_conf["proxy_cache"]["cache_status"]: get.proxy_json_conf["proxy_cache"]["cache_status"] = True get.proxy_json_conf["proxy_cache"]["expires"] = get.expires get.proxy_json_conf["proxy_cache"]["cache_conf"] = cache_conf else: get.proxy_json_conf["proxy_cache"]["cache_status"] = False get.proxy_json_conf["proxy_cache"]["cache_conf"] = static_cache update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/18 下午10:43 设置全局websocket支持 def set_global_websocket(self, get): ''' @name @author wzz <2024/4/19 下午2:37> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('websocket_status').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.websocket_status = get.get("websocket_status/d", 999) if get.websocket_status == 999: return public.return_message(-1, 0, public.lang("Websocket_status cannot be empty, please pass number 1 or 0!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if get.websocket_status == 1: get.proxy_json_conf["websocket"]["websocket_status"] = True else: get.proxy_json_conf["websocket"]["websocket_status"] = False update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/19 下午2:54 设置备注 def set_remak(self, get): ''' @name 设置备注 @param get: @return: ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("id cannot be empty!")) get.remark = get.get("remark", "") get.table = "sites" get.ps = get.remark get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from data import data data_obj = data() result = data_obj.setPs(get) if not result["status"]: public.returnResult(status=False, msg=result["msg"]) get.proxy_json_conf["remark"] = get.remark self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return public.returnResult(msg=result["msg"]) # 2024/4/19 下午2:59 添加反向代理 def add_proxy(self, get): ''' @name @author wzz <2024/4/19 下午3:00> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('proxy_pass').String(), Param('proxy_host').String(), Param('proxy_type').String(), Param('remark').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.proxy_pass = get.get("proxy_pass", "") if get.proxy_pass == "": return public.return_message(-1, 0, public.lang("Proxy_pass cannot be empty!")) #去掉路径最后的/ get.proxy_pass = get.proxy_pass.rstrip("/") get.proxy_host = get.get("proxy_host", "$http_host") get.proxy_type = get.get("proxy_type", "http") get.remark = get.get("remark", "") get.keepuri = get.get("keepuri", 1) get.proxy_timeout = "proxy_connect_timeout 60s;\n proxy_send_timeout 600s;\n proxy_read_timeout 600s;" if get.remark != "": get.remark = public.xssencode2(get.remark) if get.proxy_type == "unix": if not get.proxy_pass.startswith("/"): return public.return_message(-1, 0, public.lang("The Unix file path must start with/, such as/tmp/flash.app.sock!")) if not get.proxy_pass.endswith(".sock"): return public.return_message(-1, 0, public.lang("Unix files must end with. lock, such as/tmp/flash.app. lock!")) if not os.path.exists(get.proxy_pass): return public.return_message(-1, 0, public.lang("The proxy target does not exist!")) get.proxy_pass = "http://unix:{}".format(get.proxy_pass) elif get.proxy_type == "http": if not get.proxy_pass.startswith("http://") and not get.proxy_pass.startswith("https://"): return public.return_message(-1, 0, public.lang("The proxy target must start with http://or https://")) #检测重写路径 checkRewriteDirArgs=self.CheckRewriteDirArgs(get) if checkRewriteDirArgs !="": return public.return_message(-1, 0, checkRewriteDirArgs) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) # 2024/4/19 下午3:45 检测是否已经存在proxy_path,有的话就返回错误 for proxy_info in get.proxy_json_conf["proxy_info"]: if proxy_info["proxy_path"] == get.proxy_path: return public.return_message(-1, 0, public.lang("【 {} 】 already exists in the reverse proxy and cannot be added again!", get.proxy_path)) if len(get.proxy_json_conf["basic_auth"]) != 0: for i in range(len(get.proxy_json_conf["basic_auth"])): if get.proxy_json_conf["basic_auth"][i]["auth_path"] == get.proxy_path: return public.return_message(-1, 0,"[{}] already exists in Basicauth, please delete it before adding a reverse proxy!".format( get.proxy_path)) sni_conf = "" if get.proxy_pass.startswith("https://"): sni_conf = "proxy_ssl_server_name on;" get.proxy_conf = self._template_proxy_conf.format( ip_limit="", gzip="", proxy_cache="", sub_filter="", server_log="", basic_auth="", proxy_pass=get.proxy_pass, proxy_host=get.proxy_host, proxy_path=get.proxy_path, SNI=sni_conf, custom_conf="", timeout_conf=get.proxy_timeout, websocket_support=get.proxy_json_conf["websocket"]["websocket_conf"], rewrite_direct=self.setRewritedir(get.get("rewritedir",'[{"dir1":"","dir2":""}]')), ) get.proxy_json_conf["proxy_info"].append({ "proxy_type": get.proxy_type, "proxy_path": get.proxy_path, "proxy_pass": get.proxy_pass, "proxy_host": get.proxy_host, "keepuri": get.keepuri, "ip_limit": { "ip_black": [], "ip_white": [], }, "basic_auth": {}, "proxy_cache": { "cache_status": False, "cache_zone": "", "static_cache": "", "expires": "1d", "cache_conf": "", }, "gzip": { "gzip_status": False, "gzip_min_length": "10k", "gzip_comp_level": "6", "gzip_types": "text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/json image/jpeg image/gif image/png font/ttf font/otf image/svg+xml application/xml+rss text/x-js", "gzip_conf": "gzip on;\n gzip_min_length 10k;\n gzip_buffers 4 16k;\n gzip_http_version 1.1;\n gzip_comp_level 2;\n gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;\n gzip_vary on;\n gzip_proxied expired no-cache no-store private auth;\n gzip_disable \"MSIE [1-6]\\.\";", }, "sub_filter": { "sub_filter_str": [], }, "rewritedir": json.loads(get.get("rewritedir", '[{"dir1":"","dir2":""}]')), "websocket": { "websocket_status": True, "websocket_conf": "proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";", }, "proxy_log": { "log_type": "off", "log_conf": "", }, "timeout": { "proxy_connect_timeout": "60", "proxy_send_timeout": "600", "proxy_read_timeout": "600", "timeout_conf": "proxy_connect_timeout 60s;\n proxy_send_timeout 600s;\n proxy_read_timeout 600s;", }, "custom_conf": "", "proxy_conf": get.proxy_conf, "remark": get.remark, "template_proxy_conf": self._template_proxy_conf, }) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result public.set_module_logs('site_proxy', 'add_proxy', 1) return public.return_message(0, 0, public.lang("Added successfully!")) # 2024/4/19 下午9:45 删除指定站点 def delete(self, get): ''' @name @author wzz <2024/4/19 下午9:45> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('remove_path').Integer(), Param('id').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") get.id = get.get("id", "") get.remove_path = get.get("remove_path/d", 0) if get.id == "": return public.return_message(-1, 0, public.lang("ID cannot be empty!")) get.reload = get.get("reload/d", 1) if public.M('sites').where('id=?', (get.id,)).count() < 1: return public.return_message(-1, 0, public.lang("The specified site does not exist!")) site_file = public.get_setup_path() + '/panel/vhost/nginx/' + get.site_name + '.conf' if os.path.exists(site_file): public.ExecShell('rm -f {}'.format(site_file)) redirect_dir = public.get_setup_path() + '/panel/vhost/nginx/redirect/' + get.site_name if os.path.exists(redirect_dir): public.ExecShell('rm -rf {}'.format(redirect_dir)) logs_file = public.get_logs_path() + '/{}*'.format(get.site_name) public.ExecShell('rm -f {}'.format(logs_file)) self._site_proxy_conf_path = "{path}/{site_name}".format( path=self._proxy_config_path, site_name=get.site_name ) public.ExecShell('rm -f {}'.format(self._site_proxy_conf_path)) if get.remove_path == 1: public.ExecShell('rm -rf /www/wwwroot/{}'.format(get.site_name)) if get.reload == 1: public.serviceReload() # 从数据库删除 public.M('sites').where("id=?", (get.id,)).delete() public.M('domain').where("pid=?", (get.id,)).delete() public.WriteLog('TYPE_SITE', "SITE_DEL_SUCCESS", (get.site_name,)) return public.return_message(0, 0, public.lang("Reverse proxy project deleted successfully!")) # 2024/5/28 上午9:50 批量删除站点 def batch_delete(self, get): ''' @name 批量删除站点 @author wzz <2024/5/28 上午9:51> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_list = get.get("site_list", []) get.remove_path = get.get("remove_path/d", 0) get.reload = get.get("reload/d", 0) try: site_list = json.loads(get.site_list) except: return public.returnResult(False, "请传入需要删除的网站列表!") acc_list = [] for site in site_list: args = public.dict_obj() args.site_name = site["site_name"] args.remove_path = get.remove_path args.reload = get.reload args.id = site["id"] de_result = self.delete(args) if not de_result["status"]: acc_list.append({"site_name": site["site_name"], "status": False}) continue acc_list.append({"site_name": site["site_name"], "status": True}) public.serviceReload() return public.returnResult(True, msg="批量删除站点成功!", data=acc_list) # 2024/4/26 下午4:57 获取证书的部署状态 def get_site_ssl_info(self, siteName): ''' @name 获取证书的部署状态 @author wzz <2024/4/26 下午4:58> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' try: import time import re s_file = 'vhost/nginx/{}.conf'.format(siteName) is_apache = False if not os.path.exists(s_file): s_file = 'vhost/apache/{}.conf'.format(siteName) is_apache = True if not os.path.exists(s_file): return -1 s_conf = public.readFile(s_file) if not s_conf: return -1 ssl_file = None if is_apache: if s_conf.find('SSLCertificateFile') == -1: return -1 s_tmp = re.findall(r"SSLCertificateFile\s+(.+\.pem)", s_conf) if not s_tmp: return -1 ssl_file = s_tmp[0] else: if s_conf.find('ssl_certificate') == -1: return -1 s_tmp = re.findall(r"ssl_certificate\s+(.+\.pem);", s_conf) if not s_tmp: return -1 ssl_file = s_tmp[0] ssl_info = public.get_cert_data(ssl_file) if not ssl_info: return -1 ssl_info['endtime'] = int( int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) return ssl_info except: return -1 # 获取反向代理列表 def get_list(self, get): ''' @name 重构获取列表,添加排序机制 @param get: @return: ''' try: get.validate([ Param('search').String(), Param('p').Integer(), Param('limit').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) p = int(get.get("p/d", 1)) limit = int(get.get("limit/d", 10)) search = get.get("search", "") re_order = get.get('re_order', '') # 流量排序专用 where = "project_type=?" param = ["proxy"] if search != "": where += " and (name like ? or ps like ?)" search_pattern = "%{}%".format(search) param.append(search_pattern) param.append(search_pattern) import db sql = db.Sql() all_data = sql.table('sites').where(where, param).field('id,name,path,status,ps,addtime,edate').order( 'addtime desc').select() if not all_data: return public.return_message(0, 0, {"data": [], "page": ""}) re_data = None if re_order: import data_v2 res = data_v2.data().get_site_request(public.to_dict_obj({'site_type': 'proxy'})) if res.get('status') == 0: re_data = res.get('message') try: waf_res = json.loads(public.readFile('/www/server/btwaf/site.json')) except: waf_res = {} for site in all_data: site['re_total'] = 0 if re_data and site['name'] in re_data: site['re_total'] = re_data[site['name']]['total']['request'] get.site_name = site["name"] project_config = self.read_json_conf(get).get('message', {}) if not project_config: site.update({"healthy": 0, "conf_path": "", "ssl": -1, "proxy_pass": "", "waf": {}}) continue site["healthy"] = 1 site["conf_path"] = public.get_setup_path() + '/panel/vhost/nginx/' + site['name'] + '.conf' site["ssl"] = self.get_site_ssl_info(site['name']) site["proxy_pass"] = project_config.get("proxy_info", [{}])[0].get("proxy_pass", "") site["waf"] = {"status": True} if (site['name'] in waf_res and waf_res[site['name']].get('open')) else {} if re_order: is_reverse = True if re_order == 'desc' else False all_data = sorted(all_data, key=lambda x: x["re_total"], reverse=is_reverse) count = len(all_data) start = (p - 1) * limit end = start + limit paged_data = all_data[start:end] import page pg = page.Page() info = { 'count': count, 'row': limit, 'p': p, 'return_js': '', 'uri': '' } try: from flask import request info['uri'] = public.url_encode(request.full_path) except: pass return_data = { 'data': paged_data, 'page': pg.GetPage(info) } return public.return_message(0, 0, public.returnResult(data=return_data)) # 2024/4/19 下午11:46 给指定网站添加域名 def add_domain(self, get): ''' @name @author wzz <2024/4/19 下午11:46> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('domains').String(), Param('id').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("ID cannot be empty!")) get.domains = get.get("domains", "") if get.domains == "": return public.return_message(-1, 0, public.lang("Domains cannot be empty!")) if "," in get.domains: return public.return_message(-1, 0, public.lang("The domain name cannot contain commas!")) get.domain_list = get.domains.strip().replace(' ', '').split("\n") get.domain = ",".join(get.domain_list) get.webname = get.site_name port_list = [] for domain in get.domain_list: if not ":" in domain.strip(): continue d_port = domain.strip().split(":")[1] if not public.checkPort(d_port): return public.return_message(-1, 0, public.lang("The port number of domain name [{}] is illegal!", domain)) port_list.append(d_port) # 2024/4/20 上午12:02 更新json文件 get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["domain_list"].extend(get.domain_list) get.proxy_json_conf["site_port"].extend(port_list) self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) from panelSite import panelSite result = panelSite().AddDomain(get) result_status=0 if not result['status']: result_status=-1 return public.return_message(result_status,0,result["msg"]) # 2024/4/20 上午12:07 删除指定网站的某个域名 def del_domain(self, get): ''' @name @author wzz <2024/4/20 上午12:07> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('domains').String(), Param('id').Integer(), Param('port').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("ID cannot be empty!")) get.port = get.get("port", "") if get.port == "": return public.return_message(-1, 0, public.lang("Port cannot be empty!")) get.domain = get.get("domain", "") if get.domain == "": return public.return_message(-1, 0, public.lang("Domain cannot be empty!")) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.webname = get.site_name # 2024/4/20 上午12:02 更新json文件 get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if len(get.proxy_json_conf["domain_list"]) == 1: return public.return_message(-1, 0, public.lang("Keep at least one domain name!")) while get.domain in get.proxy_json_conf["domain_list"]: get.proxy_json_conf["domain_list"].remove(get.domain) if get.port in get.proxy_json_conf["site_port"] and len(get.proxy_json_conf["site_port"]) != 1: while get.port in get.proxy_json_conf["site_port"]: get.proxy_json_conf["site_port"].remove(get.port) self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) from panelSite import panelSite result = panelSite().DelDomain(get) return_status =0 if not result['status']: return_status=-1 return public.return_message(return_status,0, result["msg"]) # 2024/4/20 上午12:20 批量删除指定网站域名 def batch_del_domain(self, get): ''' @name 批量删除指定网站域名 @param get: @return: ''' get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("id cannot be empty!")) get.domains = get.get("domains", "") if get.domains == "": return public.return_message(-1, 0, public.lang("domains cannot be empty!")) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.webname = get.site_name # 2024/4/20 上午12:02 更新json文件 get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.domain_list = get.domains.strip().replace(' ', '').split("\n") for domain in get.domain_list: while domain in get.proxy_json_conf["domain_list"]: get.proxy_json_conf["domain_list"].remove(domain) if ":" in domain: port = domain.split(":")[1] if len(get.proxy_json_conf["site_port"]) == 1: continue while port in get.proxy_json_conf["site_port"]: get.proxy_json_conf["site_port"].remove(port) self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) from panelSite import panelSite res_domains = [] for domain in get.domain_list: get.domain = domain get.port = "80" if ":" in domain: get.port = domain.split(":")[1] result = panelSite().DelDomain(get) res_domains.append({"name": domain, "status": result["status"], "msg": result["msg"]}) public.serviceReload() return public.return_message(0, 0, res_domains) # 2024/4/20 上午9:17 获取域名列表和https端口 def get_domain_list(self, get): ''' @name 获取域名列表和https端口 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('id').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("ID cannot be empty!")) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.siteName = get.webname = get.site_name get.table = "domain" get.list = True get.search = get.id # 2024/4/20 上午12:02 更新json文件 get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) result_data = {} import data dataObject = data.data() result_data["domain_list"] = dataObject.getData(get) if get.proxy_json_conf["ssl_info"]["ssl_status"]: if not "https_port" in get.proxy_json_conf or get.proxy_json_conf["https_port"] == "": get.proxy_json_conf["https_port"] = "443" result_data["https_port"] = get.proxy_json_conf["https_port"] else: result_data["https_port"] = "HTTPS not enabled" # 2024/4/20 上午9:21 domain_list里面没有的域名健康状态显示为0 for domain in result_data["domain_list"]: domain["healthy"] = 1 if domain["name"] not in get.proxy_json_conf["domain_list"]: domain["healthy"] = 0 public.set_module_logs('site_proxy', 'get_domain_list', 1) return public.return_message(0,0,result_data) # 2024/4/20 下午2:22 获取配置文件 def get_config(self, get): ''' @name @author wzz <2024/4/20 下午2:22> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} '''# 校验参数 try: get.validate([ Param('site_name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) site_conf = public.readFile(public.get_setup_path() + '/panel/vhost/nginx/' + get.site_name + '.conf') ssl_conf = get.proxy_json_conf["ssl_info"]["ssl_conf"].format( site_name=get.site_name, ) if get.proxy_json_conf["ssl_info"]["force_https"]: ssl_conf = get.proxy_json_conf["ssl_info"]["force_ssl_conf"].format( site_name=get.site_name, force_conf=get.proxy_json_conf["ssl_info"]["force_ssl_conf"], ) if "If there is abnormal access to the reverse proxy website and the content has already been configured here, please prioritize checking if the configuration here is correct" in get.proxy_json_conf["http_block"]: http_block = get.proxy_json_conf["http_block"] else: http_block = '''# All HTTP fields such as server | upstream | map can be set, such as: # server {{ # listen 10086; # server_name ... # }} # upstream stream_ser {{ # server back_test.com; # server ... # }} {default_describe} {http_block}'''.format( default_describe=self._init_proxy_conf["default_describe"], http_block=get.proxy_json_conf["http_block"], ) if "If there is abnormal access to the reverse proxy website and the content has already been configured here, please prioritize checking if the configuration here is correct" in get.proxy_json_conf["server_block"]: server_block = get.proxy_json_conf["server_block"] else: server_block = '''# All server fields such as server | location can be set, such as: # location /web {{ # try_files $uri $uri/ /index.php$is_args$args; # }} # error_page 404 /diy_404.html; {default_describe} {server_block}'''.format( default_describe=self._init_proxy_conf["default_describe"], server_block=get.proxy_json_conf["server_block"], ) data = { "site_conf": site_conf if not site_conf is False else "", "http_block": http_block, "server_block": server_block, "ssl_conf": ssl_conf, } return public.return_message(0, 0,data) # 2024/4/20 下午2:38 保存配置文件 def save_config(self, get): ''' @name 保存配置文件 @param get: @return: '''# 校验参数 try: get.validate([ Param('site_name').String(), Param('conf_type').String(), Param('body').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.conf_type = get.get("conf_type", "") if get.conf_type == "": return public.return_message(-1, 0, public.lang("Conf_type cannot be empty!")) get.body = get.get("body", "") get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if get.conf_type == "http_block": get.proxy_json_conf["http_block"] = get.body else: get.proxy_json_conf["server_block"] = get.body update_result = self.update_conf(get) if update_result["status"]==-1: return update_result public.serviceReload() return public.return_message(0, 0, public.lang("Save successful!")) # 2024/4/20 下午3:11 根据proxy_json_conf,填入self._template_conf,然后生成nginx配置,保存到指定网站的conf文件中 def generate_config(self, get): """ @name 根据proxy_json_conf,填入self._template_conf,然后生成nginx配置,保存到指定网站的conf文件中 @param get: @return: """ # 2024/4/20 下午3:36 构造ip黑白名单 ip_black = "" ip_white = "" for ip in get.proxy_json_conf["ip_limit"]["ip_black"]: ip_black += ("deny {};\n ").format(ip) for ip in get.proxy_json_conf["ip_limit"]["ip_white"]: ip_white += ("allow {};\n ").format(ip) if ip_white != "": ip_white += "deny all;" ip_limit_conf = ip_black + "\n " + ip_white proxy_conf = "" ignore_path = [] if len(get.proxy_json_conf["proxy_info"]) != 0: for info in get.proxy_json_conf["proxy_info"]: proxy_auth_conf = "" if info["basic_auth"]: proxy_auth_conf = ("auth_basic \"Authorization\";" "\n auth_basic_user_file {auth_file};").format( auth_path=info["basic_auth"]["auth_path"], auth_file=info["basic_auth"]["auth_file"], ) if len(get.proxy_json_conf["basic_auth"]) != 0: for auth in get.proxy_json_conf["basic_auth"]: if info["proxy_path"] == auth["auth_path"]: ignore_path.append(auth["auth_path"]) proxy_auth_conf = ("auth_basic \"Authorization\";" "\n auth_basic_user_file {auth_file};").format( auth_path=auth["auth_path"], auth_file=auth["auth_file"], ) break p_ip_black = "" p_ip_white = "" for ip in info["ip_limit"]["ip_black"]: p_ip_black += ("deny {};\n ").format(ip) for ip in info["ip_limit"]["ip_white"]: p_ip_white += ("allow {};\n ").format(ip) if p_ip_white != "": p_ip_white += "deny all;" p_ip_limit_conf = p_ip_black + "\n " + p_ip_white if p_ip_black == "" and p_ip_white == "": p_ip_limit_conf = "" p_gzip_conf = "" if info["gzip"]["gzip_status"]: p_gzip_conf = info["gzip"]["gzip_conf"] p_sub_filter = "" if len(info["sub_filter"]["sub_filter_str"]) != 0: p_sub_filter = 'proxy_set_header Accept-Encoding \"\";' if not "subs_filter" in get.proxy_json_conf: get.proxy_json_conf["subs_filter"] = public.ExecShell("nginx -V 2>&1|grep 'ngx_http_substitutions_filter' -o")[0] != "" if not get.proxy_json_conf["subs_filter"]: for filter in info["sub_filter"]["sub_filter_str"]: p_sub_filter += "\n sub_filter {oldstr} {newstr};".format( oldstr=filter["oldstr"] if filter["oldstr"] != "" else "\"\"", newstr=filter["newstr"] if filter["newstr"] != "" else "\"\"", ) p_sub_filter += "\n sub_filter_once off;" else: for filter in info["sub_filter"]["sub_filter_str"]: p_sub_filter += "\n subs_filter {oldstr} {newstr} {sub_type};".format( oldstr=filter["oldstr"] if filter["oldstr"] != "" else "\"\"", newstr=filter["newstr"] if filter["newstr"] != "" else "\"\"", sub_type=filter["sub_type"] if "sub_type" in filter and filter["sub_type"] != "" else "\"\"", ) p_websocket_support = "" if info["websocket"]["websocket_status"]: p_websocket_support = info["websocket"]["websocket_conf"] timeout_conf = ("proxy_connect_timeout {proxy_connect_timeout};" "\n proxy_send_timeout {proxy_send_timeout};" "\n proxy_read_timeout {proxy_read_timeout};").format( proxy_connect_timeout=info["timeout"]["proxy_connect_timeout"].replace("s", "") + "s", proxy_send_timeout=info["timeout"]["proxy_send_timeout"].replace("s", "") + "s", proxy_read_timeout=info["timeout"]["proxy_read_timeout"].replace("s", "") + "s", ) args = public.dict_obj() args.rewritedir = json.dumps(info.get("rewritedir", [{"dir1":"","dir2":""}])) rewriteconf=self.setRewritedir(args) proxy_pass_target=info["proxy_pass"] if int(get.get("keepuri", 1)) ==0: proxy_pass_target+="/" sni_conf = "" if info["proxy_pass"].startswith("https://"): sni_conf = "proxy_ssl_server_name on;" tmp_conf = info["template_proxy_conf"].format( basic_auth=proxy_auth_conf, ip_limit=p_ip_limit_conf, gzip=p_gzip_conf, sub_filter=p_sub_filter, proxy_cache=info["proxy_cache"]["cache_conf"], server_log="", proxy_pass=proxy_pass_target, proxy_host=info["proxy_host"], proxy_path=info["proxy_path"], custom_conf=info["custom_conf"], timeout_conf=timeout_conf, websocket_support=p_websocket_support, rewrite_direct=rewriteconf, SNI=sni_conf, ) if tmp_conf.find("rewrite_direct") == -1 and tmp_conf.find("rewrite ") == -1 and rewriteconf != "": rewrite_conf="{}\n\tproxy_pass".format(rewriteconf) tmp_conf=tmp_conf.replace("proxy_pass",rewrite_conf) info["proxy_conf"] = tmp_conf proxy_conf += tmp_conf + "\n " # 2024/4/20 下午3:37 构造basicauth auth_conf = "" if len(get.proxy_json_conf["basic_auth"]) != 0: for auth in get.proxy_json_conf["basic_auth"]: if auth["auth_path"] not in ignore_path: tmp_conf = ("location ^~ {auth_path} {{" "\n auth_basic \"Authorization\";" "\n auth_basic_user_file {auth_file};" "\n }}").format(auth_path=auth["auth_path"], auth_file=auth["auth_file"]) auth_conf += tmp_conf + "\n " websocket_support = "" if get.proxy_json_conf["websocket"]["websocket_status"]: websocket_support = get.proxy_json_conf["websocket"]["websocket_conf"] gzip_conf = "" if get.proxy_json_conf["gzip"]["gzip_status"]: gzip_conf = get.proxy_json_conf["gzip"]["gzip_conf"] ssl_conf = "#error_page 404/404.html;" # listen_port = " ".join(get.proxy_json_conf["site_port"]) # listen_ipv6 = "\n listen [::]:{};".format(" ".join(get.proxy_json_conf["site_port"])) # port_conf = get.proxy_json_conf["port_conf"].format( # listen_port=listen_port, # listen_ipv6=listen_ipv6, # ) if not "https_port" in get.proxy_json_conf or get.proxy_json_conf["https_port"] == "": get.proxy_json_conf["https_port"] = "443" if not "ipv4_port_conf" in get.proxy_json_conf: get.proxy_json_conf["ipv4_port_conf"] = "listen {listen_port};" if not "ipv6_port_conf" in get.proxy_json_conf: get.proxy_json_conf["ipv6_port_conf"] = "listen [::]:{listen_port};" if not "ipv4_http3_ssl_port_conf" in get.proxy_json_conf: get.proxy_json_conf["ipv4_http3_ssl_port_conf"] = "{ipv4_port_conf}\n listen {https_port} quic;\n listen {https_port} ssl;" if not "ipv6_http3_ssl_port_conf" in get.proxy_json_conf: get.proxy_json_conf["ipv6_http3_ssl_port_conf"] = "{ipv6_port_conf}\n listen [::]:{https_port} quic;\n listen [::]:{https_port} ssl ;" if not "ipv4_ssl_port_conf" in get.proxy_json_conf: get.proxy_json_conf["ipv4_ssl_port_conf"] = "{ipv4_port_conf}\n listen {https_port} ssl http2 ;" if not "ipv6_ssl_port_conf" in get.proxy_json_conf: get.proxy_json_conf["ipv6_ssl_port_conf"] = "{ipv6_port_conf}\n listen [::]:{https_port} ssl http2 ;" ipv4_port_conf = "" ipv6_port_conf = "" for p in get.proxy_json_conf["site_port"]: ipv4_port_conf += get.proxy_json_conf["ipv4_port_conf"].format( listen_port=p, ) + "\n " ipv6_port_conf += get.proxy_json_conf["ipv6_port_conf"].format( listen_port=p, ) + "\n " if get.proxy_json_conf["ssl_info"]["ssl_status"]: if public.ExecShell("nginx -V 2>&1| grep 'http_v3_module' -o")[0] != "": ipv4_http3_ssl_port_conf = get.proxy_json_conf["ipv4_http3_ssl_port_conf"].format( ipv4_port_conf=ipv4_port_conf, https_port=get.proxy_json_conf["https_port"], ) + "\n " ipv6_http3_ssl_port_conf = get.proxy_json_conf["ipv6_http3_ssl_port_conf"].format( ipv6_port_conf=ipv6_port_conf, https_port=get.proxy_json_conf["https_port"], ) + "\n " port_conf = ipv4_http3_ssl_port_conf + ipv6_http3_ssl_port_conf + "\n http2 on;" else: ipv4_ssl_port_conf = get.proxy_json_conf["ipv4_ssl_port_conf"].format( ipv4_port_conf=ipv4_port_conf, https_port=get.proxy_json_conf["https_port"], ) + "\n " ipv6_ssl_port_conf = get.proxy_json_conf["ipv6_ssl_port_conf"].format( ipv6_port_conf=ipv6_port_conf, https_port=get.proxy_json_conf["https_port"], ) + "\n " port_conf = ipv4_ssl_port_conf + ipv6_ssl_port_conf ssl_conf = get.proxy_json_conf["ssl_info"]["ssl_conf"].format(site_name=get.site_name) if get.proxy_json_conf["ssl_info"]["force_https"]: ssl_conf = get.proxy_json_conf["ssl_info"]["force_ssl_conf"].format( site_name=get.site_name, force_conf=get.proxy_json_conf["ssl_info"]["force_conf"] ) else: port_conf = ipv4_port_conf + "\n" + ipv6_port_conf redirect_conf = "" if get.proxy_json_conf["redirect"]["redirect_status"]: redirect_conf = get.proxy_json_conf["redirect"]["redirect_conf"].format(site_name=get.site_name) security_conf = "" if get.proxy_json_conf["security"]["security_status"]: domains = get.proxy_json_conf["security"]["domains"] if not get.proxy_json_conf["security"][ "http_status"] else "none blocked " + get.proxy_json_conf["security"]["domains"] security_conf = get.proxy_json_conf["security"]["security_conf"].format( static_resource=get.proxy_json_conf["security"]["static_resource"], expires="30d", domains=domains.replace(",", " "), return_resource=get.proxy_json_conf["security"]["return_resource"], ) default_cache = get.proxy_json_conf["default_cache"].format( site_name=get.site_name, cache_name=get.site_name.replace(".", "_") ) get.http_block = default_cache + "\n" + get.proxy_json_conf["http_block"] # 2024/6/4 下午4:20 兼容新版监控报表的配置 monitor_conf = "" if (os.path.exists("/www/server/panel/plugin/monitor/monitor_main.py") and os.path.exists("/www/server/monitor/config/sites.json")): try: sites_data = json.loads(public.readFile("/www/server/monitor/config/sites.json")) if sites_data[get.site_name]["open"]: id = public.M('domain').where("name=?", (get.site_name,)).getField('id') monitor_conf = '''#Monitor-Config-Start monitor log sending configuration access_log syslog:server=unix:/tmp/bt-monitor.sock,nohostname,tag={sid}__access monitor; error_log syslog:server=unix:/tmp/bt-monitor.sock,nohostname,tag={sid}__error; #Monitor-Config-End'''.format(sid=id) except: pass get.site_conf = self._template_conf.format( http_block=get.http_block, server_block=get.proxy_json_conf["server_block"], port_conf=port_conf, ssl_start_msg=public.getMsg('NGINX_CONF_MSG1'), err_page_msg=public.getMsg('NGINX_CONF_MSG2'), php_info_start=public.getMsg('NGINX_CONF_MSG3'), rewrite_start_msg=public.getMsg('NGINX_CONF_MSG4'), domains=' '.join(get.proxy_json_conf["domain_list"]) if len( get.proxy_json_conf["domain_list"]) > 1 else get.site_name, site_name=get.site_name, ssl_info=ssl_conf, err_age_404=get.proxy_json_conf["err_age_404"], err_age_502=get.proxy_json_conf["err_age_502"], ip_limit_conf=ip_limit_conf, auth_conf=auth_conf, sub_filter="", gzip_conf=gzip_conf, security_conf=security_conf, redirect_conf=redirect_conf, proxy_conf=proxy_conf, proxy_cache=get.proxy_json_conf["proxy_cache"]["cache_conf"], server_log=get.proxy_json_conf["proxy_log"]["log_conf"], site_path=get.proxy_json_conf["site_path"], websocket_support=websocket_support, monitor_conf=monitor_conf, ) # 2024/4/21 下午10:46 设置商业ssl证书 def set_cert(self, get): ''' @name @author wzz <2024/4/21 下午10:46> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.oid = get.get("oid", "") if get.oid == "": return public.return_message(-1, 0, public.lang("oid cannot be empty!")) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.siteName = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from panelSSL import panelSSL ssl_obj = panelSSL() set_result = ssl_obj.set_cert(get) if set_result["status"] == False: return set_result get.proxy_json_conf["ssl_info"]["ssl_status"] = True get.proxy_json_conf["https_port"] = "443" self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return set_result # 2024/4/21 下午11:03 关闭SSl证书 def close_ssl(self, get): ''' @name @author wzz <2024/4/21 下午11:04> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.siteName = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from panelSite import panelSite result = panelSite().CloseSSLConf(get) if not result["status"]: return result get.proxy_json_conf["ssl_info"]["ssl_status"] = False get.proxy_json_conf["https_port"] = "443" self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return result # 2024/4/21 下午11:10 保存指定网站的SSL证书 def set_ssl(self, get): ''' @name 保存指定网站的SSL证书 @author wzz <2024/4/21 下午11:12> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.key = get.get("key", "") if get.key == "": return public.return_message(-1, 0, public.lang("key cannot be empty!")) get.csr = get.get("csr", "") if get.csr == "": return public.return_message(-1, 0, public.lang("csr cannot be empty!")) get.siteName = get.site_name get.type = -1 get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from panel_site_v2 import panelSite result = panelSite().SetSSL(get) if result["status"] == -1: # return public.returnResult(status=False, msg=result["msg"]) return result get.proxy_json_conf["ssl_info"]["ssl_status"] = True get.proxy_json_conf["https_port"] = "443" self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return result # 2024/4/21 下午11:29 部署测试证书 def set_test_cert(self, get): ''' @name @author wzz <2024/4/21 下午11:30> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.partnerOrderId = get.get("partnerOrderId", "") if get.partnerOrderId == "": return public.return_message(-1, 0, public.lang("partnerOrderId cannot be empty!")) get.siteName = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from panelSSL import panelSSL ssl_obj = panelSSL() set_result = ssl_obj.GetSSLInfo(get) if set_result["status"] == False: return set_result get.proxy_json_conf["ssl_info"]["ssl_status"] = True get.proxy_json_conf["https_port"] = "443" self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return set_result # 2024/4/21 下午11:33 申请let' encrypt证书 def apply_cert_api(self, get): ''' @name @author wzz <2024/4/21 下午11:34> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.domains = get.get("domains", "") if get.domains == "": return public.return_message(-1, 0, public.lang("domains cannot be empty!")) get.auth_type = get.get("auth_type", "") if get.auth_type == "": return public.return_message(-1, 0, public.lang("auth_type cannot be empty!")) get.auth_to = get.get("auth_to", "") if get.auth_to == "": return public.return_message(-1, 0, public.lang("auth_to cannot be empty!")) get.auto_wildcard = get.get("auto_wildcard", "") if get.auto_wildcard == "": return public.return_message(-1, 0, public.lang("auto_wildcard cannot be empty!")) get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("id cannot be empty!")) get.siteName = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from acme_v2 import acme_v2 acme = acme_v2() result = acme.apply_cert_api(get) if not result["status"]: return result get.proxy_json_conf["ssl_info"]["ssl_status"] = True get.proxy_json_conf["https_port"] = "443" self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return result # 2024/4/21 下午11:36 验证let' encrypt dns def apply_dns_auth(self, get): ''' @name @author wzz <2024/4/21 下午11:36> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.index = get.get("index", "") if get.index == "": return public.return_message(-1, 0, public.lang("index cannot be empty!")) from acme_v2 import acme_v2 acme = acme_v2() return acme.apply_dns_auth(get) # 2024/4/21 下午11:44 设置证书夹里面的证书 def SetBatchCertToSite(self, get): ''' @name @author wzz <2024/4/21 下午11:44> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.BatchInfo = get.get("BatchInfo", "") if get.BatchInfo == "": return public.return_message(-1, 0, public.lang("BatchInfo cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from panelSSL import panelSSL ssl_obj = panelSSL() set_result = ssl_obj.SetBatchCertToSite(get) if not "successList" in set_result: return set_result for re in set_result["successList"]: if re["status"] and re["siteName"] == get.site_name: get.proxy_json_conf["ssl_info"]["ssl_status"] = True break self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return set_result # 2024/4/22 上午9:43 设置强制https def set_force_https(self, get): ''' @name 设置强制https @param get: site_name: 网站名 force_https: 1/0 @return: ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.force_https = get.get("force_https/d", 999) if get.force_https == 999: return public.return_message(-1, 0, public.lang("force_https cannot be empty!")) get.siteName = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["ssl_info"]["force_https"] = True if get.force_https == 1 else False from panelSite import panelSite if get.force_https == 1: result = panelSite().HttpToHttps(get) else: result = panelSite().CloseToHttps(get) if not result["status"]: return result self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return result # 2024/4/22 上午10:27 创建重定向 def CreateRedirect(self, get): ''' @name @author wzz <2024/4/22 上午10:27> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('domainorpath').String(), Param('redirecttype').String(), Param('redirectpath').String(), Param('tourl').String(), Param('redirectdomain').String(), Param('redirectname').String(), Param('type').Integer(), Param('holdpath').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.domainorpath = get.get("domainorpath", "") if get.domainorpath == "": return public.return_message(-1, 0, public.lang("Domainorpath cannot be empty!")) get.redirecttype = get.get("redirecttype", "") if get.redirecttype == "": return public.return_message(-1, 0, public.lang("Redirecttype cannot be empty!")) get.redirectpath = get.get("redirectpath", "") if get.domainorpath == "path" and get.redirectpath == "": return public.return_message(-1, 0, public.lang("Redirectpath cannot be empty!")) get.tourl = get.get("tourl", "") if get.tourl == "": return public.return_message(-1, 0, public.lang("Tour cannot be empty!")) get.redirectdomain = get.get("redirectdomain", "") if get.domainorpath == "domain" and get.redirectdomain == "": return public.return_message(-1, 0, public.lang("Redirectdomain cannot be empty!")) get.redirectname = get.get("redirectname", "") if get.redirectname == "": return public.return_message(-1, 0, public.lang("Redirectname cannot be empty!")) get.sitename = get.site_name get.type = 1 get.holdpath = 1 get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["redirect"]["redirect_status"] = True from panelRedirect import panelRedirect result = panelRedirect().CreateRedirect(get) self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) if not result['status']: return public.return_message(-1,0,result['msg']) return public.return_message(0,0,result['msg']) # 2024/4/22 上午10:45 删除指定网站的某个重定向规则 def DeleteRedirect(self, get): ''' @name 删除指定网站的某个重定向规则 @author wzz <2024/4/22 上午10:45> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('redirectname').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.redirectname = get.get("redirectname", "") if get.redirectname == "": return public.return_message(-1, 0, public.lang("Redirectname cannot be empty!")) get.sitename = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) from panelRedirect import panelRedirect redirect_list = panelRedirect().GetRedirectList(get) if len(redirect_list) == 0: get.proxy_json_conf["redirect"]["redirect_status"] = False self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) result =panelRedirect().DeleteRedirect(get) if not result['status']: return public.return_message(-1,0,result['msg']) return public.return_message(0,0,result['msg']) # 2024/4/23 上午10:38 编辑指定网站的某个重定向规则 def ModifyRedirect(self, get): ''' @name 编辑指定网站的某个重定向规则 @author wzz <2024/4/23 上午10:38> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} '''# 校验参数 try: get.validate([ Param('site_name').String(), Param('domainorpath').String(), Param('redirecttype').String(), Param('redirectpath').String(), Param('tourl').String(), Param('redirectdomain').String(), Param('redirectname').String(), Param('type').Integer(), Param('holdpath').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.domainorpath = get.get("domainorpath", "") if get.domainorpath == "": return public.return_message(-1, 0, public.lang("Domainorpath cannot be empty!")) get.redirecttype = get.get("redirecttype", "") if get.redirecttype == "": return public.return_message(-1, 0, public.lang("Redirecttype cannot be empty!")) get.redirectpath = get.get("redirectpath", "") if get.domainorpath == "path" and get.redirectpath == "": return public.return_message(-1, 0, public.lang("Redirectpath cannot be empty!")) get.tourl = get.get("tourl", "") if get.tourl == "": return public.return_message(-1, 0, public.lang("Tour cannot be empty!")) get.redirectdomain = get.get("redirectdomain", "") if get.domainorpath == "domain" and get.redirectdomain == "": return public.return_message(-1, 0, public.lang("Redirectdomain cannot be empty!")) get.redirectname = get.get("redirectname", "") if get.redirectname == "": return public.return_message(-1, 0, public.lang("Redirectname cannot be empty!")) get.sitename = get.site_name get.type = get.get("type/d", 1) get.holdpath = get.get("holdpath/d", 1) from panelRedirect import panelRedirect result =panelRedirect().ModifyRedirect(get) if not result['status']: return public.return_message(-1, 0,result['msg']) return public.return_message(0, 0,result['msg']) # 2024/4/26 下午3:32 获取指定网站指定重定向规则的信息 def GetRedirectFile(self, get): ''' @name 获取指定网站指定重定向规则的信息 @author wzz <2024/4/26 下午3:32> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' try: get.validate([ Param('path').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.path = get.get("path", "") if get.path == "": return public.return_message(-1, 0, public.lang("Path cannot be empty!")) if not os.path.exists(get.path): return public.return_message(-1, 0, public.lang("Redirection has stopped or the configuration file directory does not exist!")) import files f = files.files() result = f.GetFileBody(get) if not result['status']: del result['status'] return public.return_message(-1, 0,result) del result['status'] return public.return_message(0, 0,result) # 2024/4/22 上午11:12 设置防盗链 def SetSecurity(self, get): ''' @name 设置防盗链 @author wzz <2024/4/22 上午11:12> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('fix').String(), Param('domains').String(), Param('return_rule').String(), Param('name').String(), Param('http_status').Bool(), Param('status').Bool(), Param('id').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.fix = get.get("fix", "") if get.fix == "": return public.return_message(-1, 0, public.lang("Fix cannot be empty!")) get.domains = get.get("domains", "") if get.domains == "": return public.return_message(-1, 0, public.lang("Domains cannot be empty!")) get.return_rule = get.get("return_rule", "") if get.return_rule == "": return public.return_message(-1, 0, public.lang("Return_rule cannot be empty!")) get.http_status = get.get("http_status", "") if get.http_status == "": return public.return_message(-1, 0, public.lang("Https status cannot be empty!")) get.status = get.get("status", "") if get.status == "": return public.return_message(-1, 0, public.lang("Status cannot be empty!")) get.id = get.get("id", "") if get.id == "": return public.return_message(-1, 0, public.lang("ID cannot be empty!")) get.name = get.site_name get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["security"]["security_status"] = True if get.status == "true" else False get.proxy_json_conf["security"]["static_resource"] = get.fix get.proxy_json_conf["security"]["domains"] = get.domains get.proxy_json_conf["security"]["return_resource"] = get.return_rule get.proxy_json_conf["security"]["http_status"] = True if get.http_status else False from panel_site_v2 import panelSite result = panelSite().SetSecurity(get) if result["status"]==-1: return result self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return result # 2024/4/23 下午3:07 添加全局IP黑白名单 def add_ip_limit(self, get): ''' @name 添加全局IP黑白名单 @author wzz <2024/4/23 下午3:08> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('ip_type').String(), Param('ips').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.ip_type = get.get("ip_type", "black") if get.ip_type not in ["black", "white"]: return public.return_message(-1, 0, public.lang("The ip_type parameter is incorrect, black or white must be passed!")) get.ips = get.get("ips", "") if get.ips == "": return public.return_message(-1, 0, public.lang("IPS cannot be empty, please enter IP, one per line!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.ips = get.ips.split("\n") for ip in get.ips: if ip not in get.proxy_json_conf["ip_limit"]["ip_{}".format(get.ip_type)]: get.proxy_json_conf["ip_limit"]["ip_{}".format(get.ip_type)].append(ip) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/23 下午3:12 删除全局IP黑白名单 def del_ip_limit(self, get): ''' @name 删除全局IP黑白名单 @author wzz <2024/4/23 下午3:12> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('ip_type').String(), Param('ip').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.ip_type = get.get("ip_type", "black") if get.ip_type not in ["black", "white"]: return public.return_message(-1, 0, public.lang("The ip_type parameter is incorrect, black or white must be passed!")) get.ip = get.get("ip", "") if get.ip == "": return public.return_message(-1, 0, public.lang("IP cannot be empty, please enter IP!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if get.ip in get.proxy_json_conf["ip_limit"]["ip_{}".format(get.ip_type)]: get.proxy_json_conf["ip_limit"]["ip_{}".format(get.ip_type)].remove(get.ip) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Delete successful!")) # 2024/4/23 下午3:13 批量删除全局IP黑白名单 def batch_del_ip_limit(self, get): ''' @name 批量删除全局IP黑白名单 @author wzz <2024/4/23 下午3:14> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('ip_type').String(), Param('ips').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.ip_type = get.get("ip_type", "black") if get.ip_type not in ["black", "white", "all"]: return public.return_message(-1, 0, public.lang("The ip_type parameter is incorrect, black or white must be passed!")) get.ips = get.get("ips", "") if get.ips == "": return public.return_message(-1, 0, public.lang("IPS cannot be empty, please enter IP, one per line!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.ips = get.ips.split("\n") for ip in get.ips: if get.ip_type == "all": if ip in get.proxy_json_conf["ip_limit"]["ip_black"]: get.proxy_json_conf["ip_limit"]["ip_black"].remove(ip) if ip in get.proxy_json_conf["ip_limit"]["ip_white"]: get.proxy_json_conf["ip_limit"]["ip_white"].remove(ip) else: if ip in get.proxy_json_conf["ip_limit"]["ip_{}".format(get.ip_type)]: get.proxy_json_conf["ip_limit"]["ip_{}".format(get.ip_type)].remove(ip) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Delete successful!")) # 2024/4/22 下午9:01 获取指定网站的方向代理列表 def get_proxy_list(self, get): ''' @name @author wzz <2024/4/22 下午9:02> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} '''# 校验参数 try: get.validate([ Param('site_name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if len(get.proxy_json_conf["proxy_info"]) == 0: return public.return_message(-1, 0, public.lang("No proxy information!")) subs_filter = get.proxy_json_conf["subs_filter"] if "subs_filter" in get.proxy_json_conf else public.ExecShell("nginx -V 2>&1|grep 'ngx_http_substitutions_filter' -o")[0] != "" if get.proxy_path != "": for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: info["global_websocket"] = get.proxy_json_conf["websocket"]["websocket_status"] info["subs_filter"] = subs_filter if "rewritedir" not in info: info["rewritedir"] = json.loads(get.proxy_json_conf.get("rewritedir",'[{"dir1":"","dir2":""}]')) if "keepuri" not in info: info["keepuri"] = get.proxy_json_conf.get("keepuri",1) if info["proxy_path"] == "/": info["keepuri"]=1 if "http://unix:" in info["proxy_pass"]: info["proxy_pass"] = info["proxy_pass"].replace("http://unix:", "") return public.return_message(0, 0,info) else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) public.set_module_logs('site_proxy', 'get_proxy_list', 1) return public.return_message(0, 0,get.proxy_json_conf["proxy_info"]) # 2024/4/23 上午11:16 获取指定网站的所有配置信息 def get_global_conf(self, get): ''' @name 获取指定网站的所有配置信息 @author wzz <2024/4/23 上午11:16> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) return public.return_message(0, 0, get.proxy_json_conf) # 2024/4/22 下午9:04 设置指定网站指定URL的反向代理 def set_url_proxy(self, get): ''' @name @author wzz <2024/4/22 下午9:04> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('remark').String(), Param('proxy_pass').String(), Param('proxy_host').String(), Param('proxy_type').String(), # Param('rewritedir').String(), Param('websocket').Integer(), Param('proxy_connect_timeout').Integer(), Param('proxy_send_timeout').Integer(), Param('proxy_read_timeout').Integer(), Param('keepuri').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) #/目录不支持关闭保持uri if get.proxy_path == "/" and int(get.keepuri) == 0: return public.return_message(-1, 0, public.lang("Proxy_path is root directory, cannot close Show Proxy Path!")) get.proxy_host = get.get("proxy_host", "") if get.proxy_host == "": return public.return_message(-1, 0, public.lang("Proxy_host cannot be empty!")) get.proxy_pass = get.get("proxy_pass", "") if get.proxy_pass == "": return public.return_message(-1, 0, public.lang("Proxy_pass cannot be empty!")) #去掉路径最后的/ get.proxy_pass = get.proxy_pass.rstrip("/") get.proxy_type = get.get("proxy_type", "") if get.proxy_type == "": return public.return_message(-1, 0, public.lang("Proxy_type cannot be empty!")) get.proxy_connect_timeout = get.get("proxy_connect_timeout", "60s") get.proxy_send_timeout = get.get("proxy_send_timeout", "600s") get.proxy_read_timeout = get.get("proxy_read_timeout", "600s") get.remark = get.get("remark", "") if get.remark != "": get.remark = public.xssencode2(get.remark) if get.proxy_type == "unix": if not get.proxy_pass.startswith("http://unix:"): if not get.proxy_pass.startswith("/"): return public.return_message(-1, 0, public.lang("Unix file path must be in/or http://unix: At the beginning, such as/tmp/flash.app. lock!")) if not get.proxy_pass.endswith(".sock"): return public.return_message(-1, 0, public.lang("Unix files must end with. lock, such as/tmp/flash.app. lock!")) if not os.path.exists(get.proxy_pass): return public.return_message(-1, 0, public.lang("The proxy target does not exist!")) get.proxy_pass = "http://unix:" + get.proxy_pass elif get.proxy_type == "http": if not get.proxy_pass.startswith("http://") and not get.proxy_pass.startswith("https://"): return public.return_message(-1, 0, public.lang("The proxy target must start with http://or https://!")) #检测重写路径 checkRewriteDirArgs=self.CheckRewriteDirArgs(get) if checkRewriteDirArgs !="": return public.return_message(-1, 0, checkRewriteDirArgs) get.websocket = get.get("websocket/d", 1) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if get.proxy_json_conf["websocket"]["websocket_status"] and get.websocket != 1: return public.return_message(-1, 0, public.lang("The global websocket is in an open state, and it is not allowed to individually disable websocket support for this URL!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: info["proxy_host"] = get.proxy_host info["proxy_pass"] = get.proxy_pass info["proxy_type"] = get.proxy_type info["timeout"]["proxy_connect_timeout"] = get.proxy_connect_timeout.replace("s", "") info["timeout"]["proxy_send_timeout"] = get.proxy_send_timeout.replace("s", "") info["timeout"]["proxy_read_timeout"] = get.proxy_read_timeout.replace("s", "") info["websocket"]["websocket_status"] = True if get.websocket == 1 else False info["remark"] = get.remark info["keepuri"]=int(get.keepuri) info["rewritedir"]= json.loads(get.get("rewritedir", '[{"dir1":"","dir2":""}]')) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/22 下午9:34 删除指定网站指定URL的反向代理 def del_url_proxy(self, get): ''' @name 删除指定网站指定URL的反向代理 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: get.proxy_json_conf["proxy_info"].remove(info) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Delete successful!")) # 2024/4/22 下午9:36 设置指定网站指定URL反向代理的备注 def set_url_remark(self, get): ''' @name 设置指定网站指定URL反向代理的备注 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('remark').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.remark = get.get("remark", "") if get.remark != "": get.remark = public.xssencode2(get.remark) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: info["remark"] = get.remark break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/22 下午9:40 添加指定网站指定URL的内容替换 def add_sub_filter(self, get): ''' @name 添加指定网站指定URL的内容替换 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('oldstr').String(), Param('newstr').String(), Param('proxy_path').String(), Param('sub_type').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.oldstr = get.get("oldstr", "") get.newstr = get.get("newstr", "") if get.oldstr == "" and get.newstr == "": return public.return_message(-1, 0, public.lang("Oldstr and Newstr cannot be empty at the same time!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.sub_type = get.get("sub_type", "g") if get.sub_type == "": get.sub_type = "g" import re if not re.match(r'^[ior]+$|^g(?!.*o)|^o(?!.*g)$', get.sub_type): return public.return_message(-1, 0, public.lang("Get.sub_type can only contain letter combinations from 'g', 'i', 'o', or 'r', and 'g' and 'o' cannot coexist!")) is_subs = public.ExecShell("nginx -V 2>&1|grep 'ngx_http_substitutions_filter' -o")[0] if not is_subs and re.search(u'[\u4e00-\u9fa5]', get.oldstr + get.newstr): return public.return_message(-1, 0, public.lang("The content you entered contains Chinese. We have detected that the current version of nginx does not support it. Please try reinstalling a version of nginx 1.20 or higher and try again!")) if get.sub_type != "g" and not is_subs: return public.return_message(-1, 0, public.lang("Detected that the current nginx version only supports default replacement types. Please try reinstalling nginx version 1.20 or higher and try again!")) if not "g" in get.sub_type and not "o" in get.sub_type: get.sub_type = "g" + get.sub_type for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: for sub in info["sub_filter"]["sub_filter_str"]: if get.oldstr == sub["oldstr"]: return public.return_message(-1, 0,"Content before replacement: The configuration information for [{}] already exists, please do not add it again!".format( get.oldstr)) info["sub_filter"]["sub_filter_str"].append( { "sub_type": get.sub_type, "oldstr": get.oldstr, "newstr": get.newstr } ) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/22 下午10:00 删除指定网站指定URL的内容替换 def del_sub_filter(self, get): ''' @name 删除指定网站指定URL的内容替换 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('oldstr').String(), Param('newstr').String(), Param('proxy_path').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.oldstr = get.get("oldstr", "") get.newstr = get.get("newstr", "") if get.oldstr == "" and get.newstr == "": return public.return_message(-1, 0, public.lang("Oldstr and Newstr cannot be empty at the same time!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: for sub in info["sub_filter"]["sub_filter_str"]: if get.oldstr == sub["oldstr"]: info["sub_filter"]["sub_filter_str"].remove(sub) break else: return public.return_message(-1, 0, public.lang("No configuration information found for content before replacement: [{}]!", get.oldstr)) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Delete successful!")) # 2024/4/22 下午10:03 设置指定网站指定URL的内容压缩 def set_url_gzip(self, get): ''' @name 设置指定网站指定URL的内容压缩 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('gzip_min_length').String(), Param('proxy_path').String(), Param('gzip_types').String(), Param('gzip_status').Integer(), Param('gzip_comp_level').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.gzip_status = get.get("gzip_status/d", 999) if get.gzip_status == 999: return public.return_message(-1, 0, public.lang("Gzip_status cannot be empty, please pass number 1 or 0!")) get.gzip_min_length = get.get("gzip_min_length", "10k") get.gzip_comp_level = get.get("gzip_comp_level", "6") if get.gzip_min_length[0] == "0" or get.gzip_min_length.startswith("-"): return public.return_message(-1, 0, public.lang("The gzip_min_length parameter is invalid. Please enter a number greater than 0!")) if get.gzip_comp_level == "0" or get.gzip_comp_level.startswith("-"): return public.return_message(-1, 0, public.lang("The gzip_comp_level parameter is invalid. Please enter a number greater than 0!")) get.gzip_types = get.get( "gzip_types", "text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/json image/jpeg image/gif image/png font/ttf font/otf image/svg+xml application/xml+rss text/x-js" ) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: info["gzip"]["gzip_status"] = True if get.gzip_status == 1 else False if get.gzip_status == 1: info["gzip"]["gzip_types"] = get.gzip_types info["gzip"]["gzip_min_length"] = get.gzip_min_length info["gzip"]["gzip_comp_level"] = get.gzip_comp_level info["gzip"]["gzip_conf"] = ("gzip on;" "\n gzip_min_length {gzip_min_length};" "\n gzip_buffers 4 16k;" "\n gzip_http_version 1.1;" "\n gzip_comp_level {gzip_comp_level};" "\n gzip_types {gzip_types};" "\n gzip_vary on;" "\n gzip_proxied expired no-cache no-store private auth;" "\n gzip_disable \"MSIE [1-6]\\.\";").format( gzip_min_length=get.gzip_min_length, gzip_comp_level=get.gzip_comp_level, gzip_types=get.gzip_types, ) else: info["gzip"]["gzip_conf"] = "" break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/22 下午10:15 添加指定网站指定URL的IP黑白名单 def add_url_ip_limit(self, get): ''' @name 添加指定网站指定URL的IP黑白名单 @author wzz <2024/4/22 下午10:16> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('ip_type').String(), Param('ips').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.ip_type = get.get("ip_type", "black") if get.ip_type not in ["black", "white"]: return public.return_message(-1, 0, public.lang("The ip_type parameter is incorrect, black or white must be passed!")) get.ips = get.get("ips", "") if get.ips == "": return public.return_message(-1, 0, public.lang("IPS cannot be empty, please enter IP, one per line!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.ips = get.ips.split("\n") for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: for ip in get.ips: if get.ip_type == "black": if not ip in info["ip_limit"]["ip_black"]: info["ip_limit"]["ip_black"].append(ip) else: if not ip in info["ip_limit"]["ip_white"]: info["ip_limit"]["ip_white"].append(ip) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/22 下午10:21 删除指定网站指定URL的IP黑白名单 def del_url_ip_limit(self, get): ''' @name 删除指定网站指定URL的IP黑白名单 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('ip_type').String(), Param('ip').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.ip_type = get.get("ip_type", "black") if get.ip_type not in ["black", "white"]: return public.return_message(-1, 0, public.lang("The ip_type parameter is incorrect, black or white must be passed!")) get.ip = get.get("ip", "") if get.ip == "": return public.return_message(-1, 0, public.lang("IP cannot be empty, please enter IP!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: if get.ip in info["ip_limit"]["ip_{ip_type}".format(ip_type=get.ip_type)]: info["ip_limit"]["ip_{ip_type}".format(ip_type=get.ip_type)].remove(get.ip) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/24 上午11:21 批量删除指定网站指定URL的IP黑白名单 def batch_del_url_ip_limit(self, get): ''' @name 批量删除指定网站指定URL的IP黑白名单 @author wzz <2024/4/24 上午11:22> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('ip_type').String(), Param('ips').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.ip_type = get.get("ip_type", "black") if get.ip_type not in ["black", "white", "all"]: return public.return_message(-1, 0, public.lang("The ip_type parameter is incorrect, black or white must be passed!")) get.ips = get.get("ips", "") if get.ips == "": return public.return_message(-1, 0, public.lang("IPS cannot be empty, please enter IP, one per line!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: get.ips = get.ips.split("\n") if get.ip_type == "all": for ip in get.ips: if ip in info["ip_limit"]["ip_black"]: info["ip_limit"]["ip_black"].remove(ip) if ip in info["ip_limit"]["ip_white"]: info["ip_limit"]["ip_white"].remove(ip) else: for ip in get.ips: if ip in info["ip_limit"]["ip_{ip_type}".format(ip_type=get.ip_type)]: info["ip_limit"]["ip_{ip_type}".format(ip_type=get.ip_type)].remove(ip) break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Delete successful!")) # 2024/4/22 下午8:14 设置指定网站指定URL的缓存 def set_url_cache(self, get): ''' @name 设置指定网站指定URL的缓存 @param get: @return: ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('expires').String(), Param('proxy_path').String(), Param('cache_status').Integer(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.cache_status = get.get("cache_status/d", 999) if get.cache_status == 999: return public.return_message(-1, 0, public.lang("Cache_status cannot be empty, please pass number 1 or 0!")) get.expires = get.get("expires", "1d") if get.expires[0] == "0" or get.expires.startswith("-"): return public.return_message(-1, 0, public.lang("The expires parameter is illegal. Please enter a number greater than 0!")) expires = "expires {}".format(get.expires) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) static_cache = ("\n location ~ .*\\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\\.map|js\\.map)$" "\n {{" "\n {expires};" "\n error_log /dev/null;" "\n access_log /dev/null;" "\n }}").format( expires=expires, ) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: info["proxy_cache"]["cache_status"] = True if get.cache_status == 1 else False info["proxy_cache"]["expires"] = get.expires if get.cache_status == 1: info["proxy_cache"]["cache_conf"] = ("\n proxy_cache {cache_zone};" "\n proxy_cache_key $host$uri$is_args$args;" "\n proxy_ignore_headers Set-Cookie Cache-Control expires X-Accel-Expires;" "\n proxy_cache_valid 200 304 301 302 {expires};" "\n proxy_cache_valid 404 1m;" "{static_cache}").format( cache_zone=get.proxy_json_conf["proxy_cache"]["cache_zone"], expires=get.expires, static_cache=static_cache, ) else: info["proxy_cache"]["cache_conf"] = "" break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) # 2024/4/24 上午9:57 设置指定网站指定URL的自定义配置 def set_url_custom_conf(self, get): ''' @name 设置指定网站指定URL的自定义配置 @author wzz <2024/4/24 上午9:58> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), Param('proxy_path').String(), Param('custom_conf').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_path = get.get("proxy_path", "") if get.proxy_path == "": return public.return_message(-1, 0, public.lang("Proxy_path cannot be empty!")) get.custom_conf = get.get("custom_conf", "") get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) for info in get.proxy_json_conf["proxy_info"]: if info["proxy_path"] == get.proxy_path: info["custom_conf"] = get.custom_conf break else: return public.return_message(-1, 0, public.lang("No proxy information found for this URL [{}]!", get.proxy_path)) update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.return_message(0, 0, public.lang("Set successfully!")) @staticmethod def nginx_get_log_file(nginx_config: str, is_error_log: bool = False): import re 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: return None for i in re_data: file_path = i[0].strip(";") if file_path != "/dev/null": return file_path return None def xsssec(self, text): replace_list = { "<": "<", ">": ">", "'": "'", '"': """, } for k, v in replace_list.items(): text = text.replace(k, v) return public.xssencode2(text) # 2024/4/24 下午5:39 获取指定网站的网站日志 def GetSiteLogs(self, get): ''' @name 获取指定网站的网站日志 @author wzz <2024/4/24 下午5:39> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('type').String(), Param('site_name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name不能为空!")) get.type = get.get("type", "access") log_name = get.site_name if get.type != "access": log_name = get.site_name + ".error" get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) if get.proxy_json_conf["proxy_log"]["log_type"] == "default": log_file = public.get_logs_path() + "/" + log_name + '.log' elif get.proxy_json_conf["proxy_log"]["log_type"] == "file": log_file = get.proxy_json_conf["proxy_log"]["log_path"] + "/" + log_name + '.log' else: return public.return_message(0, 0,{"msg": "", "size": 0}) if os.path.exists(log_file): return public.return_message(0, 0,{ "msg": self.xsssec(public.GetNumLines(log_file, 1000)), "size": public.to_size(os.path.getsize(log_file)) } ) return public.return_message(0, 0,{"msg": "", "size": 0}) # 2024/4/25 上午10:51 清理指定网站的反向代理缓存 def clear_cache(self, get): ''' @name 清理指定网站的反向代理缓存 @author wzz <2024/4/25 上午10:51> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' # 校验参数 try: get.validate([ Param('site_name').String(), ], [ public.validate.trim_filter(), ]) except Exception as ex: public.print_log("error info: {}".format(ex)) return public.return_message(-1, 0, str(ex)) get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("Sitename cannot be empty!")) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) cache_dir = "/www/wwwroot/{site_name}/proxy_cache_dir".format(site_name=get.site_name) if os.path.exists(cache_dir): public.ExecShell("rm -rf {cache_dir}/*".format(cache_dir=cache_dir)) public.serviceReload() return public.return_message(0, 0, public.lang("Cleanup successful!")) return public.return_message(-1, 0, public.lang("Cleanup failed, cache directory does not exist!")) # 2024/4/25 下午9:24 设置指定网站的https端口 def set_https_port(self, get): ''' @name 设置指定网站的https端口 @author wzz <2024/4/25 下午9:24> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.site_name = get.get("site_name", "") if get.site_name == "": return public.return_message(-1, 0, public.lang("site_name cannot be empty!")) get.https_port = get.get("https_port", "443") if not public.checkPort(get.https_port) and get.https_port != "443": return public.return_message(-1, 0, public.lang("https port [{}] is illegal!", get.https_port)) get.proxy_json_conf = self.read_json_conf(get)['message'] if not get.proxy_json_conf: return public.return_message(-1, 0, public.lang("Reading configuration file failed, please delete the website and add it again!")) get.proxy_json_conf["https_port"] = get.https_port update_result = self.update_conf(get) if update_result["status"]==-1: return update_result return public.returnResult(msg="设置成功!") # 2024/4/23 下午2:12 保存并重新生成新的nginx配置文件 def update_conf(self, get): ''' @name @author wzz <2024/4/23 下午2:13> @param "data":{"参数名":""} <数据类型> 参数描述 @return dict{"status":True/False,"msg":"提示信息"} ''' get.conf_file = public.get_setup_path() + '/panel/vhost/nginx/' + get.site_name + '.conf' self.generate_config(get) get.data = get.site_conf get.encoding = "utf-8" get.path = get.conf_file import files f = files.files() save_result = f.SaveFileBody(get) if save_result["status"] == False: return public.return_message(-1,0,save_result["msg"]) self._site_proxy_conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=get.site_name ) public.writeFile(self._site_proxy_conf_path, json.dumps(get.proxy_json_conf)) return public.return_message(0, 0, public.lang("Save successful!"))