mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-23 08:02:24 +02:00
1. Added Mail Marketing Automation trigger tasks 2. Added Mail Marketing Groups Import, Export, Merge 3. Added Mail Marketing Subscribers to support paste import 4. Added Mail Marketing Template Import Export Duplicate 5. Added Mail server add timed automatic reply 6. Added HTTPS Protection function for Website (disables automatic HTTP to HTTPS redirection when enabled) 7. Optimize Website Interface Button Integration 8. Optimize the response speed of WP Toolkit interface
275 lines
11 KiB
Python
275 lines
11 KiB
Python
import json
|
|
import os
|
|
import time
|
|
from typing import Tuple, Union, Optional
|
|
|
|
from .mods import PUSH_DATA_PATH, TaskTemplateConfig
|
|
from .send_tool import WxAccountMsg
|
|
from .base_task import BaseTask
|
|
from .util import read_file, DB, GET_CLASS, write_file
|
|
|
|
|
|
class NginxLoadTask(BaseTask):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.source_name = "nginx_load_push"
|
|
self.template_name = "Load balancing alarm"
|
|
# self.title = "Load balancing alarm"
|
|
self._tip_counter = None
|
|
|
|
@property
|
|
def tip_counter(self) -> dict:
|
|
if self._tip_counter is not None:
|
|
return self._tip_counter
|
|
tip_counter = '{}/load_balance_push.json'.format(PUSH_DATA_PATH)
|
|
if os.path.exists(tip_counter):
|
|
try:
|
|
self._tip_counter = json.loads(read_file(tip_counter))
|
|
except json.JSONDecodeError:
|
|
self._tip_counter = {}
|
|
else:
|
|
self._tip_counter = {}
|
|
return self._tip_counter
|
|
|
|
def save_tip_counter(self):
|
|
tip_counter = '{}/load_balance_push.json'.format(PUSH_DATA_PATH)
|
|
write_file(tip_counter, json.dumps(self.tip_counter))
|
|
|
|
def get_title(self, task_data: dict) -> str:
|
|
if task_data["project"] == "all":
|
|
return "Load balancing alarm"
|
|
return "Load balancing alarm -- [{}] ".format(task_data["project"])
|
|
|
|
def check_task_data(self, task_data: dict) -> Union[dict, str]:
|
|
all_upstream_name = DB("upstream").field("name").select()
|
|
if isinstance(all_upstream_name, str) and all_upstream_name.startswith("error"):
|
|
return 'Alarms cannot be set without load balancing configuration'
|
|
all_upstream_name = [i["name"] for i in all_upstream_name]
|
|
if not bool(all_upstream_name):
|
|
return 'Alarms cannot be set without load balancing configuration'
|
|
if task_data["project"] not in all_upstream_name and task_data["project"] != "all":
|
|
return 'Without this load balancer configuration, alarms cannot be set'
|
|
|
|
cycle = []
|
|
for i in task_data["cycle"].split("|"):
|
|
if bool(i) and i.isdecimal():
|
|
code = int(i)
|
|
if 100 <= code < 600:
|
|
cycle.append(str(code))
|
|
if not bool(cycle):
|
|
return 'If no error code is specified, the alarm cannot be set'
|
|
|
|
task_data["cycle"] = "|".join(cycle)
|
|
return task_data
|
|
|
|
def get_keyword(self, task_data: dict) -> str:
|
|
return task_data["project"]
|
|
|
|
def _check_func(self, upstream_name: str, codes: str) -> list:
|
|
import PluginLoader
|
|
get_obj = GET_CLASS()
|
|
get_obj.upstream_name = upstream_name
|
|
# 调用外部插件检查负载均衡的健康状况
|
|
upstreams = PluginLoader.plugin_run("load_balance", "get_check_upstream", get_obj)
|
|
access_codes = [int(i) for i in codes.split("|") if bool(i.strip())]
|
|
res_list = []
|
|
for upstream in upstreams:
|
|
# 检查每个节点,返回有问题的节点信息
|
|
res = upstream.check_nodes(access_codes, return_nodes=True)
|
|
for ping_url in res:
|
|
if ping_url in self.tip_counter:
|
|
self.tip_counter[ping_url].append(int(time.time()))
|
|
idx = 0
|
|
for i in self.tip_counter[ping_url]:
|
|
# 清理超过4分钟的记录
|
|
if time.time() - i > 60 * 4:
|
|
idx += 1
|
|
self.tip_counter[ping_url] = self.tip_counter[ping_url][idx:]
|
|
print("self.tip_counter[ping_url]",self.tip_counter[ping_url])
|
|
# 如果一个节点连续三次出现在告警列表中,则视为需要告警
|
|
if len(self.tip_counter[ping_url]) >= 3:
|
|
res_list.append(ping_url)
|
|
self.tip_counter[ping_url] = []
|
|
else:
|
|
self.tip_counter[ping_url] = [int(time.time()), ]
|
|
self.save_tip_counter()
|
|
return res_list
|
|
|
|
|
|
def get_push_data(self, task_id: str, task_data: dict) -> Optional[dict]:
|
|
err_nodes = self._check_func(task_data["project"], task_data["cycle"])
|
|
if not err_nodes:
|
|
return None
|
|
pj = "load balancing:【{}】".format(task_data["project"]) if task_data["project"] != "all" else "load balancing"
|
|
nodes = '、'.join(err_nodes)
|
|
self.title = self.get_title(task_data)
|
|
return {
|
|
"msg_list": [
|
|
">Notification type: Enterprise Edition load balancing alarm",
|
|
">Content of alarm: <font color=#ff0000>{}The node [{}] under the configuration has access error, please pay attention to the node situation in time and deal with it.</font> ".format(
|
|
pj, nodes),
|
|
],
|
|
"pj": pj,
|
|
"nodes": nodes
|
|
}
|
|
|
|
def filter_template(self, template: dict) -> Optional[dict]:
|
|
if not os.path.exists("/www/server/panel/plugin/load_balance/load_balance_main.py"):
|
|
return None
|
|
all_upstream = DB("upstream").field("name").select()
|
|
if isinstance(all_upstream, str) and all_upstream.startswith("error"):
|
|
return None
|
|
all_upstream_name = [i["name"] for i in all_upstream]
|
|
if not all_upstream_name:
|
|
return None
|
|
for name in all_upstream_name:
|
|
template["field"][0]["items"].append({
|
|
"title": name,
|
|
"value": name
|
|
})
|
|
return template
|
|
|
|
def to_sms_msg(self, push_data: dict, push_public_data: dict) -> Tuple[str, dict]:
|
|
return '', {}
|
|
|
|
def to_wx_account_msg(self, push_data: dict, push_public_data: dict) -> WxAccountMsg:
|
|
msg = WxAccountMsg.new_msg()
|
|
msg.thing_type = "Load balancing alarm"
|
|
msg.msg = "If the node is abnormal, log in to the panel"
|
|
return msg
|
|
|
|
def task_config_create_hook(self, task: dict) -> None:
|
|
old_config_file = "/www/server/panel/class/push/push.json"
|
|
try:
|
|
old_config = json.loads(read_file(old_config_file))
|
|
except:
|
|
return
|
|
if "load_balance_push" not in old_config:
|
|
old_config["load_balance_push"] = {}
|
|
old_data = {
|
|
"push_count": task["number_rule"].get("day_num", 2),
|
|
"cycle": task["task_data"].get("cycle", "200|301|302|403|404"),
|
|
"interval": task["task_data"].get("interval", 60),
|
|
"title": task["title"],
|
|
"status": task['status'],
|
|
"module": ",".join(task["sender"])
|
|
}
|
|
for k, v in old_config["load_balance_push"].items():
|
|
if v["project"] == task["task_data"]["project"]:
|
|
v.update(old_data)
|
|
else:
|
|
old_data["project"] = task["task_data"]["project"]
|
|
old_config["load_balance_push"][int(time.time())] = old_data
|
|
|
|
write_file(old_config_file, json.dumps(old_config))
|
|
|
|
def task_config_update_hook(self, task: dict) -> None:
|
|
return self.task_config_create_hook(task)
|
|
|
|
def task_config_remove_hook(self, task: dict) -> None:
|
|
old_config_file = "/www/server/panel/class/push/push.json"
|
|
try:
|
|
old_config = json.loads(read_file(old_config_file))
|
|
except:
|
|
return
|
|
if "load_balance_push" not in old_config:
|
|
old_config["load_balance_push"] = {}
|
|
old_config["load_balance_push"] = {
|
|
k: v for k, v in old_config["load_balance_push"].items()
|
|
if v["project"] != task["task_data"]["project"]
|
|
}
|
|
|
|
|
|
def load_load_template():
|
|
if TaskTemplateConfig().get_by_id("50"):
|
|
return None
|
|
|
|
from .mods import load_task_template_by_config
|
|
load_task_template_by_config(
|
|
[{
|
|
"id": "50",
|
|
"ver": "1",
|
|
"used": True,
|
|
"source": "nginx_load_push",
|
|
"title": "load balancing",
|
|
"load_cls": {
|
|
"load_type": "path",
|
|
"cls_path": "mod.base.push_mod.load_push",
|
|
"name": "NginxLoadTask"
|
|
},
|
|
"template": {
|
|
"field": [
|
|
{
|
|
"attr": "project",
|
|
"name": "The name of the payload",
|
|
"type": "select",
|
|
"default": "all",
|
|
"unit": "",
|
|
"suffix": (
|
|
"<i style='color: #999;font-style: initial;font-size: 12px;margin-right: 5px'>*</i>"
|
|
"<span style='color:#999'>If a node fails to access a node in the selected load configuration, an alarm is triggered</span>"
|
|
),
|
|
"items": [
|
|
{
|
|
"title": "All configured loads",
|
|
"value": "all"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"attr": "cycle",
|
|
"name": "The status code of the success",
|
|
"type": "textarea",
|
|
"unit": "",
|
|
"suffix": (
|
|
"<br><i style='color: #999;font-style: initial;font-size: 12px;margin-right: 5px'>*</i>"
|
|
"<span style='color:#999'>Status codes are separated by vertical bars, for example:200|301|302|403|404</span>"
|
|
),
|
|
"width": "400px",
|
|
"style": {
|
|
'height': '70px',
|
|
},
|
|
"default": "200|301|302|403|404"
|
|
}
|
|
],
|
|
"sorted": [
|
|
[
|
|
"project"
|
|
],
|
|
[
|
|
"cycle"
|
|
]
|
|
],
|
|
},
|
|
"default": {
|
|
"project": "all",
|
|
"cycle": "200|301|302|403|404"
|
|
},
|
|
"advanced_default": {
|
|
"number_rule": {
|
|
"day_num": 3
|
|
}
|
|
},
|
|
"send_type_list": [
|
|
"wx_account",
|
|
"dingding",
|
|
"feishu",
|
|
"mail",
|
|
"weixin",
|
|
"webhook",
|
|
"tg",
|
|
],
|
|
"unique": False
|
|
}]
|
|
)
|
|
|
|
|
|
class ViewMsgFormat(object):
|
|
|
|
@staticmethod
|
|
def get_msg(task: dict) -> Optional[str]:
|
|
if task["template_id"] == "50":
|
|
return "<span>When the node access is abnormal, the alarm message is pushed (it is not pushed after {} times per day)<span>".format(
|
|
task.get("number_rule", {}).get("day_num"))
|
|
return None
|