Initial YakPanel commit
This commit is contained in:
BIN
class/push/__pycache__/site_push.cpython-314.pyc
Normal file
BIN
class/push/__pycache__/site_push.cpython-314.pyc
Normal file
Binary file not shown.
59
class/push/base_push.py
Normal file
59
class/push/base_push.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | YakPanel
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 yakpanel(https://www.yakpanel.com) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi <baozi@yakpanel.com>
|
||||
# | Author: baozi
|
||||
# +-------------------------------------------------------------------
|
||||
import sys,os,re,json
|
||||
|
||||
import public,panelPush, time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
try:
|
||||
from YakPanel import cache
|
||||
except :
|
||||
from cachelib import SimpleCache
|
||||
cache = SimpleCache()
|
||||
|
||||
class base_push:
|
||||
|
||||
# 版本信息 目前无作用
|
||||
def get_version_info(self, get=None):
|
||||
raise NotImplementedError
|
||||
|
||||
# 格式化返回执行周期, 目前无作用
|
||||
def get_push_cycle(self, data: dict):
|
||||
return data
|
||||
|
||||
# 获取模块推送参数
|
||||
def get_module_config(self, get: public.dict_obj):
|
||||
raise NotImplementedError
|
||||
|
||||
# 获取模块配置项
|
||||
def get_push_config(self, get: public.dict_obj):
|
||||
# 其实就是配置信息,没有也会从全局配置文件push.json中读取
|
||||
raise NotImplementedError
|
||||
|
||||
# 写入推送配置文件
|
||||
def set_push_config(self, get: public.dict_obj):
|
||||
raise NotImplementedError
|
||||
|
||||
# 删除推送配置
|
||||
def del_push_config(self, get: public.dict_obj):
|
||||
# 从配置中删除信息,并做一些您想做的事,如记日志
|
||||
raise NotImplementedError
|
||||
|
||||
# 无意义???
|
||||
def get_total(self):
|
||||
return True
|
||||
|
||||
# 检查并获取推送消息,返回空时,不做推送, 传入的data是配置项
|
||||
def get_push_data(self, data, total):
|
||||
# data 内容
|
||||
# index : 时间戳 time.time()
|
||||
# 消息 以类型为key, 以内容为value, 内容中包含title 和msg
|
||||
# push_keys: 列表,发送了信息的推送任务的id,用来验证推送任务次数() 意义不大
|
||||
raise NotImplementedError
|
||||
402
class/push/panel_push.html
Normal file
402
class/push/panel_push.html
Normal file
@@ -0,0 +1,402 @@
|
||||
<style>
|
||||
.module-check i.form-checkbox{
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.module-check span{
|
||||
vertical-align: revert;
|
||||
}
|
||||
.module-check.check_disabled label,
|
||||
.module-check.check_disabled i{
|
||||
cursor: no-drop;
|
||||
}
|
||||
.panel_push_form .bt_select_updown .bt_select_list{
|
||||
max-height: 140px;
|
||||
}
|
||||
#bt_panel_push_table .divtable thead {
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
}
|
||||
#bt_panel_push_table .divtable {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
#bt_panel_push_table table {
|
||||
border: none;
|
||||
}
|
||||
.bt-form .bt-disabled{
|
||||
border: 1px solid #DDD;
|
||||
background-color: #F5F5F5;
|
||||
color: #ACA899;
|
||||
}
|
||||
</style>
|
||||
<div id="bt_panel_push_table"></div>
|
||||
|
||||
<!--面板消息推送模块-->
|
||||
<script type="text/javascript">
|
||||
|
||||
var panel_push = {
|
||||
all_info: {},
|
||||
module_name: 'panel_push',
|
||||
module_config: [],
|
||||
msg_list:[],
|
||||
push_type:'',
|
||||
init: function () {
|
||||
this.all_info = $('.alarm-view .bt-w-menu p.bgw').data('data'); //设置全局数据
|
||||
this.get_push_data();
|
||||
},
|
||||
/**
|
||||
*@description
|
||||
*/
|
||||
get_push_data: function () {
|
||||
var _this = this,data = this.all_info.data,rdata = [];
|
||||
$.each(data,function(index,item){
|
||||
item['id'] = index;
|
||||
item['name'] = _this.module_name;
|
||||
rdata.push(item)
|
||||
})
|
||||
// console.log(rdata,'rdata')
|
||||
bt_tools.table({
|
||||
el: '#bt_panel_push_table',
|
||||
data: rdata,
|
||||
default: "列表为空",
|
||||
autoHeight: true,
|
||||
height:410,
|
||||
column: [
|
||||
{ fid: 'title', title: '标题' },
|
||||
{ fid: 'm_title', title: '推送通道' },
|
||||
{ fid: 'm_cycle', title: '执行周期' },
|
||||
{
|
||||
fid: 'status',
|
||||
title: '状态',
|
||||
config: {
|
||||
icon: true,
|
||||
list: [
|
||||
[true, '正常', 'bt_success', 'glyphicon-play'],
|
||||
[false, '停用', 'bt_danger', 'glyphicon-pause']
|
||||
]
|
||||
},
|
||||
type: 'status',
|
||||
event: function(row){
|
||||
bt_tools.send({
|
||||
url:'/push?action=set_push_status',
|
||||
data: {id:row.id,name:_this.module_name,status:row['status']?0:1}
|
||||
},function(res){
|
||||
if(res.status){
|
||||
panel_push.refresh_data();
|
||||
}
|
||||
bt_tools.msg(res)
|
||||
},'设置推送状态')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作', type: 'group', width: 100, align: 'right', group: [{
|
||||
title: '编辑', event: function(row){
|
||||
var _typeA = []
|
||||
_this.get_config_data(function(){
|
||||
$.each(_this.module_config,function(index,item){
|
||||
_typeA.push(item.project)
|
||||
})
|
||||
_this.panel_push_view($.extend(true,$.inArray(row.project,_typeA) < 0 ? {isOther:true} : {},row));
|
||||
})
|
||||
}
|
||||
},{
|
||||
title: '删除', event: function (row) {
|
||||
bt.confirm({title:'删除推送通道',msg:'删除【'+row.title+'】推送,是否继续?'},function(){
|
||||
_this.del_push_config(row.id, function (res) {
|
||||
if (res.status) panel_push.refresh_data();
|
||||
bt_tools.msg(res)
|
||||
});
|
||||
});
|
||||
}
|
||||
}]
|
||||
}
|
||||
],
|
||||
tootls:[{
|
||||
type:'group',
|
||||
positon: ['left', 'top'],
|
||||
list: [{
|
||||
title: '添加推送',
|
||||
active: true,
|
||||
event: function () {
|
||||
_this.get_config_data(function(){
|
||||
_this.panel_push_view();
|
||||
})
|
||||
}
|
||||
}]
|
||||
}]
|
||||
});
|
||||
},
|
||||
/**
|
||||
*面板推送视图
|
||||
*/
|
||||
panel_push_view:function(res){
|
||||
var _this = this,config = {title:'添加推送',btn:'添加'},proList = [],editIndex = 0,param = {} ;
|
||||
if(res){
|
||||
param = $.extend(true,{},res)
|
||||
config = {title:'编辑推送',btn:'保存'}
|
||||
}
|
||||
$.each(this.module_config,function(index,item){
|
||||
if(param && param.project === item.project) editIndex = index;
|
||||
proList.push({title:item.title,value:item.project})
|
||||
})
|
||||
bt_tools.open({
|
||||
type: 1,
|
||||
title: config.title,
|
||||
area:'520px',
|
||||
btn:[config.btn,'取消'],
|
||||
skin:'panel_push_form',
|
||||
content:{
|
||||
class:'pd15',
|
||||
data:param || {},
|
||||
form:[{
|
||||
label:'通知类型',
|
||||
group:{
|
||||
type: 'select',
|
||||
name: 'project',
|
||||
width: '300px',
|
||||
value:'ssl',
|
||||
class:'projectBox',
|
||||
list: proList,
|
||||
disabled:!!res,
|
||||
change:function(formData,element,that){
|
||||
var config = panel_push.switch_push_type(that,res?res:formData)
|
||||
that.$again_render_form(config)
|
||||
}
|
||||
}
|
||||
},{
|
||||
label:'服务名称',
|
||||
display: false,
|
||||
group:{
|
||||
type: 'text',
|
||||
name: 'proName',
|
||||
width: '300px',
|
||||
placeholder:'请输入完整的服务名称',
|
||||
value:''
|
||||
}
|
||||
},{
|
||||
label:'服务状态',
|
||||
display:false,
|
||||
group:{
|
||||
type: 'select',
|
||||
name: 'key',
|
||||
width: '300px',
|
||||
placeholder:'无服务状态',
|
||||
value:'stop',
|
||||
list:[]
|
||||
}
|
||||
},{
|
||||
label:'剩余天数',
|
||||
group:[{
|
||||
type: 'number',
|
||||
name: 'cycle',
|
||||
width: '100px',
|
||||
unit: '天',
|
||||
value:1,
|
||||
}]
|
||||
},{
|
||||
label:'消息通道',
|
||||
style:{'margin-top': '7px'},
|
||||
group:[] //动态获取
|
||||
},{
|
||||
label:'推送间隔',
|
||||
display: false,
|
||||
group:{
|
||||
type: 'number',
|
||||
name:'interval',
|
||||
width: '100px',
|
||||
class: 'group',
|
||||
unit: '秒',
|
||||
value:600
|
||||
}
|
||||
},{
|
||||
group:{
|
||||
type: 'help',
|
||||
style: { 'margin-top': '0' },
|
||||
class: 'none-list-style',
|
||||
list: []
|
||||
}
|
||||
}]
|
||||
},
|
||||
success:function(layers,indexs,forms){
|
||||
forms.element.find('[data-name="project"] ul li:eq('+editIndex+')').click()
|
||||
},
|
||||
yes:function(formData,index){
|
||||
|
||||
var _configD = {},eData = $.extend(true,{status:true,type:_this.push_type},formData);
|
||||
// 默认添加的参数
|
||||
_configD['name'] = _this.module_name;
|
||||
_configD['id'] = res ? param['id'] : new Date().getTime();
|
||||
eData['title'] = $('.projectBox .bt_select_content').html();
|
||||
|
||||
//删除推送类型
|
||||
$.each(_this.msg_list,function(index,item){
|
||||
delete eData[item.name]
|
||||
})
|
||||
|
||||
//拼接选中推送方式
|
||||
var isCheck = []
|
||||
$('.module-check').not('.check_disabled').each(function(){
|
||||
if($(this).find('input').prop('checked')){
|
||||
isCheck.push($(this).find('input').prop('name'))
|
||||
}
|
||||
})
|
||||
eData['module'] = isCheck.join();
|
||||
eData['cycle'] = eData['cycle']?parseInt(eData['cycle']):1;
|
||||
eData['interval'] = eData['interval']?parseInt(eData['interval']):600;
|
||||
if(!eData['module']){
|
||||
layer.msg('请选择一种消息通道',{icon:2});
|
||||
return false
|
||||
}
|
||||
// 自定义类型时
|
||||
if(eData.project === 'other'){
|
||||
eData.project = eData.proName
|
||||
eData['title'] = eData.proName+'服务停止'
|
||||
delete eData.proName; //删除临时服务名称
|
||||
}
|
||||
_configD['data'] = JSON.stringify(eData)
|
||||
//提交
|
||||
bt_tools.send({
|
||||
url:'/push?action=set_push_config',
|
||||
data:_configD
|
||||
},function(Sres){
|
||||
if(Sres.status){
|
||||
layer.close(index);
|
||||
panel_push.refresh_data();
|
||||
}
|
||||
bt_tools.msg(Sres)
|
||||
},(res ?'保存':'添加')+'推送设置')
|
||||
}
|
||||
})
|
||||
},
|
||||
// 推送类型判断
|
||||
switch_push_type: function(config,formData){
|
||||
var that = this,_config = config.config.form,_checklist = [],isInvalid = false,isCheckType = [];
|
||||
if(!formData['project']) formData['project'] = 'ssl'
|
||||
formData['isOther'] ? formData['project'] = 'other':''
|
||||
$.each(this.module_config,function(index,item){
|
||||
if(formData.project === item.project){
|
||||
if(formData['module']){
|
||||
isCheckType = formData['module'].split(',')
|
||||
}
|
||||
$.each(item.push,function(indexP,ic){
|
||||
if(typeof that.msg_list[ic] != 'undefined') {
|
||||
_checklist.push({
|
||||
type: 'checkbox',
|
||||
name:that.msg_list[ic].name,
|
||||
class:'module-check '+(!that.msg_list[ic].setup?'check_disabled':'')+'',
|
||||
style:{'margin-right': '10px'},
|
||||
disabled:!that.msg_list[ic].setup,
|
||||
value:$.inArray(that.msg_list[ic].name,isCheckType) >= 0 ?1:0,
|
||||
title:that.msg_list[ic].title+(!that.msg_list[ic].setup?'<span style="color:red;cursor: pointer;" onclick="panel_push.alarm_jump(\''+that.msg_list[ic].title+'\')">[点击安装]</span>':''),
|
||||
event:function(formData,element,thatE){
|
||||
thatE.config.form[4].group[indexP].value = !formData[that.msg_list[ic].name]?0:1;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
_config[0].group.value = formData.project;
|
||||
_config[1].display = false;
|
||||
_config[4].group = _checklist;
|
||||
_config[6].group.list = item.helps; //动态帮助提醒
|
||||
formData['type'] = item.type;
|
||||
if(formData.project === 'other'){
|
||||
_config[1].display = true
|
||||
_config[1].group.value = formData['title']?formData['title'].slice(0, -4):''
|
||||
}
|
||||
that.push_type = item.type; //保存当前类型
|
||||
switch(item.type){
|
||||
case 'services':
|
||||
var keyList = []
|
||||
$.each(item.keys,function(indexK,ik){
|
||||
keyList.push({title:ik.val,value:ik.key})
|
||||
})
|
||||
_config[2].display = true;
|
||||
_config[2].group.list = keyList;
|
||||
_config[3].display = false;
|
||||
_config[5].display = true;
|
||||
_config[5].group.value = item.interval
|
||||
break;
|
||||
default:
|
||||
_config[2].display = false;
|
||||
_config[3].display = true;
|
||||
_config[3].group[0].value = parseInt(item.cycle);
|
||||
_config[5].display = false;
|
||||
}
|
||||
isInvalid = true;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
if(isInvalid) return _config
|
||||
},
|
||||
//安装跳转提示
|
||||
alarm_jump:function(name){
|
||||
layer.confirm("当前模块<span style='color:red'>未安装</span>,是否跳转到安装界面,是否继续?",{title:"跳转提醒",icon:3,closeBtn:2}, function() {
|
||||
layer.closeAll();
|
||||
setTimeout(function(){
|
||||
open_three_channel_auth(name)
|
||||
},200)
|
||||
})
|
||||
},
|
||||
get_config_data:function(callback){
|
||||
var _this = this;
|
||||
if(_this.module_config.length === 0 || _this.msg_list.length === 0){
|
||||
_this.get_module_config(function(res){
|
||||
_this.module_config = res
|
||||
_this.get_push_msg_list(function(list){
|
||||
_this.msg_list = list
|
||||
if(callback) callback(true)
|
||||
})
|
||||
})
|
||||
}else{
|
||||
if(callback) callback(true)
|
||||
}
|
||||
},
|
||||
/**
|
||||
*@description 删除配置
|
||||
*/
|
||||
del_push_config: function (id,callback) {
|
||||
var that = this
|
||||
$.post('/push?action=del_push_config', { name: that.module_name,id:id }, function (rdata) {
|
||||
if(callback) callback(rdata)
|
||||
})
|
||||
},
|
||||
/**
|
||||
*@description 刷新配置
|
||||
*/
|
||||
refresh_data: function () {
|
||||
var that = this
|
||||
$.post('/push?action=get_modules_list', function (rdata) {
|
||||
$('#bt_panel_push_table').empty()
|
||||
that.all_info = rdata[that.module_name];
|
||||
$('.alarm-view .bt-w-menu p.bgw').data('data',rdata[that.module_name]);
|
||||
that.get_push_data()
|
||||
})
|
||||
},
|
||||
/**
|
||||
*@description 获取模块执行日志
|
||||
*/
|
||||
get_module_logs: function () {
|
||||
var that = this
|
||||
$.post('/push?action=get_module_logs', { name: that.module_name }, function (rdata) {
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
/**
|
||||
*@description 获取配置项
|
||||
*/
|
||||
get_module_config: function (callback) {
|
||||
var that = this
|
||||
$.post('/push?action=get_module_config', { name: that.module_name }, function (rdata) {
|
||||
if(callback) callback(rdata)
|
||||
})
|
||||
},
|
||||
/**
|
||||
*@description 获取消息通道安装情况
|
||||
*/
|
||||
get_push_msg_list: function(callback) {
|
||||
$.post('/push?action=get_push_msg_list', function (rdata) {
|
||||
if(callback) callback(rdata)
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
358
class/push/panel_push.py
Normal file
358
class/push/panel_push.py
Normal file
@@ -0,0 +1,358 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | YakPanel
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 YakPanel(https://www.yakpanel.com) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: 沐落 <cjx@yakpanel.com>
|
||||
# | Author: lx
|
||||
# +-------------------------------------------------------------------
|
||||
import sys,os,time,psutil,re
|
||||
panelPath = "/www/server/panel"
|
||||
os.chdir(panelPath)
|
||||
sys.path.insert(0, "class/")
|
||||
import public,time,panelPush,public
|
||||
|
||||
try:
|
||||
from YakPanel import cache
|
||||
except :
|
||||
from cachelib import SimpleCache
|
||||
cache = SimpleCache()
|
||||
|
||||
class panel_push:
|
||||
|
||||
__push = None
|
||||
pids = None
|
||||
def __init__(self):
|
||||
self.__push = panelPush.panelPush()
|
||||
|
||||
#-----------------------------------------------------------start 添加推送 ------------------------------------------------------
|
||||
def get_version_info(self,get):
|
||||
"""
|
||||
获取版本信息
|
||||
"""
|
||||
data = {}
|
||||
data['ps'] = ''
|
||||
data['version'] = '1.2'
|
||||
data['date'] = '2022-09-20'
|
||||
data['author'] = 'YakPanel'
|
||||
data['help'] = 'https://www.yakpanel.com/forum'
|
||||
return data
|
||||
|
||||
#名取PID
|
||||
def getPid(self,pname):
|
||||
try:
|
||||
if not self.pids: self.pids = psutil.pids()
|
||||
for pid in self.pids:
|
||||
if psutil.Process(pid).name() == pname: return True
|
||||
return False
|
||||
except: return True
|
||||
|
||||
#检测指定进程是否存活
|
||||
def checkProcess(self,pid):
|
||||
try:
|
||||
if not self.pids: self.pids = psutil.pids()
|
||||
if int(pid) in self.pids: return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
#检查是否启动
|
||||
def check_run(self,name):
|
||||
if name == "php-fpm":
|
||||
status = False
|
||||
base_path = "/www/server/php"
|
||||
if not os.path.exists(base_path):
|
||||
return status
|
||||
for p in os.listdir(base_path):
|
||||
pid_file = os.path.join(base_path, p, "var/run/php-fpm.pid")
|
||||
if os.path.exists(pid_file):
|
||||
php_pid = int(public.readFile(pid_file))
|
||||
status = self.checkProcess(php_pid)
|
||||
if status:
|
||||
return status
|
||||
return status
|
||||
elif name == 'nginx':
|
||||
status = False
|
||||
if os.path.exists('/etc/init.d/nginx'):
|
||||
pidf = '/www/server/nginx/logs/nginx.pid'
|
||||
if os.path.exists(pidf):
|
||||
try:
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
except:
|
||||
pass
|
||||
return status
|
||||
elif name == 'apache':
|
||||
status = False
|
||||
if os.path.exists('/etc/init.d/httpd'):
|
||||
pidf = '/www/server/apache/logs/httpd.pid'
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
elif name == 'mysql':
|
||||
res = public.ExecShell("service mysqld status")
|
||||
if res and not re.search(r"not\s+running", res[0]):
|
||||
return True
|
||||
return False
|
||||
elif name == 'tomcat':
|
||||
status = False
|
||||
if os.path.exists('/www/server/tomcat/logs/catalina-daemon.pid'):
|
||||
if self.getPid('jsvc'): status = True
|
||||
if not status:
|
||||
if self.getPid('java'): status = True
|
||||
return status
|
||||
elif name == 'pure-ftpd':
|
||||
pidf = '/var/run/pure-ftpd.pid'
|
||||
status = False
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
elif name == 'redis':
|
||||
status = False
|
||||
pidf = '/www/server/redis/redis.pid'
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
elif name == 'memcached':
|
||||
status = False
|
||||
pidf = '/var/run/memcached.pid'
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
return True
|
||||
|
||||
def get_server_status(self, server_name):
|
||||
status = self.check_run(server_name)
|
||||
if status:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
"""
|
||||
@获取推送模块配置
|
||||
"""
|
||||
def get_module_config(self,get):
|
||||
|
||||
data = []
|
||||
|
||||
item = self.__push.format_push_data(push = ["mail",'dingding','weixin',"feishu"],project = 'ssl',type = 'ssl')
|
||||
item['cycle'] = 30
|
||||
item['title'] = 'SSL到期提醒'
|
||||
item['helps'] = ['SSL到期提醒一天只发送一次','证书夹内【剩余天数不足30天】的所有证书.']
|
||||
data.append(item)
|
||||
|
||||
item = self.__push.format_push_data(push = ["mail",'dingding','weixin',"feishu"],project = 'endtime',type = 'endtime')
|
||||
item['cycle'] = 5
|
||||
item['title'] = '专业版/企业版到期提醒'
|
||||
item['helps'] = ['']
|
||||
data.append(item)
|
||||
|
||||
services = ['nginx','apache',"pure-ftpd",'mysql','php-fpm','memcached','redis']
|
||||
channels = ['mail', 'dingding','weixin','sms',"feishu"]
|
||||
for x in services:
|
||||
item = {}
|
||||
item['project'] = x
|
||||
item['title'] = '{}服务停止通知'.format(x)
|
||||
|
||||
if x == 'other':
|
||||
item['title'] = '自定义服务停止通知'
|
||||
else:
|
||||
ser_name = self.__get_service_name(x)
|
||||
if self.get_server_status(ser_name) == -1: continue
|
||||
|
||||
item['type'] = 'services'
|
||||
item['keys'] = []
|
||||
item['interval'] = 300
|
||||
for x in ['stop']:
|
||||
item['keys'].append({'key':x,'val':'停止'})
|
||||
|
||||
item['push'] = channels
|
||||
item['helps'] = ['部分服务停止可能会造成业务中断.','短信推送需提前购买,如需帮助请联系 [<a href="https://www.yakpanel.com/forum" target="_blank" class="btlink">YakPanel 社区</a>]']
|
||||
data.append(item)
|
||||
import json
|
||||
public.writeFile('data/push/config/panel_push.json',json.dumps(data))
|
||||
return data
|
||||
|
||||
def get_push_cycle(self,data):
|
||||
"""
|
||||
@获取执行周期
|
||||
"""
|
||||
result = {}
|
||||
for skey in data:
|
||||
result[skey] = data[skey]
|
||||
|
||||
m_cycle =[]
|
||||
m_type = data[skey]['type']
|
||||
if m_type in ['endtime','ssl']:
|
||||
m_cycle.append('剩余{}天时,每天1次'.format(data[skey]['cycle']))
|
||||
elif m_type in ['services']:
|
||||
m_cycle.append('服务停止时,每{}秒1次'.format(data[skey]['interval']))
|
||||
if len(m_cycle) > 0:
|
||||
result[skey]['m_cycle'] = ''.join(m_cycle)
|
||||
return result
|
||||
|
||||
#-----------------------------------------------------------end 添加推送 ------------------------------------------------------
|
||||
"""
|
||||
@获取服务真实名称
|
||||
"""
|
||||
def __get_service_name(self,name):
|
||||
slist = {"FTP服务端":'pure-ftpd'}
|
||||
if name in slist: name = slist[name]
|
||||
|
||||
return name
|
||||
"""
|
||||
@获取推送栏目
|
||||
"""
|
||||
def get_total(self):
|
||||
return True;
|
||||
|
||||
def get_push_data(self,data,total):
|
||||
if data['type'] == 'services':
|
||||
ser_name = data['project']
|
||||
ser_name = self.__get_service_name(ser_name)
|
||||
|
||||
status = self.get_server_status(ser_name)
|
||||
if status > 0:
|
||||
return public.returnMsg(False, public.lang("状态正常,跳过."))
|
||||
else:
|
||||
if status == 0:
|
||||
return self.__get_service_result(data)
|
||||
return public.returnMsg(False, public.lang("服务未安装,跳过."))
|
||||
|
||||
elif data['type'] in ['ssl']:
|
||||
|
||||
if time.time() < data['index'] + 86400:
|
||||
return public.returnMsg(False, public.lang("一天推送一次,跳过."))
|
||||
|
||||
import panelSSL
|
||||
ssl = panelSSL.panelSSL()
|
||||
clist = []
|
||||
for x in ssl.GetCertList(None):
|
||||
timeArray = time.strptime(x['notAfter'], "%Y-%m-%d")
|
||||
endtime = time.mktime(timeArray)
|
||||
day = int((endtime - time.time()) / 86400)
|
||||
if day > data['cycle']: continue
|
||||
clist.append(x)
|
||||
|
||||
return self.__get_ssl_result(data,clist)
|
||||
|
||||
elif data['type'] in ['endtime']:
|
||||
if time.time() < data['index'] + 86400:
|
||||
return public.returnMsg(False, public.lang("一天推送一次,跳过."))
|
||||
|
||||
from pluginAuth import Plugin
|
||||
softs = Plugin(False).get_plugin_list(True)
|
||||
if softs['pro'] == 0: return public.returnMsg(False, public.lang("永久专业版,跳过."))
|
||||
|
||||
if softs['ltd'] == -2 and softs['pro'] == -2:
|
||||
pass
|
||||
else:
|
||||
pro_data = {}
|
||||
if softs['ltd'] > 0:
|
||||
pro_data['endtime'] = softs['ltd']
|
||||
pro_data['name'] = "Linux企业版"
|
||||
pro_data['affect'] = '全部企业版插件'
|
||||
elif softs['pro'] > 0:
|
||||
pro_data['endtime'] = softs['pro']
|
||||
pro_data['name'] = "Linux专业版"
|
||||
pro_data['affect'] = '全部专业版插件'
|
||||
|
||||
pro_data['day'] = int((pro_data['endtime'] - time.time()) / 86400)
|
||||
|
||||
if pro_data['day'] <= data['cycle']:
|
||||
return self.__get_ltd_result(data,pro_data)
|
||||
|
||||
return public.returnMsg(False, public.lang("未达到阈值,跳过."))
|
||||
|
||||
"""
|
||||
@企业版到期提醒
|
||||
"""
|
||||
def __get_ltd_result(self,data,pro_data):
|
||||
result = {'index':time.time()}
|
||||
|
||||
for m_module in data['module'].split(','):
|
||||
result[m_module] = self.__push.format_msg_data()
|
||||
newline = ""
|
||||
if m_module in ['dingding','weixin',"feishu","mail"]:
|
||||
if m_module in ["mail"]:
|
||||
newline="<br/>"
|
||||
result[m_module]["title"] = "YakPanel 业务到期提醒"
|
||||
else:
|
||||
newline = "\n\n"
|
||||
result[m_module]['msg'] = "".join((
|
||||
"#### YakPanel 业务到期提醒"+newline,
|
||||
">服务器 :"+ public.GetLocalIp() + newline,
|
||||
">剩余天数:"+ str(pro_data['day'] + 1) +" 天"+newline,
|
||||
">到期产品:"+ pro_data['name'] +newline,
|
||||
">到期时间:"+ public.format_date(times =pro_data['endtime']) +newline,
|
||||
">影响业务:"+ pro_data['affect'] +newline,
|
||||
">通知时间:" + public.format_date() + newline))
|
||||
return result
|
||||
|
||||
"""
|
||||
@ssl到期返回
|
||||
"""
|
||||
def __get_ssl_result(self,data,clist):
|
||||
if len(clist) == 0:
|
||||
return public.returnMsg(False, public.lang("未找到到期证书,跳过."))
|
||||
|
||||
result = {'index':time.time() }
|
||||
for m_module in data['module'].split(','):
|
||||
|
||||
result[m_module] = self.__push.format_msg_data()
|
||||
newline = ""
|
||||
if m_module in ['dingding','weixin',"feishu","mail"]:
|
||||
if m_module in ["mail"]:
|
||||
newline="<br/>"
|
||||
result[m_module]["title"] = "YakPanel SSL到期提醒"
|
||||
else:
|
||||
newline = "\n\n"
|
||||
|
||||
p_msg = "";
|
||||
for x in clist: p_msg+= " 到期:{} 域名:{}".format(x['notAfter'],x['subject']) + newline
|
||||
|
||||
result[m_module]['msg'] ="".join((
|
||||
"#### YakPanel SSL到期提醒" + newline,
|
||||
">服务器 :"+ public.GetLocalIp() +newline,
|
||||
">检测时间:" + public.format_date() +newline,
|
||||
">About to expire: "+ str(len(clist)) +" "+newline,
|
||||
p_msg))
|
||||
return result
|
||||
|
||||
"""
|
||||
@服务停止返回
|
||||
"""
|
||||
def __get_service_result(self,data):
|
||||
|
||||
s_idx = int(time.time())
|
||||
if s_idx < data['index'] + data['interval']:
|
||||
return public.returnMsg(False, public.lang("The interval is not reached, skip."))
|
||||
|
||||
result = {'index':s_idx}
|
||||
|
||||
for m_module in data['module'].split(','):
|
||||
result[m_module] = self.__push.format_msg_data()
|
||||
newline = ""
|
||||
if m_module in ['dingding','weixin',"feishu","mail"]:
|
||||
if m_module in ["mail"]:
|
||||
newline="<br/>"
|
||||
result[m_module]["title"] = "堡塔服务停止告警"
|
||||
else:
|
||||
newline = "\n\n"
|
||||
result[m_module]['msg'] = "".join((
|
||||
"#### 堡塔服务停止告警" + newline,
|
||||
">服务器 :"+ public.GetLocalIp() +newline + " ",
|
||||
">Type of service: "+ data["project"] +newline + " ",
|
||||
">Service state: 已停止"+newline+" ",
|
||||
">检测时间:"+ public.format_date()))
|
||||
elif m_module in ['sms']:
|
||||
result[m_module]['sm_type'] = 'servcies'
|
||||
result[m_module]['sm_args'] = { 'name':'{}'.format(public.GetConfigValue('title')), 'product':data["project"],'product1':data["project"]}
|
||||
return result
|
||||
|
||||
|
||||
|
||||
11
class/push/push_list.json
Normal file
11
class/push/push_list.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"name": "panel_push",
|
||||
"title": "面板基础提醒",
|
||||
"version": "1.2",
|
||||
"date": "2021-07-05",
|
||||
"help": "",
|
||||
"ps": "用于推送面板相关信息",
|
||||
"update_msg": "1、新增服务停止推送<br>2、新增证书到期推送<br>3、新增专业版、企业版到期推送"
|
||||
}
|
||||
]
|
||||
11
class/push/scripts/panel_login.json
Normal file
11
class/push/scripts/panel_login.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"interval": 600,
|
||||
"type": "panel_login",
|
||||
"title": "Panel login alarm",
|
||||
"project": "",
|
||||
"cycle": 1,
|
||||
"count":1,
|
||||
"push_count": 1,
|
||||
"status": true,
|
||||
"module_type":"site_push"
|
||||
}
|
||||
9
class/push/scripts/panel_pwd_endtime.json
Normal file
9
class/push/scripts/panel_pwd_endtime.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"interval": 600,
|
||||
"type": "panel_pwd_endtime",
|
||||
"title": "Panel password Expiration Date",
|
||||
"cycle": 15,
|
||||
"push_count": 1,
|
||||
"module_type":"site_push",
|
||||
"status": true
|
||||
}
|
||||
10
class/push/scripts/site_endtime.json
Normal file
10
class/push/scripts/site_endtime.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"interval": 600,
|
||||
"type": "site_endtime",
|
||||
"title": "Expiration of website",
|
||||
"cycle": 7,
|
||||
"push_count": 1,
|
||||
"module": "",
|
||||
"module_type":"site_push",
|
||||
"status": true
|
||||
}
|
||||
10
class/push/scripts/ssl.json
Normal file
10
class/push/scripts/ssl.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"interval": 600,
|
||||
"type": "ssl",
|
||||
"title": "Site Certificate (SSL) expiration",
|
||||
"project": "all_site",
|
||||
"cycle": 15,
|
||||
"push_count": 1,
|
||||
"module_type":"site_push",
|
||||
"status": true
|
||||
}
|
||||
845
class/push/site_push.py
Normal file
845
class/push/site_push.py
Normal file
@@ -0,0 +1,845 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | yakpanel Windows面板
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 yakpanel(https://www.yakpanel.com) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: 沐落 <cjx@yakpanel.com>
|
||||
# +-------------------------------------------------------------------
|
||||
import sys, os, time, json, re, psutil
|
||||
|
||||
panelPath = "/www/server/panel"
|
||||
os.chdir(panelPath)
|
||||
sys.path.append("class/")
|
||||
import public,db,time,html,panelPush
|
||||
import config
|
||||
|
||||
try:
|
||||
from YakPanel import cache
|
||||
except :
|
||||
from cachelib import SimpleCache
|
||||
cache = SimpleCache()
|
||||
|
||||
class site_push:
|
||||
|
||||
__push = None
|
||||
__push_model = ['dingding','weixin','mail','sms','wx_account','feishu']
|
||||
__conf_path = "{}/class/push/push.json".format(panelPath)
|
||||
pids = None
|
||||
|
||||
def __init__(self):
|
||||
self.__push = panelPush.panelPush()
|
||||
|
||||
#-----------------------------------------------------------start 添加推送 ------------------------------------------------------
|
||||
def get_version_info(self,get):
|
||||
"""
|
||||
获取版本信息
|
||||
"""
|
||||
data = {}
|
||||
data['ps'] = ''
|
||||
data['version'] = '1.0'
|
||||
data['date'] = '2020-08-10'
|
||||
data['author'] = 'YakPanel'
|
||||
data['help'] = 'http://www.yakpanel.com'
|
||||
return data
|
||||
|
||||
"""
|
||||
@获取推送模块配置
|
||||
"""
|
||||
def get_module_config(self,get):
|
||||
|
||||
stype = None
|
||||
if 'type' in get:
|
||||
stype = get.type
|
||||
|
||||
data = []
|
||||
#证书到期提醒
|
||||
item = self.__push.format_push_data()
|
||||
item['cycle'] = 30
|
||||
item['type'] = 'ssl'
|
||||
item['push'] = self.__push_model
|
||||
item['title'] = 'Website SSL Expiration Reminder'
|
||||
item['helps'] = ['SSL expiration reminders are sent only once a day']
|
||||
data.append(item)
|
||||
|
||||
#网站到期提醒
|
||||
item = self.__push.format_push_data(push = ['dingding','weixin','mail'])
|
||||
item['cycle'] = 15
|
||||
item['type'] = 'site_endtime'
|
||||
item['title'] = 'Website Expiration Reminder'
|
||||
item['helps'] = ['Site expiration reminders are sent only once a day']
|
||||
data.append(item)
|
||||
|
||||
for data_item in data:
|
||||
if stype == data_item['type']:
|
||||
return data_item
|
||||
return data
|
||||
|
||||
|
||||
def get_push_cycle(self,data):
|
||||
"""
|
||||
@获取执行周期
|
||||
"""
|
||||
result = {}
|
||||
for skey in data:
|
||||
result[skey] = data[skey]
|
||||
|
||||
m_cycle =[]
|
||||
m_type = data[skey]['type']
|
||||
if m_type in ['endtime','ssl','site_endtime']:
|
||||
m_cycle.append('1 time per day when {} days remain'.format(data[skey]['cycle']))
|
||||
|
||||
if len(m_cycle) > 0:
|
||||
result[skey]['m_cycle'] = ''.join(m_cycle)
|
||||
return result
|
||||
|
||||
def get_server_status(self, server_name):
|
||||
status = self.check_run(server_name)
|
||||
if status:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# 检测指定进程是否存活
|
||||
|
||||
def checkProcess(self, pid):
|
||||
try:
|
||||
if not self.pids: self.pids = psutil.pids()
|
||||
if int(pid) in self.pids: return True
|
||||
return False
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
# 名取PID
|
||||
|
||||
def getPid(self, pname):
|
||||
try:
|
||||
if not self.pids: self.pids = psutil.pids()
|
||||
for pid in self.pids:
|
||||
if psutil.Process(pid).name() == pname: return True
|
||||
return False
|
||||
except:
|
||||
return True
|
||||
|
||||
# 检查是否启动
|
||||
|
||||
def check_run(self, name):
|
||||
if name == "php-fpm":
|
||||
status = False
|
||||
base_path = "/www/server/php"
|
||||
if not os.path.exists(base_path):
|
||||
return status
|
||||
for p in os.listdir(base_path):
|
||||
pid_file = os.path.join(base_path, p, "var/run/php-fpm.pid")
|
||||
if os.path.exists(pid_file):
|
||||
php_pid = int(public.readFile(pid_file))
|
||||
status = self.checkProcess(php_pid)
|
||||
if status:
|
||||
return status
|
||||
return status
|
||||
elif name == 'nginx':
|
||||
status = False
|
||||
if os.path.exists('/etc/init.d/nginx'):
|
||||
pidf = '/www/server/nginx/logs/nginx.pid'
|
||||
if os.path.exists(pidf):
|
||||
try:
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
except:
|
||||
pass
|
||||
return status
|
||||
elif name == 'apache':
|
||||
status = False
|
||||
if os.path.exists('/etc/init.d/httpd'):
|
||||
pidf = '/www/server/apache/logs/httpd.pid'
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
#public.print_log(status)
|
||||
return status
|
||||
elif name == 'mysql':
|
||||
res = public.ExecShell("service mysqld status")
|
||||
if res and not re.search(r"not\s+running", res[0]):
|
||||
return True
|
||||
return False
|
||||
elif name == 'tomcat':
|
||||
status = False
|
||||
if os.path.exists('/www/server/tomcat/logs/catalina-daemon.pid'):
|
||||
if self.getPid('jsvc'): status = True
|
||||
if not status:
|
||||
if self.getPid('java'): status = True
|
||||
return status
|
||||
elif name == 'pure-ftpd':
|
||||
pidf = '/var/run/pure-ftpd.pid'
|
||||
status = False
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
elif name == 'redis':
|
||||
status = False
|
||||
pidf = '/www/server/redis/redis.pid'
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
elif name == 'memcached':
|
||||
status = False
|
||||
pidf = '/var/run/memcached.pid'
|
||||
if os.path.exists(pidf):
|
||||
pid = public.readFile(pidf)
|
||||
status = self.checkProcess(pid)
|
||||
return status
|
||||
return True
|
||||
|
||||
def clear_push_count(self,id):
|
||||
"""
|
||||
@清除推送次数
|
||||
"""
|
||||
try:
|
||||
#编辑后清理推送次数标记
|
||||
tip_file = '{}/data/push/tips/{}'.format(public.get_panel_path(),id)
|
||||
if os.path.exists(tip_file):
|
||||
os.remove(tip_file)
|
||||
except:pass
|
||||
|
||||
|
||||
def set_push_config(self,get):
|
||||
"""
|
||||
@name 设置推送配置
|
||||
"""
|
||||
id = get.id
|
||||
module = get.name
|
||||
pdata = json.loads(get.data)
|
||||
|
||||
data = self.__push._get_conf()
|
||||
if not module in data:data[module] = {}
|
||||
|
||||
self.clear_push_count(id)
|
||||
|
||||
is_create = True
|
||||
if pdata['type'] in ['ssl']:
|
||||
for x in data[module]:
|
||||
item = data[module][x]
|
||||
if item['type'] == pdata['type'] and item['project'] == pdata['project']:
|
||||
is_create = False
|
||||
data[module][x] = pdata
|
||||
elif pdata['type'] in ['panel_login']:
|
||||
p_module = pdata['module'].split(',')
|
||||
if len(p_module) > 1:
|
||||
return public.returnMsg(False, public.lang("The panel login alarm only supports one alarm mode."))
|
||||
|
||||
if not pdata['status']:
|
||||
return public.returnMsg(False, public.lang("It does not support suspending the panel login alarm, if you need to suspend, please delete it directly."))
|
||||
|
||||
import config
|
||||
c_obj = config.config()
|
||||
|
||||
args = public.dict_obj()
|
||||
args.type = pdata['module'].strip()
|
||||
|
||||
res = c_obj.set_login_send(args)
|
||||
if not res['status']: return res
|
||||
|
||||
elif pdata['type'] in ['ssh_login']:
|
||||
|
||||
p_module = pdata['module'].split(',')
|
||||
if len(p_module) > 1:
|
||||
return public.returnMsg(False, public.lang("SSH login alarm only supports one alarm mode."))
|
||||
|
||||
if not pdata['status']:
|
||||
return public.returnMsg(False, public.lang("It does not support suspending the SSH login alarm. If you need to suspend, please delete it directly."))
|
||||
|
||||
import ssh_security
|
||||
c_obj = ssh_security.ssh_security()
|
||||
|
||||
args = public.dict_obj()
|
||||
args.type = pdata['module'].strip()
|
||||
|
||||
res = c_obj.set_login_send(args)
|
||||
if not res['status']: return res
|
||||
|
||||
elif pdata['type'] in ['ssh_login_error']:
|
||||
|
||||
res = public.get_ips_area(['127.0.0.1'])
|
||||
if 'status' in res:
|
||||
return res
|
||||
|
||||
elif pdata['type'] in ['panel_safe_push']:
|
||||
pdata['interval'] = 30
|
||||
|
||||
if is_create: data[module][id] = pdata
|
||||
public.set_module_logs('site_push_ssl','set_push_config',1)
|
||||
return data
|
||||
|
||||
def del_push_config(self,get):
|
||||
"""
|
||||
@name 删除推送记录
|
||||
@param get
|
||||
id = 告警记录标识
|
||||
module = 告警模块, site_push,panel_push
|
||||
"""
|
||||
id = get.id
|
||||
module = get.name
|
||||
self.clear_push_count(id)
|
||||
|
||||
data = self.__push.get_push_list(get)
|
||||
info = data[module][id]
|
||||
if id in ['panel_login']:
|
||||
|
||||
c_obj = config.config()
|
||||
args = public.dict_obj()
|
||||
args.type = info['module'].strip()
|
||||
res = c_obj.clear_login_send(args)
|
||||
# public.print_log(json.dumps(res))
|
||||
if not res['status']: return res
|
||||
elif id in ['ssh_login']:
|
||||
|
||||
import ssh_security
|
||||
c_obj = ssh_security.ssh_security()
|
||||
res = c_obj.clear_login_send(None)
|
||||
|
||||
if not res['status']: return res
|
||||
|
||||
try:
|
||||
data = self.__push._get_conf()
|
||||
del data[module][id]
|
||||
public.writeFile(self.__conf_path,json.dumps(data))
|
||||
except: pass
|
||||
return public.returnMsg(True, public.lang("successfully deleted."))
|
||||
|
||||
|
||||
#-----------------------------------------------------------end 添加推送 ------------------------------------------------------
|
||||
def get_unixtime(self,data,format = "%Y-%m-%d %H:%M:%S"):
|
||||
import time
|
||||
timeArray = time.strptime(data,format )
|
||||
timeStamp = int(time.mktime(timeArray))
|
||||
return timeStamp
|
||||
|
||||
def get_site_ssl_info(self,webType,siteName,project_type = ''):
|
||||
"""
|
||||
@获取SSL详细信息
|
||||
@webType string web类型 /nginx /apache /iis
|
||||
@siteName string 站点名称
|
||||
"""
|
||||
result = False
|
||||
if webType in ['nginx','apache']:
|
||||
path = public.get_setup_path()
|
||||
if public.get_os('windows'):
|
||||
conf_file = '{}/{}/conf/vhost/{}.conf'.format(path,webType,siteName)
|
||||
ssl_file = '{}/{}/conf/ssl/{}/fullchain.pem'.format(path,webType,siteName)
|
||||
else:
|
||||
conf_file ='{}/vhost/{}/{}{}.conf'.format(public.get_panel_path(),webType,project_type,siteName)
|
||||
ssl_file = '{}/vhost/cert/{}/fullchain.pem'.format(public.get_panel_path(),siteName)
|
||||
|
||||
conf = public.readFile(conf_file)
|
||||
|
||||
if not conf:
|
||||
return result
|
||||
|
||||
if conf.find('SSLCertificateFile') >=0 or conf.find('ssl_certificate') >= 0:
|
||||
|
||||
if os.path.exists(ssl_file):
|
||||
cert_data = public.get_cert_data(ssl_file)
|
||||
return cert_data
|
||||
return result
|
||||
|
||||
|
||||
def get_total(self):
|
||||
return True
|
||||
|
||||
def get_ssl_push_data(self,data):
|
||||
"""
|
||||
@name 获取SSL推送数据
|
||||
@param data
|
||||
type = ssl
|
||||
project = 项目名称
|
||||
siteName = 站点名称
|
||||
"""
|
||||
|
||||
if time.time() < data['index'] + 86400:
|
||||
return public.returnMsg(False, public.lang("SSL is pushed once a day, skipped."))
|
||||
|
||||
push_keys = []
|
||||
ssl_list = []
|
||||
sql = public.M('sites')
|
||||
if data['project'] == 'all':
|
||||
#过滤单独设置提醒的网站
|
||||
n_list = []
|
||||
try:
|
||||
push_list = self.__push._get_conf()['site_push']
|
||||
for skey in push_list:
|
||||
p_name = push_list[skey]['project']
|
||||
if p_name != 'all': n_list.append(p_name)
|
||||
except : pass
|
||||
|
||||
#所有正常网站
|
||||
web_list = sql.where('status=1',()).select()
|
||||
for web in web_list:
|
||||
project_type = ''
|
||||
if web['name'] in n_list: continue
|
||||
if web['name'] in data['tips_list']: continue
|
||||
|
||||
if not web['project_type'] in ['PHP']:
|
||||
project_type = web['project_type'].lower() + '_'
|
||||
|
||||
nlist = []
|
||||
info = self.__check_endtime(web['name'],data['cycle'],project_type)
|
||||
if type(info) != list:
|
||||
nlist.append(info)
|
||||
else:
|
||||
nlist = info
|
||||
|
||||
for info in nlist:
|
||||
if not info: continue
|
||||
info['siteName'] = web['name']
|
||||
push_keys.append(web['name'])
|
||||
ssl_list.append(info)
|
||||
else:
|
||||
project_type = ''
|
||||
find = sql.where('name=? and status=1',(data['project'],)).find()
|
||||
if not find: return public.returnMsg(False, public.lang("no site available."))
|
||||
|
||||
if not find['project_type'] in ['PHP']:
|
||||
project_type = find['project_type'].lower() + '_'
|
||||
|
||||
nlist = []
|
||||
info = self.__check_endtime(find['name'],data['cycle'],project_type)
|
||||
if type(info) != list:
|
||||
nlist.append(info)
|
||||
else:
|
||||
nlist = info
|
||||
|
||||
for info in nlist:
|
||||
if not info: continue
|
||||
info['siteName'] = find['name']
|
||||
ssl_list.append(info)
|
||||
|
||||
return self.__get_ssl_result(data,ssl_list,push_keys)
|
||||
|
||||
def get_panel_update_data(self,data):
|
||||
"""
|
||||
@name 获取面板更新推送
|
||||
@param push_keys array 推送次数缓存key
|
||||
"""
|
||||
stime = time.time()
|
||||
result = {'index': stime ,'push_keys':[data['id']]}
|
||||
|
||||
#面板更新提醒
|
||||
if stime < data['index'] + 86400:
|
||||
return public.returnMsg(False, public.lang("push once a day, skip."))
|
||||
|
||||
if public.is_self_hosted():
|
||||
return public.returnMsg(False, public.lang("Panel update push is disabled in self-hosted mode."))
|
||||
|
||||
s_url = '{}/api/panel/updateLinuxEn'
|
||||
if public.get_os('windows'): s_url = '{}/api/wpanel/updateWindows'
|
||||
s_url = s_url.format(public.OfficialApiBase())
|
||||
|
||||
try:
|
||||
res = json.loads(public.httpPost(s_url,{}))
|
||||
if not res: return public.returnMsg(False, public.lang("Failed to get update information."))
|
||||
except:pass
|
||||
|
||||
n_ver = res['version']
|
||||
if res['is_beta']:
|
||||
n_ver = res['beta']['version']
|
||||
|
||||
old_ver = public.get_cache_func(data['type'])['data']
|
||||
if not old_ver:
|
||||
public.set_cache_func(data['type'],n_ver)
|
||||
else:
|
||||
if old_ver == n_ver:
|
||||
#处理推送次数逻辑
|
||||
if data['id'] in data['tips_list']:
|
||||
print('Notifications exceeded, skip.')
|
||||
return result
|
||||
else:
|
||||
#清除缓存
|
||||
data['tips_list'] = []
|
||||
try:
|
||||
tips_path = '{}/data/push/tips/{}'.format(public.get_panel_path(),data['id'])
|
||||
os.remove(tips_path)
|
||||
print('New version found, recount notifications.')
|
||||
except:pass
|
||||
public.set_cache_func(data['type'],n_ver)
|
||||
|
||||
if public.version() != n_ver:
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
|
||||
s_list = ["> Notification Type: Panel Version Update",">current version:{} ".format(public.version()),">The latest version of:{}".format(n_ver)]
|
||||
sdata = public.get_push_info('Panel Update Reminder',s_list)
|
||||
result[m_module] = sdata
|
||||
|
||||
return result
|
||||
|
||||
def get_panel_safe_push(self,data,result):
|
||||
s_list = []
|
||||
#面板登录用户安全
|
||||
t_add,t_del,total = self.get_records_calc('login_user_safe',public.M('users'))
|
||||
if t_add > 0 or t_del > 0:
|
||||
s_list.append(">Login user change:<font color=#ff0000> total {}, add {}, delete {}</font>.".format(total,t_add,t_del))
|
||||
|
||||
#面板日志发生删除
|
||||
t_add,t_del,total = self.get_records_calc('panel_logs_safe',public.M('logs'),1)
|
||||
if t_del > 0:
|
||||
s_list.append(">The panel log is deleted, the number of deleted items:<font color=#ff0000>{} </font>".format(t_del))
|
||||
|
||||
debug_str = 'Disable'
|
||||
debug_status = 'False'
|
||||
#面板开启开发者模式告警
|
||||
if os.path.exists('{}/data/debug.pl'.format(public.get_panel_path())):
|
||||
debug_status = 'True'
|
||||
debug_str = 'Enable'
|
||||
|
||||
skey = 'panel_debug_safe'
|
||||
tmp = public.get_cache_func(skey)['data']
|
||||
if not tmp:
|
||||
public.set_cache_func(skey,debug_status)
|
||||
else:
|
||||
if str(debug_status) != tmp:
|
||||
s_list.append(">Panel developer mode changed, current status:{}".format(debug_str))
|
||||
public.set_cache_func(skey,debug_status)
|
||||
|
||||
# #面板开启api告警
|
||||
# api_str = 'False'
|
||||
# s_path = '{}/config/api.json'.format(public.get_panel_path())
|
||||
# if os.path.exists(s_path):
|
||||
# api_str = public.readFile(s_path).strip()
|
||||
# if not api_str: api_str = 'False'
|
||||
|
||||
# api_str = public.md5(api_str)
|
||||
# skey = 'panel_api_safe'
|
||||
# tmp = public.get_cache_func(skey)['data']
|
||||
# if not tmp:
|
||||
# public.set_cache_func(skey,api_str)
|
||||
# else:
|
||||
# if api_str != tmp:
|
||||
# s_list.append(">面板API配置发生改变,请及时确认是否本人操作.")
|
||||
# public.set_cache_func(skey,api_str)
|
||||
|
||||
|
||||
#面板用户名和密码发生变更
|
||||
find = public.M('users').where('id=?',(1,)).find()
|
||||
|
||||
if find:
|
||||
skey = 'panel_user_change_safe'
|
||||
user_str = public.md5(find['username']) + '|' + public.md5(find['password'])
|
||||
tmp = public.get_cache_func(skey)['data']
|
||||
if not tmp:
|
||||
public.set_cache_func(skey,user_str)
|
||||
else:
|
||||
if user_str != tmp:
|
||||
s_list.append(">YakPanel login account or password changed")
|
||||
public.set_cache_func(skey,user_str)
|
||||
|
||||
|
||||
if len(s_list) > 0:
|
||||
sdata = public.get_push_info('YakPanel security warning',s_list)
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
result[m_module] = sdata
|
||||
|
||||
return result
|
||||
|
||||
def get_push_data(self,data,total):
|
||||
"""
|
||||
@检测推送数据
|
||||
@data dict 推送数据
|
||||
title:标题
|
||||
project:项目
|
||||
type:类型 ssl:证书提醒
|
||||
cycle:周期 天、小时
|
||||
keys:检测键值
|
||||
"""
|
||||
stime = time.time()
|
||||
if not 'tips_list' in data: data['tips_list'] = []
|
||||
if not 'project' in data: data['project'] = ''
|
||||
|
||||
#优先处理面板更新
|
||||
if data['type'] in ['panel_update']:
|
||||
return self.get_panel_update_data(data)
|
||||
|
||||
result = {'index': stime ,'push_keys':[data['id']]}
|
||||
if data['project']:
|
||||
result['push_keys'] = [data['project']]
|
||||
|
||||
#检测推送次数,超过次数不再推送
|
||||
if data['project'] in data['tips_list'] or data['id'] in data['tips_list']:
|
||||
return result
|
||||
|
||||
if data['type'] in ['ssl']:
|
||||
return self.get_ssl_push_data(data)
|
||||
|
||||
elif data['type'] in ['site_endtime']:
|
||||
result['push_keys'] = []
|
||||
|
||||
if stime < data['index'] + 86400:
|
||||
return public.returnMsg(False, public.lang("push once a day, skip."))
|
||||
|
||||
mEdate = public.format_date(format='%Y-%m-%d',times = stime + 86400 * int(data['cycle']))
|
||||
web_list = public.M('sites').where('edate>? AND edate<? AND (status=? OR status=?)',('0000-00-00',mEdate,1,u'Running')).field('id,name,edate').select()
|
||||
|
||||
if len(web_list) > 0:
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
|
||||
s_list = ['>Expiring:<font color=#ff0000>{} website</font>'.format(len(web_list))]
|
||||
for x in web_list:
|
||||
if x['name'] in data['tips_list']: continue
|
||||
result['push_keys'].append(x['name'])
|
||||
|
||||
s_list.append(">Website: {} Expires: {}".format(x['name'],x['edate']))
|
||||
|
||||
sdata = public.get_push_info('YakPanel Website Expiration Reminder',s_list)
|
||||
result[m_module] = sdata
|
||||
return result
|
||||
|
||||
elif data['type'] in ['panel_pwd_endtime']:
|
||||
if stime < data['index'] + 86400:
|
||||
return public.returnMsg(False, public.lang("push once a day, skip."))
|
||||
|
||||
import config
|
||||
c_obj = config.config()
|
||||
res = c_obj.get_password_config(None)
|
||||
|
||||
if res['expire'] > 0 and res['expire_day'] < data['cycle']:
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
|
||||
s_list = [">Alarm type: Login password is about to expire",">Days remaining: <font color=#ff0000>{} days</font>".format(res['expire_day'])]
|
||||
sdata = public.get_push_info('YakPanel password expiration reminder',s_list)
|
||||
result[m_module] = sdata
|
||||
return result
|
||||
elif data['type'] in ['clear_bash_history']:
|
||||
stime = time.time()
|
||||
|
||||
result = {'index': stime}
|
||||
|
||||
elif data['type'] in ['panel_bind_user_change']:
|
||||
#面板绑定帐号发生变更
|
||||
uinfo = public.get_user_info()
|
||||
|
||||
user_str = public.md5(uinfo['username'])
|
||||
old_str = public.get_cache_func(data['type'])['data']
|
||||
if not old_str:
|
||||
public.set_cache_func(data['type'],user_str)
|
||||
else:
|
||||
if user_str != old_str:
|
||||
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
|
||||
s_list = [">Alarm type: panel binding account change",">Currently bound account:{}****{}".format(uinfo['username'][:3],uinfo['username'][-4:])]
|
||||
sdata = public.get_push_info('Panel binding account change reminder',s_list)
|
||||
result[m_module] = sdata
|
||||
|
||||
public.set_cache_func(data['type'],user_str)
|
||||
return result
|
||||
|
||||
elif data['type'] in ['panel_safe_push']:
|
||||
return self.get_panel_safe_push(data,result)
|
||||
|
||||
elif data['type'] in ['panel_oneav_push']:
|
||||
#微步在线木马扫描提醒
|
||||
sfile = '{}/plugin/oneav/oneav_main.py'.format(public.get_panel_path())
|
||||
if not os.path.exists(sfile): return
|
||||
|
||||
_obj = public.get_script_object(sfile)
|
||||
_main = getattr(_obj,'oneav_main',None)
|
||||
if not _main: return
|
||||
|
||||
args = public.dict_obj()
|
||||
args.p = 1
|
||||
args.count = 1000
|
||||
|
||||
f_list = []
|
||||
s_day = public.getDate(format='%Y-%m-%d')
|
||||
|
||||
for line in _main().get_logs(args):
|
||||
|
||||
#未检测到当天日志,跳出
|
||||
if public.format_date(times=line['time']).find(s_day) == -1:
|
||||
break
|
||||
if line['file'] in f_list: continue
|
||||
|
||||
f_list.append(line['file'])
|
||||
|
||||
if not f_list: return
|
||||
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
|
||||
s_list = [">alert type:Trojan detects alarms",">Content of notification: <font color=#ff0000> Found suspected Trojan files {}</font>".format(len(f_list)),">listed files:[{}]".format('、'.join(f_list))]
|
||||
sdata = public.get_push_info('YakPanel trojan detects alarms',s_list)
|
||||
result[m_module] = sdata
|
||||
return result
|
||||
|
||||
# 登录失败次数
|
||||
elif data['type'] in ['ssh_login_error']:
|
||||
import PluginLoader
|
||||
|
||||
args = public.dict_obj()
|
||||
args.model_index = 'safe'
|
||||
args.count = data['count']
|
||||
args.p = 1
|
||||
res = PluginLoader.module_run("syslog","get_ssh_error",args)
|
||||
if 'status' in res:
|
||||
return
|
||||
|
||||
if type(res) == list:
|
||||
last_info = res[data['count'] -1]
|
||||
if public.to_date(times=last_info['time']) >= time.time() - data['cycle'] * 60:
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
|
||||
s_list = [">Notification type: SSH login failure alarm",">Alarm content: <font color=#ff0000>login failed more than {} times within {} minutes</font> ".format(data['cycle'],data['count'])]
|
||||
sdata = public.get_push_info('SSH login failure alarm',s_list)
|
||||
result[m_module] = sdata
|
||||
return result
|
||||
|
||||
elif data['type'] in ['services']:
|
||||
ser_name = data['project']
|
||||
|
||||
status = self.get_server_status(ser_name)
|
||||
if status > 0:
|
||||
return public.returnMsg(False, public.lang("normal status,Skip."))
|
||||
else:
|
||||
if status == 0:
|
||||
return self.__get_service_result(data)
|
||||
return public.returnMsg(False, public.lang("service not installed,Skip."))
|
||||
|
||||
return public.returnMsg(False, public.lang("Threshold not reached,Skip."))
|
||||
|
||||
|
||||
def get_records_calc(self,skey,table,stype = 0):
|
||||
'''
|
||||
@name 获取指定表数据是否发生改变
|
||||
@param skey string 缓存key
|
||||
@param table db 表对象
|
||||
@param stype int 0:计算总条数 1:只计算删除
|
||||
@return array
|
||||
total int 总数
|
||||
|
||||
'''
|
||||
total_add = 0
|
||||
total_del = 0
|
||||
|
||||
#获取当前总数和最大索引值
|
||||
u_count = table.count()
|
||||
u_max = table.order('id desc').getField('id')
|
||||
|
||||
n_data = {'count': u_count,'max': u_max}
|
||||
tmp = public.get_cache_func(skey)['data']
|
||||
if not tmp:
|
||||
public.set_cache_func(skey,n_data)
|
||||
else:
|
||||
n_data = tmp
|
||||
|
||||
#检测上一次记录条数是否被删除
|
||||
pre_count = table.where('id<=?',(n_data['max'])).count()
|
||||
if stype == 1:
|
||||
if pre_count < n_data['count']: #有数据被删除,记录被删条数
|
||||
total_del += n_data['count'] - pre_count
|
||||
|
||||
n_count = u_max - pre_count #上次记录后新增的条数
|
||||
n_idx = u_max - n_data['max'] #上次记录后新增的索引差
|
||||
if n_count < n_idx:
|
||||
total_del += n_idx - n_count
|
||||
else:
|
||||
|
||||
if pre_count < n_data['count']: #有数据被删除,记录被删条数
|
||||
total_del += n_data['count'] - pre_count
|
||||
elif pre_count > n_data['count']:
|
||||
total_add += pre_count - n_data['count']
|
||||
|
||||
t1_del = 0
|
||||
t1_add = 0
|
||||
n_count = u_count - pre_count #上次记录后新增的条数
|
||||
|
||||
if u_max > n_data['max']:
|
||||
n_idx = u_max - n_data['max'] #上次记录后新增的索引差
|
||||
if n_count < n_idx: t1_del = n_idx - n_count
|
||||
|
||||
#新纪录除开删除,全部计算为新增
|
||||
t1_add = n_count - t1_del
|
||||
if t1_add > 0: total_add += t1_add
|
||||
|
||||
total_del += t1_del
|
||||
|
||||
public.set_cache_func(skey,{'count': u_count,'max': u_max})
|
||||
return total_add,total_del,u_count
|
||||
|
||||
def __check_endtime(self,siteName,cycle,project_type = ''):
|
||||
"""
|
||||
@name 检测到期时间
|
||||
@param siteName str 网站名称
|
||||
@param cycle int 提前提醒天数
|
||||
@param project_type str 网站类型
|
||||
"""
|
||||
info = self.get_site_ssl_info(public.get_webserver(),siteName,project_type)
|
||||
if info:
|
||||
endtime = self.get_unixtime(info['notAfter'],'%Y-%m-%d')
|
||||
day = int((endtime - time.time()) / 86400)
|
||||
if day <= cycle: return info
|
||||
|
||||
return False
|
||||
|
||||
def __get_ssl_result(self,data,clist,push_keys = []):
|
||||
"""
|
||||
@ssl到期返回
|
||||
@data dict 推送数据
|
||||
@clist list 证书列表
|
||||
@return dict
|
||||
"""
|
||||
if len(clist) == 0:
|
||||
return public.returnMsg(False, public.lang("Expired certificate not found, skipping."))
|
||||
|
||||
result = {'index':time.time(),'push_keys':push_keys }
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module in self.__push_model:
|
||||
|
||||
sdata = self.__push.format_msg_data()
|
||||
if m_module in ['sms']:
|
||||
|
||||
sdata['sm_type'] = 'ssl_end|YakPanel SSL Expiration Reminder'
|
||||
sdata['sm_args'] = public.check_sms_argv({
|
||||
'name':public.get_push_address(),
|
||||
'website':public.push_argv(clist[0]["siteName"]),
|
||||
'time':clist[0]["notAfter"],
|
||||
'total':len(clist)
|
||||
})
|
||||
else:
|
||||
s_list = ['>Expiring soon: <font color=#ff0000>{} copies</font>'.format(len(clist))]
|
||||
for x in clist:
|
||||
s_list.append(">Website: {} Expires: {}".format(x['siteName'],x['notAfter']))
|
||||
|
||||
sdata = public.get_push_info('YakPanel SSL expiration reminder',s_list)
|
||||
|
||||
result[m_module] = sdata
|
||||
return result
|
||||
|
||||
# 服务停止返回
|
||||
def __get_service_result(self, data):
|
||||
s_idx = int(time.time())
|
||||
if s_idx < data['index'] + data['interval']:
|
||||
return public.returnMsg(False, public.lang("Interval not reached,Skip."))
|
||||
|
||||
result = {'index': s_idx}
|
||||
|
||||
for m_module in data['module'].split(','):
|
||||
result[m_module] = self.__push.format_msg_data()
|
||||
|
||||
if m_module in ['dingding', 'weixin', 'mail', 'wx_account', 'feishu']:
|
||||
s_list = [
|
||||
">Service type:" + data["project"],
|
||||
">Service Status: Stopped"]
|
||||
sdata = public.get_push_info('service stop warning', s_list)
|
||||
result[m_module] = sdata
|
||||
|
||||
elif m_module in ['sms']:
|
||||
result[m_module]['sm_type'] = 'servcies'
|
||||
result[m_module]['sm_args'] = {'name': '{}'.format(public.GetConfigValue('title')),
|
||||
'product': data["project"], 'product1': data["project"]}
|
||||
|
||||
return result
|
||||
313
class/push/tamper_push.py
Normal file
313
class/push/tamper_push.py
Normal file
@@ -0,0 +1,313 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | YakPanel
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2020 YakPanel(https://www.yakpanel.com) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: baozi <baozi@yakpanel.com>
|
||||
# | Author: baozi
|
||||
# +-------------------------------------------------------------------
|
||||
import sys,os,re,json
|
||||
|
||||
import public,panelPush, time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
try:
|
||||
from YakPanel import cache
|
||||
except :
|
||||
from cachelib import SimpleCache
|
||||
cache = SimpleCache()
|
||||
|
||||
class base_push:
|
||||
|
||||
# 版本信息 目前无作用
|
||||
def get_version_info(self, get=None):
|
||||
raise NotImplementedError
|
||||
|
||||
# 格式化返回执行周期, 目前无作用
|
||||
def get_push_cycle(self, data: dict):
|
||||
return data
|
||||
|
||||
# 获取模块推送参数
|
||||
def get_module_config(self, get: public.dict_obj):
|
||||
raise NotImplementedError
|
||||
|
||||
# 获取模块配置项
|
||||
def get_push_config(self, get: public.dict_obj):
|
||||
# 其实就是配置信息,没有也会从全局配置文件push.json中读取
|
||||
raise NotImplementedError
|
||||
|
||||
# 写入推送配置文件
|
||||
def set_push_config(self, get: public.dict_obj):
|
||||
raise NotImplementedError
|
||||
|
||||
# 删除推送配置
|
||||
def del_push_config(self, get: public.dict_obj):
|
||||
# 从配置中删除信息,并做一些您想做的事,如记日志
|
||||
raise NotImplementedError
|
||||
|
||||
# 无意义???
|
||||
def get_total(self):
|
||||
return True
|
||||
|
||||
# 检查并获取推送消息,返回空时,不做推送, 传入的data是配置项
|
||||
def get_push_data(self, data, total):
|
||||
# data 内容
|
||||
# index : 时间戳 time.time()
|
||||
# 消息 以类型为key, 以内容为value, 内容中包含title 和msg
|
||||
# push_keys: 列表,发送了信息的推送任务的id,用来验证推送任务次数() 意义不大
|
||||
raise NotImplementedError
|
||||
|
||||
class tamper_push(base_push):
|
||||
__tamper_path = "{}/tamper".format(public.get_setup_path())
|
||||
__total_path = "{}/total/total.json".format(__tamper_path)
|
||||
__config_file = "{}/tamper.conf".format(__tamper_path)
|
||||
__push_conf = "{}/class/push/push.json".format(public.get_panel_path())
|
||||
__logs_path = "{}/logs".format(__tamper_path)
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__push = panelPush.panelPush()
|
||||
try:
|
||||
config = public.readFile(self.__config_file)
|
||||
config_dict = json.loads(config)
|
||||
self.__config = {}
|
||||
for i in config_dict["paths"]:
|
||||
self.__config[i["pid"]] = i
|
||||
except:
|
||||
self.__config = None
|
||||
|
||||
|
||||
# 版本信息 目前无作用
|
||||
def get_version_info(self, get=None):
|
||||
data = {}
|
||||
data['ps'] = 'Tamper-proof for Enterprise'
|
||||
data['version'] = '1.0'
|
||||
data['date'] = '2023-03-24'
|
||||
data['author'] = 'YakPanel'
|
||||
data['help'] = 'http://www.yakpanel.com/bbs'
|
||||
return data
|
||||
|
||||
# 获取模块推送参数
|
||||
def get_module_config(self, get: public.dict_obj):
|
||||
data = []
|
||||
item = self.__push.format_push_data(push = ["mail",'dingding','weixin',"feishu", "wx_account", "tg"], project = 'tamper',type = '')
|
||||
item['cycle'] = 30
|
||||
item['title'] = 'Tamper-proof for Enterprise'
|
||||
data.append(item)
|
||||
return data
|
||||
|
||||
|
||||
# 获取模块配置项
|
||||
def get_push_config(self, get: public.dict_obj):
|
||||
id = get.id
|
||||
# 其实就是配置信息,没有也会从全局配置文件push.json中读取
|
||||
push_list = self.__push._get_conf()
|
||||
|
||||
if not id in push_list["tamper_push"]:
|
||||
res_data = public.returnMsg(False, 'The specified configuration was not found.')
|
||||
res_data['code'] = 100
|
||||
return res_data
|
||||
result = push_list["tamper_push"][id]
|
||||
return result
|
||||
|
||||
|
||||
# 写入推送配置文件
|
||||
def set_push_config(self, get: public.dict_obj):
|
||||
if self.__config is None:
|
||||
return public.returnMsg(False, public.lang('Configuration error, cannot be set, please try use the command fix: "bash /www/server/panel/plugin/tamper_core/install.sh install"')
|
||||
)
|
||||
try:
|
||||
id = int(get.id)
|
||||
if id != 0 and id not in self.__config:
|
||||
return public.returnMsg(False, public.lang("No protection directory specified"))
|
||||
except ValueError:
|
||||
return public.returnMsg(False, public.lang("No protection directory specified"))
|
||||
|
||||
pdata = json.loads(get.data)
|
||||
data = self.__push._get_conf()
|
||||
if "tamper_push" not in data: data["tamper_push"] = {}
|
||||
if not str(id) in data["tamper_push"]:
|
||||
if id != 0:
|
||||
public.WriteLog("Tamper-proof for Enterprise","Protected directory: {} set tampering alarm ".format(self.__config[id]["path"]))
|
||||
else:
|
||||
public.WriteLog("Tamper-proof for Enterprise","All directory is set, tampering alarm ")
|
||||
else:
|
||||
if id != 0:
|
||||
public.WriteLog("Tamper-proof for Enterprise","Protected directory: {} Changed alert config".format(self.__config[id]["path"]))
|
||||
else:
|
||||
public.WriteLog("Tamper-proof for Enterprise"," All directory alarm config changed")
|
||||
self._del_today_push_count(id)
|
||||
pdata["status"] = True
|
||||
pdata["pid"] = id
|
||||
pdata["project"] = "tamper_core"
|
||||
data["tamper_push"][str(id)] = pdata
|
||||
return data
|
||||
|
||||
# 删除推送配置
|
||||
def del_push_config(self, get: public.dict_obj):
|
||||
# 从配置中删除信息,并做一些您想做的事,如记日志
|
||||
id = get.id
|
||||
data = self.__push._get_conf()
|
||||
if str(id).strip() in data["tamper_push"]:
|
||||
del data["tamper_push"][id]
|
||||
public.writeFile(self.__push_conf,json.dumps(data))
|
||||
return public.returnMsg(True, public.lang("successfully deleted."))
|
||||
|
||||
# 无意义???
|
||||
def get_total(self):
|
||||
return True
|
||||
|
||||
# 检查并获取推送消息,返回空时,不做推送, 传入的data是配置项
|
||||
def get_push_data(self, data, total):
|
||||
# 返回内容
|
||||
# index : 时间戳 time.time()
|
||||
# 消息 以类型为key, 以内容为value, 内容中包含title 和msg
|
||||
# push_keys: 列表,发送了信息的推送任务的id,用来验证推送任务次数() 意义不大
|
||||
"""
|
||||
@检测推送数据
|
||||
@data dict 推送数据
|
||||
title:标题
|
||||
count:触发次数
|
||||
cycle:周期 天、小时
|
||||
keys:检测键值
|
||||
"""
|
||||
if not self._log_check(data["id"], data["cycle"], data["count"]): return None
|
||||
if data["push_count"] <= self._get_today_push_count(data["id"]):return None
|
||||
result = {'index': time.time(), }
|
||||
tamper_path = "All protected directory " if not int(data["id"]) else "Protected Directory:{}".format(self.__config[int(data["id"])]["path"])
|
||||
for m_module in data['module'].split(','):
|
||||
if m_module == 'sms': continue
|
||||
s_list = [">Notification: Tamper-proof for Enterprise alarm", ">Alarm content: <font color=#ff0000>Last {} minutes, {} has been tampered with more than {} times, and it has been successfully intercepted, please pay attention to the website situation , and deal with it in timely manner.</font> ".format(data['cycle'], tamper_path, data['count'])]
|
||||
sdata = public.get_push_info('Tamper-proof for Enterprise alarm', s_list)
|
||||
result[m_module] = sdata
|
||||
self._set_total()
|
||||
self._set_today_push_count(data["id"])
|
||||
return result
|
||||
|
||||
def _log_check(self, id, cycle, count):
|
||||
target_time = datetime.now() - timedelta(minutes=cycle)
|
||||
tday, yday = datetime.now().strftime('%Y-%m-%d'), (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
|
||||
files = []
|
||||
if id == "0":
|
||||
for i in self.__config.keys():
|
||||
files.append("{}/{}/{}.log".format(self.__logs_path,i,tday))
|
||||
files.append("{}/{}/{}.log".format(self.__logs_path,i,yday))
|
||||
else:
|
||||
files.append("{}/{}/{}.log".format(self.__logs_path,id,tday))
|
||||
files.append("{}/{}/{}.log".format(self.__logs_path,id,yday))
|
||||
|
||||
_count = 0
|
||||
public.print_log(files)
|
||||
_f = '%Y-%m-%d %H:%M:%S'
|
||||
for i in self._get_logs(files):
|
||||
if _count >= count:
|
||||
return True
|
||||
if datetime.strptime(i, _f) > target_time:
|
||||
_count += 1
|
||||
else:
|
||||
return _count >= count
|
||||
return _count >= count
|
||||
|
||||
def _get_logs(self, files):
|
||||
def the_generator(self):
|
||||
_buf = b""
|
||||
for fp in self._get_fp():
|
||||
is_start = True
|
||||
while is_start:
|
||||
buf = b''
|
||||
while True:
|
||||
pos = fp.tell()
|
||||
read_size = pos if pos <= 38 else 38
|
||||
fp.seek(-read_size, 1)
|
||||
_buf = fp.read(read_size) + _buf
|
||||
fp.seek(-read_size, 1)
|
||||
nl_idx = _buf.rfind(ord('\n'))
|
||||
if nl_idx == -1:
|
||||
if pos <= 38:
|
||||
buf, _buf = _buf, b''
|
||||
is_start = False
|
||||
break
|
||||
else:
|
||||
buf = _buf[nl_idx+1:]
|
||||
_buf = _buf[:nl_idx]
|
||||
break
|
||||
yield self._get_time(buf.decode("utf-8"))
|
||||
|
||||
def the_init(self,log_files):
|
||||
self.log_files = log_files
|
||||
|
||||
def the_get_fp(self):
|
||||
for i in self.log_files:
|
||||
if not os.path.exists(i): continue
|
||||
with open(i, 'rb') as fp:
|
||||
fp.seek(-1, 2)
|
||||
yield fp
|
||||
|
||||
def the_get_time(self, log: str):
|
||||
return log.split("] [", 1)[0].strip("[").strip()
|
||||
|
||||
attr = {
|
||||
"__init__": the_init,
|
||||
"_get_fp": the_get_fp,
|
||||
"__iter__": the_generator,
|
||||
"_get_time": the_get_time,
|
||||
}
|
||||
return type("LogContent", (object, ), attr)(files)
|
||||
|
||||
def _set_total(self):
|
||||
try:
|
||||
total = json.loads(public.readFile(self.__total_path))
|
||||
if "warning_msg" not in total:
|
||||
total["warning_msg"] = 1
|
||||
else:
|
||||
total["warning_msg"] += 1
|
||||
public.writeFile(self.__total_path, json.dumps(total))
|
||||
except:
|
||||
pass
|
||||
|
||||
def _get_today_push_count(self, id):
|
||||
t_day = datetime.now().strftime('%Y-%m-%d')
|
||||
today_tip = '{}/data/push/tips/tamper_today.json'.format(public.get_panel_path())
|
||||
if os.path.exists(today_tip):
|
||||
tip = json.loads(public.readFile(today_tip))
|
||||
if tip["t_day"] != t_day:
|
||||
tip = {"t_day": t_day}
|
||||
res = 0
|
||||
elif id in tip:
|
||||
res = tip[id]
|
||||
else:
|
||||
res = 0
|
||||
else:
|
||||
tip = {"t_day": t_day}
|
||||
res = 0
|
||||
|
||||
public.writeFile(today_tip, json.dumps(tip))
|
||||
setattr(self, "_tip_", tip)
|
||||
return res
|
||||
|
||||
|
||||
def _set_today_push_count(self, id):
|
||||
today_tip = '{}/data/push/tips/tamper_today.json'.format(public.get_panel_path())
|
||||
if hasattr(self, "_tip_"):
|
||||
tip = getattr(self, "_tip_")
|
||||
else:
|
||||
tip = json.loads(public.readFile(today_tip))
|
||||
if id in tip:
|
||||
tip[id] += 1
|
||||
else:
|
||||
tip[id] = 1
|
||||
|
||||
public.writeFile(today_tip, json.dumps(tip))
|
||||
|
||||
def _del_today_push_count(self, id):
|
||||
t_day = datetime.now().strftime('%Y-%m-%d')
|
||||
today_tip = '{}/data/push/tips/tamper_today.json'.format(public.get_panel_path())
|
||||
if os.path.exists(today_tip):
|
||||
tip = json.loads(public.readFile(today_tip))
|
||||
if tip["t_day"] != t_day:
|
||||
tip = {"t_day": t_day}
|
||||
elif id in tip:
|
||||
del tip[id]
|
||||
|
||||
public.writeFile(today_tip, json.dumps(tip))
|
||||
|
||||
Reference in New Issue
Block a user