961 lines
34 KiB
Python
961 lines
34 KiB
Python
|
|
# coding: utf-8
|
|||
|
|
# +-------------------------------------------------------------------
|
|||
|
|
# | YakPanel
|
|||
|
|
# +-------------------------------------------------------------------
|
|||
|
|
# | Copyright (c) 2015-2016 YakPanel(www.yakpanel.com) All rights reserved.
|
|||
|
|
# +-------------------------------------------------------------------
|
|||
|
|
# | Author: 沐落 <cjx@yakpanel.com>
|
|||
|
|
# | Author: lx
|
|||
|
|
# | 消息推送管理
|
|||
|
|
# | 对外方法 get_modules_list、install_module、uninstall_module、get_module_template、set_push_config、get_push_config、del_push_config
|
|||
|
|
# +-------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
import os, sys
|
|||
|
|
import json
|
|||
|
|
import time
|
|||
|
|
from importlib import import_module
|
|||
|
|
|
|||
|
|
panelPath = "/www/server/panel"
|
|||
|
|
os.chdir(panelPath)
|
|||
|
|
sys.path.insert(0, panelPath + "/class/")
|
|||
|
|
import public
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
from YakPanel import session
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class panelPush:
|
|||
|
|
__conf_path = "{}/class/push/push.json".format(panelPath)
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
spath = '{}/class/push'.format(panelPath)
|
|||
|
|
if not os.path.exists(spath): os.makedirs(spath)
|
|||
|
|
self._all_push_mode = {}
|
|||
|
|
self.base_push_mode = None
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def all_push_mode(self) -> dict:
|
|||
|
|
if self._all_push_mode:
|
|||
|
|
return self._all_push_mode
|
|||
|
|
spath = '{}/class/push'.format(panelPath)
|
|||
|
|
module_names = [i[:-3] for i in os.listdir(spath) if i.endswith(".py")]
|
|||
|
|
for m in module_names:
|
|||
|
|
try:
|
|||
|
|
push_class = getattr(import_module(".{}".format(m), package="push"), m, None)
|
|||
|
|
except:
|
|||
|
|
continue
|
|||
|
|
if m == "base_push":
|
|||
|
|
self.base_push_mode = push_class()
|
|||
|
|
elif push_class is not None:
|
|||
|
|
self._all_push_mode[m] = push_class()
|
|||
|
|
|
|||
|
|
return self._all_push_mode
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@获取推送模块列表
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_modules_list(self, get):
|
|||
|
|
cpath = '{}/class/push/push_list.json'.format(panelPath)
|
|||
|
|
try:
|
|||
|
|
spath = os.path.dirname(cpath)
|
|||
|
|
if not os.path.exists(spath): os.makedirs(spath)
|
|||
|
|
|
|||
|
|
if 'force' in get or not os.path.exists(cpath):
|
|||
|
|
if not 'download_url' in session: session['download_url'] = public.get_url()
|
|||
|
|
public.downloadFile('{}/linux/panel/push/push_list.json'.format(session['download_url']), cpath)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
if not os.path.exists(cpath):
|
|||
|
|
return {}
|
|||
|
|
|
|||
|
|
data = {}
|
|||
|
|
push_list = self._get_conf()
|
|||
|
|
# module_list = public.get_modules('class/push')
|
|||
|
|
module_list = self.all_push_mode
|
|||
|
|
|
|||
|
|
configs = json.loads(public.readFile(cpath))
|
|||
|
|
for p_info in configs:
|
|||
|
|
p_info['data'] = {}
|
|||
|
|
p_info['setup'] = False
|
|||
|
|
p_info['info'] = False
|
|||
|
|
key = p_info['name']
|
|||
|
|
try:
|
|||
|
|
if key in module_list:
|
|||
|
|
p_info['setup'] = True
|
|||
|
|
# if key in module_list:
|
|||
|
|
# print(dir(module_list))
|
|||
|
|
# print(dir(module_list[key]))
|
|||
|
|
# print(dir(getattr(module_list[key], key)))
|
|||
|
|
push_module = module_list[key]
|
|||
|
|
p_info['info'] = push_module.get_version_info(None)
|
|||
|
|
# 格式化消息通道
|
|||
|
|
if key in push_list:
|
|||
|
|
p_info['data'] = self.__get_push_list(push_list[key])
|
|||
|
|
# 格式化返回执行周期
|
|||
|
|
if hasattr(push_module, 'get_push_cycle'):
|
|||
|
|
p_info['data'] = push_module.get_push_cycle(p_info['data'])
|
|||
|
|
except:
|
|||
|
|
return public.get_error_object(None)
|
|||
|
|
data[key] = p_info
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
安装/更新消息通道模块
|
|||
|
|
@name 需要安装的模块名称
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def install_module(self, get):
|
|||
|
|
module_name = get.name
|
|||
|
|
down_url = public.get_url()
|
|||
|
|
|
|||
|
|
local_path = '{}/class/push'.format(panelPath)
|
|||
|
|
if not os.path.exists(local_path): os.makedirs(local_path)
|
|||
|
|
|
|||
|
|
sfile = '{}/{}.py'.format(local_path, module_name)
|
|||
|
|
public.downloadFile('{}/linux/panel/push/{}.py'.format(down_url, module_name), sfile)
|
|||
|
|
if not os.path.exists(sfile): return public.returnMsg(False, public.lang("[{}] Module installation failed",
|
|||
|
|
module_name))
|
|||
|
|
if os.path.getsize(sfile) < 1024: return public.returnMsg(False, public.lang("[{}] Module installation failed",
|
|||
|
|
module_name))
|
|||
|
|
|
|||
|
|
sfile = '{}/class/push/{}.html'.format(panelPath, module_name)
|
|||
|
|
public.downloadFile('{}/linux/panel/push/{}.html'.format(down_url, module_name), sfile)
|
|||
|
|
|
|||
|
|
return public.returnMsg(True, public.lang("[{}] Module installed successfully.", module_name))
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
卸载消息通道模块
|
|||
|
|
@name 需要卸载的模块名称
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def uninstall_module(self, get):
|
|||
|
|
module_name = get.name
|
|||
|
|
sfile = '{}/class/push/{}.py'.format(panelPath, module_name)
|
|||
|
|
if os.path.exists(sfile): os.remove(sfile)
|
|||
|
|
|
|||
|
|
return public.returnMsg(True, public.lang("[{}] Module uninstalled successfully", module_name))
|
|||
|
|
# 格式化传参 修改
|
|||
|
|
# public.lang("[{}] xxx", a)) public.lang("[{}] xxx",aa)
|
|||
|
|
# 写日志 修改
|
|||
|
|
# public.WriteLog("Nginx firewall alarm", msg)
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@获取模块执行日志
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_module_logs(self, get):
|
|||
|
|
module_name = get.name
|
|||
|
|
id = get.id
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
获取模块模板
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_module_template(self, get):
|
|||
|
|
sfile = '{}/class/push/{}.html'.format(panelPath, get.module_name)
|
|||
|
|
|
|||
|
|
if not os.path.exists(sfile):
|
|||
|
|
return public.returnMsg(False, public.lang("template file does not exist!"))
|
|||
|
|
|
|||
|
|
shtml = public.readFile(sfile)
|
|||
|
|
return public.returnMsg(True, shtml)
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@获取模块推送参数,如:panel_push ssl到期,服务停止
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_module_config(self, get):
|
|||
|
|
module = get.name
|
|||
|
|
# p_list = public.get_modules('class/push')
|
|||
|
|
p_list = self.all_push_mode
|
|||
|
|
push_module = p_list.get(module, None)
|
|||
|
|
|
|||
|
|
if not module in p_list:
|
|||
|
|
return public.returnMsg(False, public.lang("The specified module [{}] is not installed!", module))
|
|||
|
|
|
|||
|
|
if not hasattr(push_module, 'get_module_config'):
|
|||
|
|
return public.returnMsg(False,
|
|||
|
|
public.lang("No get_module_config method exists for the specified module [{}].",
|
|||
|
|
module))
|
|||
|
|
return push_module.get_module_config(get)
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@获取模块配置项
|
|||
|
|
@优先调用模块内的get_push_config
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_push_config(self, get):
|
|||
|
|
module = get.name
|
|||
|
|
id = get.id
|
|||
|
|
# p_list = public.get_modules('class/push')
|
|||
|
|
p_list = self.all_push_mode
|
|||
|
|
if not module in p_list:
|
|||
|
|
return public.returnMsg(False, public.lang("The specified module [{}] is not installed.", module))
|
|||
|
|
|
|||
|
|
result = None
|
|||
|
|
push_module = p_list[module]
|
|||
|
|
if not hasattr(push_module, 'get_push_config'):
|
|||
|
|
push_list = self._get_conf()
|
|||
|
|
|
|||
|
|
res_data = public.returnMsg(False, 'The specified configuration was not found!')
|
|||
|
|
res_data['code'] = 100
|
|||
|
|
if not module in push_list:
|
|||
|
|
return res_data
|
|||
|
|
if not id in push_list[module]:
|
|||
|
|
return res_data
|
|||
|
|
|
|||
|
|
result = push_list[module][id]
|
|||
|
|
else:
|
|||
|
|
result = push_module.get_push_config(get)
|
|||
|
|
if "code" in result:
|
|||
|
|
return result
|
|||
|
|
return self.get_push_user(result)
|
|||
|
|
|
|||
|
|
def get_push_user(self, result):
|
|||
|
|
|
|||
|
|
# 获取发送给谁
|
|||
|
|
if not 'to_user' in result:
|
|||
|
|
result['to_user'] = {}
|
|||
|
|
if 'module' in result:
|
|||
|
|
for s_module in result['module'].split(','):
|
|||
|
|
result['to_user'][s_module] = 'default'
|
|||
|
|
else:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
info = {}
|
|||
|
|
for s_module in result['module'].split(','):
|
|||
|
|
if s_module == "":
|
|||
|
|
continue
|
|||
|
|
msg_obj = public.init_msg(s_module)
|
|||
|
|
if not msg_obj: continue
|
|||
|
|
|
|||
|
|
info[s_module] = {}
|
|||
|
|
data = msg_obj.get_config(None)
|
|||
|
|
|
|||
|
|
if 'list' in data:
|
|||
|
|
for key in result['to_user'][s_module].split(','):
|
|||
|
|
if not key in data['list']:
|
|||
|
|
continue
|
|||
|
|
info[s_module][key] = data['list'][key]
|
|||
|
|
result['user_info'] = info
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@设置推送配置
|
|||
|
|
@优先调用模块内的set_push_config
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def set_push_config(self, get):
|
|||
|
|
if not hasattr(get, "id"):
|
|||
|
|
return public.returnMsg(False, public.lang('Parameter id is missing'))
|
|||
|
|
if not hasattr(get, "name"):
|
|||
|
|
return public.returnMsg(False, public.lang('There is no module information'))
|
|||
|
|
if not hasattr(get, "data"):
|
|||
|
|
return public.returnMsg(False, public.lang('There is no alarm setting information'))
|
|||
|
|
|
|||
|
|
module = get.name
|
|||
|
|
id = get.id
|
|||
|
|
# p_list = public.get_modules('class/push')
|
|||
|
|
p_list = self.all_push_mode
|
|||
|
|
get_data_dict = json.loads(get['data'])
|
|||
|
|
if 'title' in get_data_dict.keys():
|
|||
|
|
title = get_data_dict["title"]
|
|||
|
|
elif "type" in get_data_dict.keys():
|
|||
|
|
title = get_data_dict["type"]
|
|||
|
|
else:
|
|||
|
|
title = module
|
|||
|
|
res = public.WriteLog('Alarm settings', 'Add an alarm task [{}]'.format(title))
|
|||
|
|
if not module in p_list:
|
|||
|
|
return public.returnMsg(False, public.lang("The specified module [{}] is not installed.", module))
|
|||
|
|
|
|||
|
|
pdata = json.loads(get.data)
|
|||
|
|
if not 'module' in pdata or not pdata['module']:
|
|||
|
|
return public.returnMsg(False, public.lang("The specified alarm method is not set, please select again."))
|
|||
|
|
if module == "load_balance_push":
|
|||
|
|
pdata = self.__get_args(pdata, 'cycle', "200|301|302|403|404", type_list=(str,))
|
|||
|
|
else:
|
|||
|
|
pdata = self.__get_args(pdata, 'cycle', 1, type_list=(int,))
|
|||
|
|
pdata = self.__get_args(pdata, 'count', 1, type_list=(int, float))
|
|||
|
|
pdata = self.__get_args(pdata, 'interval', 600, type_list=(int, float))
|
|||
|
|
pdata = self.__get_args(pdata, 'key', '', type_list=(str,))
|
|||
|
|
if "next_data" in pdata and not isinstance(pdata["next_data"], (str, dict, list, tuple)):
|
|||
|
|
pdata["next_data"] = []
|
|||
|
|
|
|||
|
|
if "day_limit" in pdata and not isinstance(pdata["day_limit"], (str, int, float)):
|
|||
|
|
pdata["day_limit"] = 0
|
|||
|
|
|
|||
|
|
nData = {}
|
|||
|
|
for skey in ['key', 'type', 'cycle', 'count', 'interval', 'module', 'title', 'project', 'status', 'index',
|
|||
|
|
'push_count', "next_data", "day_limit"]:
|
|||
|
|
if skey in pdata:
|
|||
|
|
nData[skey] = pdata[skey]
|
|||
|
|
|
|||
|
|
if isinstance(nData["push_count"], (int, float)):
|
|||
|
|
nData["push_count"] = int(nData["push_count"])
|
|||
|
|
elif isinstance(nData["push_count"], str):
|
|||
|
|
try:
|
|||
|
|
nData["push_count"] = int(nData["push_count"])
|
|||
|
|
except:
|
|||
|
|
nData["push_count"] = 3
|
|||
|
|
try:
|
|||
|
|
public.set_module_logs('set_push_config', nData['type'])
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
class_obj = p_list[module]
|
|||
|
|
if hasattr(class_obj, 'set_push_config'):
|
|||
|
|
get['data'] = json.dumps(nData)
|
|||
|
|
result = class_obj.set_push_config(get)
|
|||
|
|
if 'status' in result: return result
|
|||
|
|
|
|||
|
|
data = result
|
|||
|
|
else:
|
|||
|
|
data = self._get_conf()
|
|||
|
|
if not module in data: data[module] = {}
|
|||
|
|
data[module][id] = nData
|
|||
|
|
|
|||
|
|
public.writeFile(self.__conf_path, json.dumps(data))
|
|||
|
|
# 兼容 负载均衡中的接口
|
|||
|
|
if module == "load_balance_push":
|
|||
|
|
try:
|
|||
|
|
from mod.base.push_mod import PushManager, get_default_module_dict
|
|||
|
|
pmgr = PushManager()
|
|||
|
|
df_mdl = get_default_module_dict()
|
|||
|
|
push_data = {
|
|||
|
|
"template_id": "50",
|
|||
|
|
"task_data": {
|
|||
|
|
"sender": [df_mdl[i.strip()] for i in nData.get("module", "").split(",") if
|
|||
|
|
i.strip() in df_mdl],
|
|||
|
|
"task_data": {
|
|||
|
|
"project": nData.get("project", ""),
|
|||
|
|
"cycle": nData.get("cycle", "200|301|302|403|404")
|
|||
|
|
},
|
|||
|
|
"number_rule": {
|
|||
|
|
"day_num": nData.get("push_count", 2)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
res = pmgr.set_task_conf_data(push_data)
|
|||
|
|
public.print_log(res)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 兼容 文件同步中的接口
|
|||
|
|
if module == "rsync_push":
|
|||
|
|
try:
|
|||
|
|
from mod.base.push_mod import PushManager, get_default_module_dict
|
|||
|
|
pmgr = PushManager()
|
|||
|
|
df_mdl = get_default_module_dict()
|
|||
|
|
sender_list = [df_mdl[i.strip()] for i in nData.get("module", "").split(",") if i.strip() in df_mdl]
|
|||
|
|
push_data = {
|
|||
|
|
"template_id": "40",
|
|||
|
|
"task_data": {
|
|||
|
|
"status": bool(nData.get("status", True)),
|
|||
|
|
"sender": sender_list,
|
|||
|
|
"task_data": {
|
|||
|
|
"interval": nData.get("interval", 600)
|
|||
|
|
},
|
|||
|
|
"number_rule": {
|
|||
|
|
"day_num": nData.get("push_count", 3)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
pmgr.set_task_conf_data(push_data)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
return public.returnMsg(True, public.lang("Saved successfully"))
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@设置推送状态
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def set_push_status(self, get):
|
|||
|
|
id = get.id
|
|||
|
|
module = get.name
|
|||
|
|
|
|||
|
|
data = self._get_conf()
|
|||
|
|
# 兼容 文件同步中的接口
|
|||
|
|
if module == "rsync_push":
|
|||
|
|
try:
|
|||
|
|
from mod.base.push_mod import TaskConfig
|
|||
|
|
tc = TaskConfig()
|
|||
|
|
# print(tc)
|
|||
|
|
for i in tc.config:
|
|||
|
|
# print(i)
|
|||
|
|
if i["source"] == i['keyword'] == "rsync_push":
|
|||
|
|
print(i)
|
|||
|
|
i["status"] = int(get.status)
|
|||
|
|
|
|||
|
|
print(i["status"])
|
|||
|
|
tc.save_config()
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
if not module in data: return public.returnMsg(True, public.lang("module name does not exist!"))
|
|||
|
|
if not id in data[module]: return public.returnMsg(True, public.lang("The specified push task does not exist!"))
|
|||
|
|
|
|||
|
|
status = int(get.status)
|
|||
|
|
if status:
|
|||
|
|
data[module][id]['status'] = True
|
|||
|
|
else:
|
|||
|
|
data[module][id]['status'] = False
|
|||
|
|
public.writeFile(self.__conf_path, json.dumps(data))
|
|||
|
|
return public.returnMsg(True, public.lang("Successful operation."))
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@删除指定配置
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def del_push_config(self, get):
|
|||
|
|
id = get.id
|
|||
|
|
module = get.name
|
|||
|
|
|
|||
|
|
# p_list = public.get_modules('class/push')
|
|||
|
|
p_list = self.all_push_mode
|
|||
|
|
if not module in p_list:
|
|||
|
|
return public.returnMsg(False, public.lang("The specified module {} is not installed.", module))
|
|||
|
|
push_module = p_list[module]
|
|||
|
|
if not hasattr(push_module, 'del_push_config'):
|
|||
|
|
data = self._get_conf()
|
|||
|
|
del data[module][id]
|
|||
|
|
public.writeFile(self.__conf_path, json.dumps(data))
|
|||
|
|
return public.returnMsg(True, public.lang("successfully deleted."))
|
|||
|
|
|
|||
|
|
return push_module.del_push_config(get)
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
获取消息通道配置列表
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_push_msg_list(self, get):
|
|||
|
|
data = {}
|
|||
|
|
msgs = self.__get_msg_list()
|
|||
|
|
from panelMessage import panelMessage
|
|||
|
|
pm = panelMessage()
|
|||
|
|
for x in msgs:
|
|||
|
|
x['setup'] = False
|
|||
|
|
key = x['name']
|
|||
|
|
try:
|
|||
|
|
obj = pm.init_msg_module(key)
|
|||
|
|
if obj:
|
|||
|
|
x['setup'] = True
|
|||
|
|
if key == 'sms': x[
|
|||
|
|
'title'] = '{}<a title="Please make sure there are enough SMS messages, otherwise you will not be able to receive notifications." href="javascript:;" class="bt-ico-ask">?</a>'.format(
|
|||
|
|
x['title'])
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
data[key] = x
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@ 获取消息推送配置
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def _get_conf(self):
|
|||
|
|
data = {}
|
|||
|
|
try:
|
|||
|
|
if os.path.exists(self.__conf_path):
|
|||
|
|
data = json.loads(public.readFile(self.__conf_path))
|
|||
|
|
self.update_config(data)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@ 获取插件版本信息
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def get_version_info(self):
|
|||
|
|
"""
|
|||
|
|
获取版本信息
|
|||
|
|
"""
|
|||
|
|
data = {}
|
|||
|
|
data['ps'] = ''
|
|||
|
|
data['version'] = '1.0'
|
|||
|
|
data['date'] = '2020-07-14'
|
|||
|
|
data['author'] = 'YakPanel'
|
|||
|
|
data['help'] = 'http://www.yakpanel.com'
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@格式化推送对象
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def format_push_data(self, push=['dingding', 'weixin', 'feishu'], project='', type=''):
|
|||
|
|
item = {
|
|||
|
|
'title': '',
|
|||
|
|
'project': project,
|
|||
|
|
'type': type,
|
|||
|
|
'cycle': 1,
|
|||
|
|
'count': 1,
|
|||
|
|
'keys': [],
|
|||
|
|
'helps': [],
|
|||
|
|
'push': push
|
|||
|
|
}
|
|||
|
|
return item
|
|||
|
|
|
|||
|
|
def push_message_immediately(self, channel_data):
|
|||
|
|
"""推送消息到指定的消息通道,即时
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
channel_data(dict):
|
|||
|
|
key: msg_channel, 消息通道名称,多个用逗号相连
|
|||
|
|
value: msg obj, 每种消息通道的消息内容格式,可能包含标题
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
{
|
|||
|
|
status: True/False,
|
|||
|
|
msg: {
|
|||
|
|
"email": {"status": msg},
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
if type(channel_data) != dict:
|
|||
|
|
return public.returnMsg(False, public.lang("The parameter is wrong"))
|
|||
|
|
|
|||
|
|
from panelMessage import panelMessage
|
|||
|
|
pm = panelMessage()
|
|||
|
|
channel_res = {}
|
|||
|
|
res = {
|
|||
|
|
"status": False,
|
|||
|
|
"msg": channel_res
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for module, msg in channel_data.items():
|
|||
|
|
|
|||
|
|
modules = []
|
|||
|
|
if module.find(",") != -1:
|
|||
|
|
modules = module.split(",")
|
|||
|
|
else:
|
|||
|
|
modules.append(module)
|
|||
|
|
for m_module in modules:
|
|||
|
|
msg_obj = pm.init_msg_module(m_module)
|
|||
|
|
if not msg_obj: continue
|
|||
|
|
ret = msg_obj.push_data(msg)
|
|||
|
|
if ret and "status" in ret and ret['status']:
|
|||
|
|
res["status"] = True
|
|||
|
|
channel_res[m_module] = ret
|
|||
|
|
else:
|
|||
|
|
msg = "Message push failed."
|
|||
|
|
if "msg" in ret:
|
|||
|
|
msg = ret["msg"]
|
|||
|
|
channel_res[m_module] = public.returnMsg(False, msg)
|
|||
|
|
return res
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@格式为消息通道格式
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def format_msg_data(self):
|
|||
|
|
data = {
|
|||
|
|
'title': '',
|
|||
|
|
'to_email': '',
|
|||
|
|
'sms_type': '',
|
|||
|
|
'sms_argv': {},
|
|||
|
|
'msg': ''
|
|||
|
|
}
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
def __get_msg_list(self):
|
|||
|
|
"""
|
|||
|
|
获取消息通道列表
|
|||
|
|
"""
|
|||
|
|
data = []
|
|||
|
|
cpath = '{}/data/msg.json'.format(panelPath)
|
|||
|
|
if not os.path.exists(cpath):
|
|||
|
|
return data
|
|||
|
|
try:
|
|||
|
|
conf = public.readFile(cpath)
|
|||
|
|
data = json.loads(conf)
|
|||
|
|
except:
|
|||
|
|
try:
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
conf = public.readFile(cpath)
|
|||
|
|
data = json.loads(conf)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
def __get_args(self, data, key, val=''):
|
|||
|
|
"""
|
|||
|
|
@获取默认参数
|
|||
|
|
"""
|
|||
|
|
if not key in data: data[key] = val
|
|||
|
|
if type(data[key]) != type(val):
|
|||
|
|
data[key] = val
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
def __get_push_list(self, data):
|
|||
|
|
"""
|
|||
|
|
@格式化列表数据
|
|||
|
|
"""
|
|||
|
|
m_data = {}
|
|||
|
|
result = {}
|
|||
|
|
for x in self.__get_msg_list(): m_data[x['name']] = x
|
|||
|
|
|
|||
|
|
for skey in data:
|
|||
|
|
result[skey] = data[skey]
|
|||
|
|
|
|||
|
|
m_list = []
|
|||
|
|
for x in data[skey]['module'].split(','):
|
|||
|
|
if x in m_data: m_list.append(m_data[x]['title'])
|
|||
|
|
result[skey]['m_title'] = '、'.join(m_list)
|
|||
|
|
|
|||
|
|
m_cycle = []
|
|||
|
|
if data[skey]['cycle'] > 1:
|
|||
|
|
m_cycle.append('every {} seconds'.format(data[skey]['cycle']))
|
|||
|
|
m_cycle.append(
|
|||
|
|
'{} times, with an interval of {} seconds'.format(data[skey]['count'], data[skey]['interval']))
|
|||
|
|
result[skey]['m_cycle'] = ''.join(m_cycle)
|
|||
|
|
|
|||
|
|
# 兼容旧版本没有返回project项,导致前端无法编辑问题
|
|||
|
|
if "project" not in result[skey] and "type" in result[skey]:
|
|||
|
|
if result[skey]["type"] == "services":
|
|||
|
|
services = ['nginx', 'apache', "pure-ftpd", 'mysql', 'php-fpm', 'memcached', 'redis']
|
|||
|
|
_title = result[skey]['title']
|
|||
|
|
for s in services:
|
|||
|
|
if _title.find(s) != -1:
|
|||
|
|
result[skey]["project"] = s
|
|||
|
|
else:
|
|||
|
|
result[skey]["project"] = result[skey]["type"]
|
|||
|
|
if "project" in result[skey]:
|
|||
|
|
if result[skey]["project"] == "FTP server":
|
|||
|
|
result[skey]["project"] = "pure-ftpd"
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
# ************************************************推送
|
|||
|
|
"""
|
|||
|
|
@推送data/push目录的所有文件
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def push_messages_from_file(self):
|
|||
|
|
|
|||
|
|
path = "{}/data/push".format(panelPath)
|
|||
|
|
if not os.path.exists(path): os.makedirs(path)
|
|||
|
|
|
|||
|
|
from panelMessage import panelMessage
|
|||
|
|
pm = panelMessage()
|
|||
|
|
|
|||
|
|
for x in os.listdir(path):
|
|||
|
|
try:
|
|||
|
|
spath = '{}/{}'.format(path, x)
|
|||
|
|
# 判断文件是否为.json 结尾
|
|||
|
|
if not spath.endswith(".json"): continue
|
|||
|
|
if os.path.isdir(spath): continue
|
|||
|
|
data = json.loads(public.readFile(spath))
|
|||
|
|
|
|||
|
|
msg_obj = pm.init_msg_module(data['module'])
|
|||
|
|
if not msg_obj: continue
|
|||
|
|
|
|||
|
|
ret = msg_obj.push_data(data)
|
|||
|
|
if ret['status']: pass
|
|||
|
|
|
|||
|
|
os.remove(spath)
|
|||
|
|
except:
|
|||
|
|
print(public.get_error_info())
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
@消息推送线程
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def start(self):
|
|||
|
|
|
|||
|
|
total = 0
|
|||
|
|
interval = 5
|
|||
|
|
|
|||
|
|
tips = '{}/data/push/tips'.format(public.get_panel_path())
|
|||
|
|
if not os.path.exists(tips): os.makedirs(tips)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
if True:
|
|||
|
|
# 推送文件
|
|||
|
|
self.push_messages_from_file()
|
|||
|
|
|
|||
|
|
# 调用推送子模块
|
|||
|
|
data = {}
|
|||
|
|
is_write = False
|
|||
|
|
path = "{}/class/push/push.json".format(panelPath)
|
|||
|
|
|
|||
|
|
if os.path.exists(path):
|
|||
|
|
data = public.readFile(path)
|
|||
|
|
data = json.loads(data)
|
|||
|
|
|
|||
|
|
# p = public.get_modules('class/push')
|
|||
|
|
p = self.all_push_mode
|
|||
|
|
for skey in data:
|
|||
|
|
if skey in ("site_push", "system_push", "database_push", "rsync_push",
|
|||
|
|
"load_balance_push", "task_manager_push"):
|
|||
|
|
continue
|
|||
|
|
if len(data[skey]) <= 0: continue
|
|||
|
|
if skey in ['panelLogin_push', 'panel_login']: continue # 面板登录主动触发
|
|||
|
|
|
|||
|
|
total = None
|
|||
|
|
if skey not in p:
|
|||
|
|
continue
|
|||
|
|
obj = p[skey]
|
|||
|
|
|
|||
|
|
for x in data[skey]:
|
|||
|
|
if x in ['panelLogin_push', 'panel_login']: continue # 面板登录主动触发
|
|||
|
|
try:
|
|||
|
|
|
|||
|
|
item = data[skey][x]
|
|||
|
|
item['id'] = x
|
|||
|
|
if not item['status']: continue
|
|||
|
|
if not item['module']: continue
|
|||
|
|
if not 'index' in item: item['index'] = 0
|
|||
|
|
|
|||
|
|
if time.time() - item['index'] < item['interval']:
|
|||
|
|
print('{} Interval not reached, skip.'.format(item['title']))
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 验证推送次数
|
|||
|
|
push_record = {}
|
|||
|
|
tips_path = '{}/{}'.format(tips, x)
|
|||
|
|
if 'push_count' in item and item['push_count'] > 0:
|
|||
|
|
item['tips_list'] = []
|
|||
|
|
try:
|
|||
|
|
push_record = json.loads(public.readFile(tips_path))
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
for k in push_record:
|
|||
|
|
if push_record[k] < item['push_count']:
|
|||
|
|
continue
|
|||
|
|
item['tips_list'].append(k)
|
|||
|
|
|
|||
|
|
# 获取推送数据
|
|||
|
|
if not total: total = obj.get_total()
|
|||
|
|
rdata = obj.get_push_data(item, total)
|
|||
|
|
if not rdata:
|
|||
|
|
continue
|
|||
|
|
push_status = False
|
|||
|
|
for m_module in item['module'].split(','):
|
|||
|
|
if m_module == "":
|
|||
|
|
continue
|
|||
|
|
if not m_module in rdata:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
msg_obj = public.init_msg(m_module)
|
|||
|
|
if not msg_obj: continue
|
|||
|
|
|
|||
|
|
if 'to_user' in item and m_module in item['to_user']:
|
|||
|
|
rdata[m_module]['to_user'] = item['to_user'][m_module]
|
|||
|
|
|
|||
|
|
ret = msg_obj.push_data(rdata[m_module])
|
|||
|
|
data[skey][x]['index'] = rdata['index']
|
|||
|
|
is_write = True
|
|||
|
|
push_status = True
|
|||
|
|
|
|||
|
|
# 获取是否推送成功.
|
|||
|
|
if push_status:
|
|||
|
|
if 'push_keys' in rdata:
|
|||
|
|
for k in rdata['push_keys']:
|
|||
|
|
if k not in push_record:
|
|||
|
|
push_record[k] = 0
|
|||
|
|
push_record[k] += 1
|
|||
|
|
public.writeFile(tips_path, json.dumps(push_record))
|
|||
|
|
except:
|
|||
|
|
# print(public.get_error_info())
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
if is_write:
|
|||
|
|
public.writeFile(path, json.dumps(data))
|
|||
|
|
# time.sleep(interval)
|
|||
|
|
except:
|
|||
|
|
# print(public.get_error_info())
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def __get_login_panel_info(self):
|
|||
|
|
"""
|
|||
|
|
@name 获取面板登录列表
|
|||
|
|
@auther cjxin
|
|||
|
|
@date 2022-09-29
|
|||
|
|
"""
|
|||
|
|
import config
|
|||
|
|
c_obj = config.config()
|
|||
|
|
send_type = c_obj.get_login_send(None)['msg']
|
|||
|
|
if not send_type:
|
|||
|
|
return False
|
|||
|
|
return {"type": "panel_login", "module": send_type, "interval": 600, "status": True,
|
|||
|
|
"title": "Panel Login Alert", "cycle": 1, "count": 1, "key": "", "module_type": 'site_push'}
|
|||
|
|
|
|||
|
|
def __get_ssh_login_info(self):
|
|||
|
|
"""
|
|||
|
|
@name 获取SSH登录列表
|
|||
|
|
@auther cjxin
|
|||
|
|
@date 2022-09-29
|
|||
|
|
"""
|
|||
|
|
import ssh_security
|
|||
|
|
c_obj = ssh_security.ssh_security()
|
|||
|
|
send_type = c_obj.get_login_send(None)['msg']
|
|||
|
|
if not send_type or send_type in ['error']:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return {"type": "ssh_login", "module": send_type, "interval": 600, "status": True, "title": "SSH login warning",
|
|||
|
|
"cycle": 1, "count": 1, "key": "", "module_type": 'site_push'}
|
|||
|
|
|
|||
|
|
def get_push_list(self, get):
|
|||
|
|
"""
|
|||
|
|
@获取所有推送列表
|
|||
|
|
"""
|
|||
|
|
conf = self._get_conf()
|
|||
|
|
del_key_list = []
|
|||
|
|
|
|||
|
|
# 用于缓存同一module结果,从而避免多次调用get_push_user
|
|||
|
|
module_user_info_cache = {}
|
|||
|
|
|
|||
|
|
for key in conf.keys():
|
|||
|
|
# 先调用一遍各模块内部的get_view_msg, 获取额外信息
|
|||
|
|
push_module = self.all_push_mode.get(key, None)
|
|||
|
|
for x in list(conf[key].keys()):
|
|||
|
|
data = conf[key][x]
|
|||
|
|
data['module_type'] = key
|
|||
|
|
|
|||
|
|
# 检查可见性
|
|||
|
|
if hasattr(push_module, "can_view_task") and not push_module.can_view_task(data):
|
|||
|
|
del_key_list.append((key, x))
|
|||
|
|
|
|||
|
|
# 获取额外显示信息
|
|||
|
|
if hasattr(push_module, "get_view_msg"):
|
|||
|
|
data = push_module.get_view_msg(x, data)
|
|||
|
|
|
|||
|
|
# 获取 data['module'],可能是逗号分隔的多个模块
|
|||
|
|
modules = data.get('module', '')
|
|||
|
|
module_key = modules # 使用完整字符串作为缓存键
|
|||
|
|
|
|||
|
|
if module_key:
|
|||
|
|
if module_key not in module_user_info_cache:
|
|||
|
|
# 第一次遇到此 module_key,调用一次 get_push_user
|
|||
|
|
# print(f"模块 {module_key} 第一次调用 get_push_user")
|
|||
|
|
updated_data = self.get_push_user(data)
|
|||
|
|
module_user_info_cache[module_key] = updated_data.get('user_info', None)
|
|||
|
|
data = updated_data
|
|||
|
|
else:
|
|||
|
|
# 使用缓存的 user_info
|
|||
|
|
# print(f"模块 {module_key} 使用缓存的 user_info")
|
|||
|
|
data['user_info'] = module_user_info_cache[module_key]
|
|||
|
|
else:
|
|||
|
|
# 没有module信息则正常调用
|
|||
|
|
data = self.get_push_user(data)
|
|||
|
|
|
|||
|
|
conf[key][x] = data
|
|||
|
|
|
|||
|
|
# 删除不可见的key
|
|||
|
|
for key, idx in del_key_list:
|
|||
|
|
del conf[key][idx]
|
|||
|
|
|
|||
|
|
if 'site_push' not in conf:
|
|||
|
|
conf['site_push'] = {}
|
|||
|
|
|
|||
|
|
data = conf['site_push']
|
|||
|
|
for skey in ['panel_login']:
|
|||
|
|
info = None
|
|||
|
|
if skey in ['panel_login']:
|
|||
|
|
info = self.__get_login_panel_info()
|
|||
|
|
# elif skey in ['ssh_login']:
|
|||
|
|
# info = self.__get_ssh_login_info()
|
|||
|
|
|
|||
|
|
if info is not False:
|
|||
|
|
info = self.all_push_mode.get("site_push").get_view_msg(skey, info)
|
|||
|
|
modules = info.get('module', '')
|
|||
|
|
module_key = modules
|
|||
|
|
if module_key:
|
|||
|
|
if module_key not in module_user_info_cache:
|
|||
|
|
# print(f"模块 {module_key} 第一次调用 get_push_user (site_push)")
|
|||
|
|
updated_info = self.get_push_user(info)
|
|||
|
|
module_user_info_cache[module_key] = updated_info.get('user_info', None)
|
|||
|
|
info = updated_info
|
|||
|
|
else:
|
|||
|
|
# print(f"模块 {module_key} 使用缓存的 user_info (site_push)")
|
|||
|
|
info['user_info'] = module_user_info_cache[module_key]
|
|||
|
|
else:
|
|||
|
|
info = self.get_push_user(info)
|
|||
|
|
data[skey] = info
|
|||
|
|
else:
|
|||
|
|
if skey in data:
|
|||
|
|
del data[skey]
|
|||
|
|
conf['site_push'] = data
|
|||
|
|
return conf
|
|||
|
|
|
|||
|
|
def get_push_logs(self, get):
|
|||
|
|
"""
|
|||
|
|
@name 获取推送日志
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
p = 1
|
|||
|
|
limit = 15
|
|||
|
|
if 'p' in get: p = get.p
|
|||
|
|
if 'limit' in get: limit = get.limit
|
|||
|
|
|
|||
|
|
where = "type = 'Alarm notification'"
|
|||
|
|
sql = public.M('logs')
|
|||
|
|
|
|||
|
|
if hasattr(get, 'search'):
|
|||
|
|
where = " and logs like '%{search}%' ".format(search=get.search)
|
|||
|
|
|
|||
|
|
count = sql.where(where, ()).count()
|
|||
|
|
data = public.get_page(count, int(p), int(limit))
|
|||
|
|
data['data'] = public.M('logs').where(where, ()).limit('{},{}'.format(data['shift'], data['row'])).order(
|
|||
|
|
'id desc').select()
|
|||
|
|
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
# 兼容旧版本的告警
|
|||
|
|
def update_config(self, config):
|
|||
|
|
if "site_push" not in config:
|
|||
|
|
config["site_push"] = {}
|
|||
|
|
if "panel_push" in config:
|
|||
|
|
for k, v in config["panel_push"].items():
|
|||
|
|
if v["type"] != "endtime":
|
|||
|
|
config["site_push"][k] = v
|
|||
|
|
if "push_count" not in v:
|
|||
|
|
v["push_count"] = 1 if v["type"] == "ssl" else 0
|
|||
|
|
del config["panel_push"]
|
|||
|
|
public.writeFile(self.__conf_path, json.dumps(config))
|
|||
|
|
|
|||
|
|
# 获取模板
|
|||
|
|
def get_task_template(self, get):
|
|||
|
|
res = []
|
|||
|
|
title, tasks = self.all_push_mode["site_push"].get_task_template()
|
|||
|
|
res.append({
|
|||
|
|
"title": title,
|
|||
|
|
"template": tasks,
|
|||
|
|
})
|
|||
|
|
title, tasks = self.all_push_mode["database_push"].get_task_template()
|
|||
|
|
res.append({
|
|||
|
|
"title": title,
|
|||
|
|
"template": tasks,
|
|||
|
|
})
|
|||
|
|
title, tasks = self.all_push_mode["system_push"].get_task_template()
|
|||
|
|
res.append({
|
|||
|
|
"title": title,
|
|||
|
|
"template": tasks,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
for k, v in self.all_push_mode.items():
|
|||
|
|
if k in ["base_push", "site_push", "system_push"]:
|
|||
|
|
continue
|
|||
|
|
# 不是可以检测的插件就跳过
|
|||
|
|
if hasattr(v, "check_self_plugin"):
|
|||
|
|
if not v.check_self_plugin():
|
|||
|
|
continue
|
|||
|
|
else:
|
|||
|
|
continue
|
|||
|
|
if hasattr(v, "get_task_template"):
|
|||
|
|
title, tasks = v.get_task_template()
|
|||
|
|
if tasks is None:
|
|||
|
|
continue
|
|||
|
|
res.append({
|
|||
|
|
"title": title,
|
|||
|
|
"template": tasks,
|
|||
|
|
})
|
|||
|
|
return res
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
panelPush().start()
|