mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-09-13 21:17:40 +02:00
v6.8.12
1. Add disk IO information to the homepage 2. Add a clear list button in the upload window 3. Optimize the background task overhead of the panel 4. Optimize the disk information caching mechanism 5. Panel Pro edition is online 6. Optimize panel resource usage 7. Optimize SSL certificate renewal 8. Fix the problem of reporting an error when the security entrance is empty 9. Fix the problem of infinite recursion when copying directories in extreme cases
This commit is contained in:
+62
-5
@@ -414,6 +414,11 @@ class acme_v2:
|
||||
|
||||
# 设置验证信息
|
||||
def set_auth_info(self, identifier_auth):
|
||||
|
||||
#从云端验证
|
||||
if not self.cloud_check_domain(identifier_auth['domain']):
|
||||
self.err = "Cloud verification failed!"
|
||||
|
||||
# 是否手动验证DNS
|
||||
if identifier_auth['auth_to'] == 'dns':
|
||||
return None
|
||||
@@ -427,6 +432,14 @@ class acme_v2:
|
||||
self.create_dns_record(
|
||||
identifier_auth['auth_to'], identifier_auth['domain'], identifier_auth['auth_value'])
|
||||
|
||||
#从云端验证域名是否可访问
|
||||
def cloud_check_domain(self,domain):
|
||||
try:
|
||||
result = requests.post('https://www.aapanel.com/api/panel/checkDomain',{"domain":domain,"ssl":1}).json()
|
||||
return result['status']
|
||||
except: return False
|
||||
|
||||
|
||||
#清理验证文件
|
||||
def claer_auth_file(self,index):
|
||||
if not self._config['orders'][index]['auth_type'] in ['http','tls']:
|
||||
@@ -881,9 +894,10 @@ fullchain.pem Paste into certificate input box
|
||||
return p12.export()
|
||||
|
||||
# 拆分根证书
|
||||
def split_ca_data(self, cert):
|
||||
datas = cert.split('-----END CERTIFICATE-----')
|
||||
return {"cert": datas[0] + "-----END CERTIFICATE-----\n", "root": datas[1] + '-----END CERTIFICATE-----\n'}
|
||||
def split_ca_data(self,cert):
|
||||
sp_key = '-----END CERTIFICATE-----\n'
|
||||
datas = cert.split(sp_key)
|
||||
return {"cert": datas[0] + sp_key, "root": sp_key.join(datas[1:])}
|
||||
|
||||
# 构造可选名称
|
||||
def get_alt_names(self, index):
|
||||
@@ -1237,6 +1251,8 @@ fullchain.pem Paste into certificate input box
|
||||
def apply_cert(self, domains, auth_type='dns', auth_to='Dns_com|None|None', **args):
|
||||
write_log("", "wb+")
|
||||
try:
|
||||
if 'auto_wildcard' in args and args['auto_wildcard']:
|
||||
self._auto_wildcard = True
|
||||
self.get_apis()
|
||||
index = None
|
||||
if 'index' in args:
|
||||
@@ -1382,7 +1398,42 @@ fullchain.pem Paste into certificate input box
|
||||
return to_path
|
||||
return False
|
||||
|
||||
def get_site_id(self,domains):
|
||||
site_ids=[]
|
||||
for domain in domains:
|
||||
if '*' in domain:
|
||||
continue
|
||||
site_id = public.M('domain').where('name=?', (domain,)).field('pid').select()
|
||||
if not site_id:
|
||||
continue
|
||||
site_ids.append(site_id[0]['pid'])
|
||||
if not site_ids:
|
||||
return False
|
||||
site_ids = list(set(site_ids))
|
||||
if not len(site_ids) == 1:
|
||||
return False
|
||||
return site_ids[0]
|
||||
|
||||
def get_site_runpath(self,domains):
|
||||
site_id = self.get_site_id(domains)
|
||||
if not site_id:
|
||||
return False
|
||||
import panelSite
|
||||
from collections import namedtuple
|
||||
ps = panelSite.panelSite()
|
||||
# 构造一个类
|
||||
get = namedtuple("get", ["id"])
|
||||
get.id=site_id
|
||||
site_path = public.M('sites').where('id=?', (get.id,)).field('path').select()[0]['path']
|
||||
runpath = ps.GetRunPath(get)
|
||||
return site_path + runpath
|
||||
|
||||
def find_site_stopped(self,domains):
|
||||
site_id = self.get_site_id(domains)
|
||||
if not site_id:
|
||||
return False
|
||||
site_status = public.M('sites').where('id=?', (site_id,)).field('status').select()[0]['status']
|
||||
return site_status
|
||||
|
||||
# 续签证书
|
||||
def renew_cert(self, index):
|
||||
@@ -1406,6 +1457,8 @@ fullchain.pem Paste into certificate input box
|
||||
self._config['orders'][i]['cert_timeout'] = int(time.time())
|
||||
if self._config['orders'][i]['cert_timeout'] > s_time or self._config['orders'][i]['auth_to'] == 'dns':
|
||||
continue
|
||||
if self.find_site_stopped(self._config['orders'][i]['domains']) == '0':
|
||||
continue
|
||||
|
||||
#已删除的网站直接跳过续签
|
||||
if self._config['orders'][i]['auth_to'].find('|') == -1 and self._config['orders'][i]['auth_to'].find('/') != -1:
|
||||
@@ -1418,15 +1471,19 @@ fullchain.pem Paste into certificate input box
|
||||
if not order_index:
|
||||
write_log(public.getMsg('ACME_NO_NEED_RENEW'))
|
||||
return
|
||||
write_log(public.getMsg("ACME_NEED_RENEW",(len(order_index),)))
|
||||
write_log(public.getMsg("ACME_NEED_RENEW",(str(len(order_index)),)))
|
||||
n = 0
|
||||
self.get_apis()
|
||||
cert = None
|
||||
for index in order_index:
|
||||
n += 1
|
||||
write_log(public.getMsg("ACME_RENEWING",(str(n),self._config['orders'][index]['domains'])))
|
||||
write_log(public.getMsg("ACME_RENEWING",(str(n),str(self._config['orders'][index]['domains']))))
|
||||
write_log(public.getMsg('ACME_CREAT_ORDER'))
|
||||
try:
|
||||
run_path = self.get_site_runpath(self._config['orders'][index]['domains'])
|
||||
if run_path:
|
||||
if self._config['orders'][index]['auth_to'] != run_path:
|
||||
self._config['orders'][index]['auth_to'] = run_path
|
||||
index = self.create_order(
|
||||
self._config['orders'][index]['domains'],
|
||||
self._config['orders'][index]['auth_type'],
|
||||
|
||||
+12
-8
@@ -36,8 +36,13 @@ class ajax:
|
||||
#取Nginx负载状态
|
||||
self.CheckStatusConf()
|
||||
result = public.httpGet('http://127.0.0.1/nginx_status')
|
||||
tmp = result.split()
|
||||
if len(tmp) < 15:
|
||||
is_curl = False
|
||||
tmp = []
|
||||
if result:
|
||||
tmp = result.split()
|
||||
if len(tmp) < 15: is_curl = True
|
||||
|
||||
if is_curl:
|
||||
result = public.ExecShell('curl http://127.0.0.1/nginx_status')[0]
|
||||
tmp = result.split()
|
||||
data = {}
|
||||
@@ -464,7 +469,9 @@ class ajax:
|
||||
import json
|
||||
conf_status = public.M('config').where("id=?",('1',)).field('status').find()
|
||||
if int(session['config']['status']) == 0 and int(conf_status['status']) == 0:
|
||||
public.HttpGet('{}/api/setupCount/setupPanel?type=Linux'.format(self.__official_url))
|
||||
# public.HttpGet('{}/api/setupCount/setupPanel?type=Linux'.format(self.__official_url))
|
||||
public.arequests('get', '{}/api/setupCount/setupPanel?type=Linux'.format(self.__official_url))
|
||||
|
||||
public.M('config').where("id=?",('1',)).setField('status',1)
|
||||
|
||||
#取回远程版本信息
|
||||
@@ -492,10 +499,7 @@ class ajax:
|
||||
data['intrusion'] = 0
|
||||
data['uid'] = self.get_uid()
|
||||
#msg = public.getMsg('PANEL_UPDATE_MSG');
|
||||
data['o'] = ''
|
||||
filename = '/www/server/panel/data/o.pl'
|
||||
if os.path.exists(filename): data['o'] = str(public.readFile(filename))
|
||||
# sUrl = 'https://console.aapanel.com/api/panel/updateLinuxEn'
|
||||
data['o'] = public.get_oem_name()
|
||||
sUrl = '{}/api/panel/updateLinuxEn'.format(self.__official_url)
|
||||
updateInfo = json.loads(public.httpPost(sUrl,data))
|
||||
if not updateInfo: return public.returnMsg(False,"CONNECT_ERR")
|
||||
@@ -1189,7 +1193,7 @@ class ajax:
|
||||
php_path = '/usr/local/lsws/lsphp'
|
||||
php_bin = php_path + php_version + '/bin/php'
|
||||
php_ini = php_path + php_version + '/etc/php.ini'
|
||||
if not os.path.exists('/etc/redhat-release'):
|
||||
if not os.path.exists('/etc/redhat-release') and public.get_webserver() == 'openlitespeed':
|
||||
php_ini = php_path + php_version + '/etc/php/'+args.php_version+'/litespeed/php.ini'
|
||||
tmp = public.ExecShell(php_bin + ' /www/server/panel/class/php_info.php')[0]
|
||||
if tmp.find('Warning: JIT is incompatible') != -1:
|
||||
|
||||
@@ -246,10 +246,30 @@ class apache:
|
||||
return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS')
|
||||
reg = '\s*#LOG_FORMAT_BEGIN_{n}(\n|.)+#LOG_FORMAT_END_{n}\n?'.format(n=args.log_format_name)
|
||||
conf = re.sub(reg,'',conf)
|
||||
self._del_format_log_of_website(args.log_format_name)
|
||||
public.writeFile(self.httpdconf,conf)
|
||||
public.serviceReload()
|
||||
return public.returnMsg(True, 'SET_SUCCESS')
|
||||
|
||||
def _del_format_log_of_website(self,log_format_name):
|
||||
site_format_log_status = self._get_format_log_to_website(log_format_name)
|
||||
try:
|
||||
for s in site_format_log_status.keys():
|
||||
if not site_format_log_status[s]:
|
||||
continue
|
||||
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(s)
|
||||
format_exist_reg = 'CustomLog\s+"/www.*"\s+{}'.format(log_format_name)
|
||||
conf = public.readFile(website_conf_file)
|
||||
if not conf:continue
|
||||
if not re.search(format_exist_reg,conf):continue
|
||||
access_log = re.search(format_exist_reg,conf).group().split()
|
||||
access_log = access_log[0] + ' ' +access_log[1] + 'combined'
|
||||
conf = re.sub(format_exist_reg,access_log,conf)
|
||||
public.writeFile(website_conf_file,conf)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_httpd_access_log_format_parameter(self,args=None):
|
||||
data = {
|
||||
"%h":"Client's IP address",
|
||||
|
||||
+40
-30
@@ -8,6 +8,7 @@
|
||||
# +-------------------------------------------------------------------
|
||||
from BTPanel import session, cache , request, redirect, g
|
||||
from datetime import datetime
|
||||
from public import dict_obj
|
||||
import os
|
||||
import public
|
||||
import json
|
||||
@@ -15,17 +16,6 @@ import sys
|
||||
import time
|
||||
|
||||
|
||||
class dict_obj:
|
||||
def __contains__(self, key):
|
||||
return getattr(self, key, None)
|
||||
|
||||
def __setitem__(self, key, value): setattr(self, key, value)
|
||||
def __getitem__(self, key): return getattr(self, key, None)
|
||||
def __delitem__(self, key): delattr(self, key)
|
||||
def __delattr__(self, key): delattr(self, key)
|
||||
def get_items(self): return self
|
||||
|
||||
|
||||
class panelSetup:
|
||||
def init(self):
|
||||
ua = request.headers.get('User-Agent','')
|
||||
@@ -33,7 +23,7 @@ class panelSetup:
|
||||
ua = ua.lower()
|
||||
if ua.find('spider') != -1 or ua.find('bot') != -1:
|
||||
return redirect('https://www.google.com')
|
||||
g.version = '6.8.8'
|
||||
g.version = '6.8.12'
|
||||
g.title = public.GetConfigValue('title')
|
||||
g.uri = request.path
|
||||
g.debug = os.path.exists('data/debug.pl')
|
||||
@@ -50,22 +40,28 @@ class panelSetup:
|
||||
else:
|
||||
g.cdn_url = '/static'
|
||||
session['title'] = g.title
|
||||
dirPath = '/www/server/phpmyadmin/pma'
|
||||
if os.path.exists(dirPath):
|
||||
public.ExecShell("rm -rf {}".format(dirPath))
|
||||
|
||||
dirPath = '/www/server/adminer'
|
||||
if os.path.exists(dirPath):
|
||||
public.ExecShell("rm -rf {}".format(dirPath))
|
||||
|
||||
dirPath = '/www/server/panel/adminer'
|
||||
if os.path.exists(dirPath):
|
||||
public.ExecShell("rm -rf {}".format(dirPath))
|
||||
|
||||
g.is_aes = False
|
||||
self.other_import()
|
||||
return None
|
||||
|
||||
|
||||
def other_import(self):
|
||||
g.o = public.readFile('data/o.pl')
|
||||
g.other_css = []
|
||||
g.other_js = []
|
||||
if g.o:
|
||||
s_path = 'BTPanel/static/other/{}'
|
||||
css_name = "css/{}.css".format(g.o)
|
||||
css_file = s_path.format(css_name)
|
||||
if os.path.exists(css_file): g.other_css.append('/static/other/{}'.format(css_name))
|
||||
|
||||
js_name = "js/{}.js".format(g.o)
|
||||
js_file = s_path.format(js_name)
|
||||
if os.path.exists(js_file): g.other_js.append('/static/other/{}'.format(js_name))
|
||||
|
||||
|
||||
|
||||
class panelAdmin(panelSetup):
|
||||
setupPath = '/www/server'
|
||||
|
||||
@@ -110,7 +106,7 @@ class panelAdmin(panelSetup):
|
||||
if not 'lan' in session:
|
||||
session['lan'] = public.GetLanguage()
|
||||
if not 'home' in session:
|
||||
session['home'] = 'https://console.aapanel.com'
|
||||
session['home'] = 'https://brandnew.aapanel.com'
|
||||
return False
|
||||
|
||||
# 检查Web服务器类型
|
||||
@@ -276,15 +272,29 @@ class panelAdmin(panelSetup):
|
||||
def GetOS(self):
|
||||
if not 'server_os' in session:
|
||||
tmp = {}
|
||||
if os.path.exists('/etc/redhat-release'):
|
||||
issue_file = '/etc/issue'
|
||||
redhat_release = '/etc/redhat-release'
|
||||
if os.path.exists(redhat_release):
|
||||
tmp['x'] = 'RHEL'
|
||||
tmp['osname'] = public.ReadFile(
|
||||
'/etc/redhat-release').split()[0]
|
||||
tmp['osname'] = self.get_osname(redhat_release)
|
||||
elif os.path.exists('/usr/bin/yum'):
|
||||
tmp['x'] = 'RHEL'
|
||||
tmp['osname'] = public.ReadFile('/etc/issue').split()[0]
|
||||
elif os.path.exists('/etc/issue'):
|
||||
tmp['osname'] = self.get_osname(issue_file)
|
||||
elif os.path.exists(issue_file):
|
||||
tmp['x'] = 'Debian'
|
||||
tmp['osname'] = public.ReadFile('/etc/issue').split()[0]
|
||||
tmp['osname'] = self.get_osname(issue_file)
|
||||
session['server_os'] = tmp
|
||||
return False
|
||||
|
||||
|
||||
def get_osname(self,i_file):
|
||||
'''
|
||||
@name 从指定文件中获取系统名称
|
||||
@author hwliang<2021-04-07>
|
||||
@param i_file<string> 指定文件全路径
|
||||
@return string
|
||||
'''
|
||||
if not os.path.exists(i_file): return ''
|
||||
issue_str = public.ReadFile(i_file).strip()
|
||||
if issue_str: return issue_str.split()[0]
|
||||
return ''
|
||||
|
||||
+151
-8
@@ -21,9 +21,9 @@ class config:
|
||||
_bk_key_file = _setup_path + "/data/bk_two_step_auth.txt"
|
||||
_username_file = _setup_path + "/data/username.txt"
|
||||
_core_fle_path = _setup_path + '/data/qrcode'
|
||||
__mail_config = '/www/server/panel/data/stmp_mail.json'
|
||||
__mail_list_data = '/www/server/panel/data/mail_list.json'
|
||||
__dingding_config = '/www/server/panel/data/dingding.json'
|
||||
__mail_config = _setup_path+'/data/stmp_mail.json'
|
||||
__mail_list_data = _setup_path+'/data/mail_list.json'
|
||||
__dingding_config = _setup_path+'/data/dingding.json'
|
||||
__mail_list = []
|
||||
__weixin_user = []
|
||||
|
||||
@@ -112,7 +112,7 @@ class config:
|
||||
return public.returnMsg(False, 'SEND_FAILED')
|
||||
|
||||
# 查看能使用的告警通道
|
||||
def get_settings(self, get):
|
||||
def get_settings(self, get=None):
|
||||
qq_mail_info = json.loads(public.ReadFile(self.__mail_config))
|
||||
if len(qq_mail_info) == 0:
|
||||
user_mail = False
|
||||
@@ -127,6 +127,7 @@ class config:
|
||||
ret['user_mail'] = {"user_name": user_mail, "mail_list": self.__mail_list,"info":self.get_user_mail(get)}
|
||||
ret['dingding'] = {"dingding": dingding,"info":self.get_dingding(get)}
|
||||
return ret
|
||||
|
||||
# 设置钉钉报警
|
||||
def set_dingding(self, get):
|
||||
if not (hasattr(get, 'url') or hasattr(get, 'atall')):
|
||||
@@ -161,12 +162,12 @@ class config:
|
||||
|
||||
|
||||
def getPanelState(self,get):
|
||||
return os.path.exists('/www/server/panel/data/close.pl')
|
||||
return os.path.exists(self._setup_path+'/data/close.pl')
|
||||
|
||||
def reload_session(self):
|
||||
userInfo = public.M('users').where("id=?",(1,)).field('username,password').find()
|
||||
token = public.Md5(userInfo['username'] + '/' + userInfo['password'])
|
||||
public.writeFile('/www/server/panel/data/login_token.pl',token)
|
||||
public.writeFile(self._setup_path+'/data/login_token.pl',token)
|
||||
|
||||
sess_path = 'data/sess_files'
|
||||
if not os.path.exists(sess_path):
|
||||
@@ -705,7 +706,7 @@ class config:
|
||||
sps = sp.set_lets(get)
|
||||
return sps
|
||||
else:
|
||||
sslConf = '/www/server/panel/data/ssl.pl'
|
||||
sslConf = self._setup_path+'/data/ssl.pl'
|
||||
if os.path.exists(sslConf):
|
||||
public.ExecShell('rm -f ' + sslConf)
|
||||
return public.returnMsg(True,'PANEL_SSL_CLOSE')
|
||||
@@ -1676,9 +1677,151 @@ class config:
|
||||
import file_execute_deny
|
||||
p = file_execute_deny.FileExecuteDeny()
|
||||
return p.del_file_deny(args)
|
||||
#查看告警
|
||||
def get_login_send(self,get):
|
||||
result={}
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
if os.path.exists('/www/server/panel/data/login_send_mail.pl'):
|
||||
result['mail']=True
|
||||
else:
|
||||
result['mail']=False
|
||||
if os.path.exists('/www/server/panel/data/login_send_dingding.pl'):
|
||||
result['dingding']=True
|
||||
else:
|
||||
result['dingding']=False
|
||||
if result['mail'] or result['dingding']:
|
||||
return public.returnMsg(True, result)
|
||||
return public.returnMsg(False, result)
|
||||
|
||||
#设置告警
|
||||
def set_login_send(self,get):
|
||||
type=get.type.strip()
|
||||
if type=='mail':
|
||||
if not os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||||
os.mknod("/www/server/panel/data/login_send_mail.pl")
|
||||
if os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||||
os.remove("/www/server/panel/data/login_send_dingding.pl")
|
||||
return public.returnMsg(True, 'Setup Successfully')
|
||||
elif type=='dingding':
|
||||
if not os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||||
os.mknod("/www/server/panel/data/login_send_dingding.pl")
|
||||
if os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||||
os.remove("/www/server/panel/data/login_send_mail.pl")
|
||||
return public.returnMsg(True, 'Setup Successfully')
|
||||
else:
|
||||
return public.returnMsg(False,'The delivery type is not supported')
|
||||
|
||||
#取消告警
|
||||
def clear_login_send(self,get):
|
||||
type = get.type.strip()
|
||||
if type == 'mail':
|
||||
if os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||||
os.remove("/www/server/panel/data/login_send_mail.pl")
|
||||
return public.returnMsg(True, '取消成功')
|
||||
elif type == 'dingding':
|
||||
if os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||||
os.remove("/www/server/panel/data/login_send_dingding.pl")
|
||||
return public.returnMsg(True, '取消成功')
|
||||
else:
|
||||
return public.returnMsg(False, '不支持该发送类型')
|
||||
|
||||
#告警日志
|
||||
def get_login_log(self,get):
|
||||
public.create_logs()
|
||||
import page
|
||||
page = page.Page()
|
||||
count = public.M('logs2').where('type=?', (u'aapanel login reminder',)).field('log,addtime').count()
|
||||
limit = 7
|
||||
info = {}
|
||||
info['count'] = count
|
||||
info['row'] = limit
|
||||
info['p'] = 1
|
||||
if hasattr(get, 'p'):
|
||||
info['p'] = int(get['p'])
|
||||
info['uri'] = get
|
||||
info['return_js'] = ''
|
||||
if hasattr(get, 'tojs'):
|
||||
info['return_js'] = get.tojs
|
||||
data = {}
|
||||
# 获取分页数据
|
||||
data['page'] = page.GetPage(info, '1,2,3,4,5,8')
|
||||
data['data'] = public.M('logs2').where('type=?', (u'aapanel login reminder',)).field('log,addtime').order('id desc').limit(
|
||||
str(page.SHIFT) + ',' + str(page.ROW)).field('log,addtime').select()
|
||||
return data
|
||||
|
||||
#白名单设置
|
||||
def login_ipwhite(self,get):
|
||||
type=get.type
|
||||
if type=='get':
|
||||
return self.get_login_ipwhite(get)
|
||||
if type=='add':
|
||||
return self.add_login_ipwhite(get)
|
||||
if type=='del':
|
||||
return self.del_login_ipwhite(get)
|
||||
if type=='clear':
|
||||
return self.clear_login_ipwhite(get)
|
||||
|
||||
#查看IP白名单
|
||||
def get_login_ipwhite(self,get):
|
||||
try:
|
||||
path='/www/server/panel/data/send_login_white.json'
|
||||
ip_white=json.loads(public.ReadFile('/www/server/panel/data/send_login_white.json'))
|
||||
if not ip_white:return public.returnMsg(True, [])
|
||||
return public.returnMsg(True, ip_white)
|
||||
except:
|
||||
public.WriteFile(path, '[]')
|
||||
return public.returnMsg(True, [])
|
||||
|
||||
def add_login_ipwhite(self,get):
|
||||
ip=get.ip.strip()
|
||||
try:
|
||||
path = '/www/server/panel/data/send_login_white.json'
|
||||
ip_white = json.loads(public.ReadFile('/www/server/panel/data/send_login_white.json'))
|
||||
if not ip in ip_white:
|
||||
ip_white.append(ip)
|
||||
public.WriteFile(path, json.dumps(ip_white))
|
||||
return public.returnMsg(True, "Add successfully")
|
||||
except:
|
||||
public.WriteFile(path, json.dumps([ip]))
|
||||
return public.returnMsg(True, "Add successfully")
|
||||
|
||||
def del_login_ipwhite(self,get):
|
||||
ip = get.ip.strip()
|
||||
try:
|
||||
path = '/www/server/panel/data/send_login_white.json'
|
||||
ip_white = json.loads(public.ReadFile('/www/server/panel/data/send_login_white.json'))
|
||||
if ip in ip_white:
|
||||
ip_white.remove(ip)
|
||||
public.WriteFile(path, json.dumps(ip_white))
|
||||
return public.returnMsg(True, "Delete successfully")
|
||||
except:
|
||||
public.WriteFile(path, json.dumps([]))
|
||||
return public.returnMsg(True, "Delete successfully")
|
||||
|
||||
def clear_login_ipwhite(self,get):
|
||||
path = '/www/server/panel/data/send_login_white.json'
|
||||
public.WriteFile(path, json.dumps([]))
|
||||
return public.returnMsg(True, "Clear successfully")
|
||||
|
||||
|
||||
def get_panel_ssl_status(self,get):
|
||||
import os
|
||||
if os.path.exists('/www/server/panel/data/ssl.pl'):
|
||||
if os.path.exists(self._setup_path+'/data/ssl.pl'):
|
||||
return public.returnMsg(True,'success')
|
||||
return public.returnMsg(False,'false')
|
||||
|
||||
def set_backup_notification(self,get):
|
||||
import os
|
||||
f = self._setup_path+'/data/send_back_error.pl'
|
||||
if os.path.exists(f):
|
||||
public.ExecShell('rm -f {}'.format(f))
|
||||
return public.returnMsg(True,'Disable successfully')
|
||||
public.writeFile(f,'1')
|
||||
return public.returnMsg(True,'Enable Successfully')
|
||||
|
||||
def get_backup_notification(self,get):
|
||||
import os
|
||||
if os.path.exists(self._setup_path+'/data/send_back_error.pl'):
|
||||
return public.returnMsg(True,'success')
|
||||
return public.returnMsg(False,'false')
|
||||
+39
-20
@@ -10,6 +10,7 @@ import public,db,os,time,re
|
||||
from BTPanel import session,cache
|
||||
class crontab:
|
||||
field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backupTo,sName,sBody,sType,urladdress'
|
||||
field += ",save_local,notice,notice_channel"
|
||||
#取计划任务列表
|
||||
def GetCrontab(self,get):
|
||||
self.checkBackup()
|
||||
@@ -23,8 +24,10 @@ class crontab:
|
||||
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'sBody' TEXT",())
|
||||
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'sType' TEXT",())
|
||||
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'urladdress' TEXT",())
|
||||
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'save_local' INTEGER DEFAULT 0",())
|
||||
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'notice' INTEGER DEFAULT 0",())
|
||||
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'notice_channel' TEXT DEFAULT ''",())
|
||||
cront = public.M('crontab').order("id desc").field(self.field).select()
|
||||
|
||||
data=[]
|
||||
for i in range(len(cront)):
|
||||
tmp = {}
|
||||
@@ -149,9 +152,17 @@ class crontab:
|
||||
cronInfo['backupTo'] = get['backupTo']
|
||||
cronInfo['sBody'] = get['sBody']
|
||||
cronInfo['urladdress'] = get['urladdress']
|
||||
public.M('crontab').where('id=?',(id,)).save('name,type,where1,where_hour,where_minute,save,backupTo,sBody,urladdress',
|
||||
(get['name'],get['type'],get['where1'],get['hour'],get['minute'],get['save'],get['backupTo'],get['sBody'],get['urladdress']))
|
||||
|
||||
columns = 'name,type,where1,where_hour,where_minute,save,backupTo,sBody,urladdress'
|
||||
values = (get['name'],get['type'],get['where1'],get['hour'],
|
||||
get['minute'],get['save'],get['backupTo'],get['sBody']
|
||||
,get['urladdress'])
|
||||
if 'save_local' in get:
|
||||
columns += ",save_local, notice, notice_channel"
|
||||
values = (get['name'],get['type'],get['where1'],get['hour'],
|
||||
get['minute'],get['save'],get['backupTo'],get['sBody'],
|
||||
get['urladdress'],get['save_local'],get["notice"],
|
||||
get["notice_channel"])
|
||||
public.M('crontab').where('id=?',(id,)).save(columns,values)
|
||||
self.remove_for_crond(cronInfo['echo'])
|
||||
self.sync_to_crond(cronInfo)
|
||||
public.WriteLog('TYPE_CRON',"MODIFY_CRON",(cronInfo['name']))
|
||||
@@ -164,8 +175,6 @@ class crontab:
|
||||
data = public.M('crontab').where('id=?',(id,)).field(self.field).find()
|
||||
return data
|
||||
|
||||
|
||||
|
||||
#同步到crond
|
||||
def sync_to_crond(self,cronInfo):
|
||||
if 'status' in cronInfo:
|
||||
@@ -196,10 +205,19 @@ class crontab:
|
||||
wRes = self.WriteShell(cuonConfig)
|
||||
if type(wRes) != bool: return wRes
|
||||
self.CrondReload()
|
||||
addData=public.M('crontab').add(
|
||||
'name,type,where1,where_hour,where_minute,echo,addtime,status,save,backupTo,sType,sName,sBody,urladdress',
|
||||
(get['name'],get['type'],get['where1'],get['hour'],get['minute'],cronName,time.strftime('%Y-%m-%d %X',time.localtime()),1,get['save'],get['backupTo'],get['sType'],get['sName'],get['sBody'],get['urladdress'])
|
||||
)
|
||||
columns = 'name,type,where1,where_hour,where_minute,echo,addtime,\
|
||||
status,save,backupTo,sType,sName,sBody,urladdress'
|
||||
values = (get['name'],get['type'],get['where1'],get['hour'],
|
||||
get['minute'],cronName,time.strftime('%Y-%m-%d %X',time.localtime()),
|
||||
1,get['save'],get['backupTo'],get['sType'],get['sName'],get['sBody'],
|
||||
get['urladdress'])
|
||||
if "save_local" in get:
|
||||
columns += ",save_local,notice,notice_channel"
|
||||
values = (get['name'],get['type'],get['where1'],get['hour'],
|
||||
get['minute'],cronName,time.strftime('%Y-%m-%d %X',time.localtime()),
|
||||
1,get['save'],get['backupTo'],get['sType'],get['sName'],get['sBody'],
|
||||
get['urladdress'], get["save_local"], get['notice'], get['notice_channel'])
|
||||
addData=public.M('crontab').add(columns,values)
|
||||
if addData>0:
|
||||
result = public.returnMsg(True,'ADD_SUCCESS')
|
||||
result['id'] = addData
|
||||
@@ -339,6 +357,10 @@ class crontab:
|
||||
def GetShell(self,param):
|
||||
#try:
|
||||
type=param['sType']
|
||||
if not 'echo' in param:
|
||||
cronName=public.md5(public.md5(str(time.time()) + '_bt'))
|
||||
else:
|
||||
cronName = param['echo']
|
||||
if type=='toFile':
|
||||
shell=param.sFile
|
||||
else :
|
||||
@@ -353,10 +375,11 @@ class crontab:
|
||||
if type in ['site','path'] and param['sBody'] != 'undefined' and len(param['sBody']) > 1:
|
||||
exports = param['sBody'].replace("\r\n","\n").replace("\n",",")
|
||||
head += "BT_EXCLUDE=\"" + exports.strip() + "\"\nexport BT_EXCLUDE\n"
|
||||
attach_param = " " + cronName
|
||||
wheres={
|
||||
'path': head + python_bin +" " + public.GetConfigValue('setup_path')+"/panel/script/backup.py path "+param['sName']+" "+str(param['save']),
|
||||
'site' : head +python_bin+ " " + public.GetConfigValue('setup_path')+"/panel/script/backup.py site "+param['sName']+" "+str(param['save']),
|
||||
'database': head +python_bin+ " " + public.GetConfigValue('setup_path')+"/panel/script/backup.py database "+param['sName']+" "+str(param['save']),
|
||||
'path': head + python_bin +" " + public.GetConfigValue('setup_path')+"/panel/script/backup.py path "+param['sName']+" "+str(param['save'])+attach_param,
|
||||
'site' : head +python_bin+ " " + public.GetConfigValue('setup_path')+"/panel/script/backup.py site "+param['sName']+" "+str(param['save'])+attach_param,
|
||||
'database': head +python_bin+ " " + public.GetConfigValue('setup_path')+"/panel/script/backup.py database "+param['sName']+" "+str(param['save'])+attach_param,
|
||||
'logs' : head +python_bin+ " " + public.GetConfigValue('setup_path')+"/panel/script/logsBackup "+param['sName']+log+" "+str(param['save']),
|
||||
'rememory' : head + "/bin/bash " + public.GetConfigValue('setup_path') + '/panel/script/rememory.sh',
|
||||
'webshell': head +python_bin+ " " + public.GetConfigValue('setup_path') + '/panel/class/webshell_check.py site ' + param['sName'] +' ' +param['urladdress']
|
||||
@@ -365,9 +388,9 @@ class crontab:
|
||||
cfile = public.GetConfigValue('setup_path') + "/panel/plugin/" + param['backupTo'] + "/" + param['backupTo'] + "_main.py"
|
||||
if not os.path.exists(cfile): cfile = public.GetConfigValue('setup_path') + "/panel/script/backup_" + param['backupTo'] + ".py"
|
||||
wheres={
|
||||
'path': head + python_bin+" " + cfile + " path " + param['sName'] + " " + str(param['save']),
|
||||
'site' : head + python_bin+" " + cfile + " site " + param['sName'] + " " + str(param['save']),
|
||||
'database': head + python_bin+" " + cfile + " database " + param['sName'] + " " + str(param['save']),
|
||||
'path': head + python_bin+" " + cfile + " path " + param['sName'] + " " + str(param['save'])+attach_param,
|
||||
'site' : head + python_bin+" " + cfile + " site " + param['sName'] + " " + str(param['save'])+attach_param,
|
||||
'database': head + python_bin+" " + cfile + " database " + param['sName'] + " " + str(param['save'])+attach_param,
|
||||
'logs' : head + python_bin+" " + public.GetConfigValue('setup_path')+"/panel/script/logsBackup "+param['sName']+log+" "+str(param['save']),
|
||||
'rememory' : head + "/bin/bash " + public.GetConfigValue('setup_path') + '/panel/script/rememory.sh',
|
||||
'webshell': head + python_bin+" " + public.GetConfigValue('setup_path') + '/panel/class/webshell_check.py site ' + param['sName'] +' ' +param['urladdress']
|
||||
@@ -389,10 +412,6 @@ echo "--------------------------------------------------------------------------
|
||||
'''
|
||||
cronPath=public.GetConfigValue('setup_path')+'/cron'
|
||||
if not os.path.exists(cronPath): public.ExecShell('mkdir -p ' + cronPath)
|
||||
if not 'echo' in param:
|
||||
cronName=public.md5(public.md5(str(time.time()) + '_bt'))
|
||||
else:
|
||||
cronName = param['echo']
|
||||
file = cronPath+'/' + cronName
|
||||
public.writeFile(file,self.CheckScript(shell))
|
||||
public.ExecShell('chmod 750 ' + file)
|
||||
|
||||
+9
-3
@@ -34,9 +34,11 @@ class database(datatool.datatools):
|
||||
if self.CheckRecycleBin(data_name): return public.returnMsg(False,'DATABASE_DEL_RECYCLE_BIN',(data_name,))
|
||||
if len(data_name) > 64: return public.returnMsg(False, 'DATABASE_NAME_LEN')
|
||||
reg = r"^[\w\.-]+$"
|
||||
if not re.match(reg, data_name): return public.returnMsg(False,'DATABASE_NAME_ERR_T')
|
||||
if not hasattr(get,'db_user'): get.db_user = data_name
|
||||
username = get.db_user.strip()
|
||||
if not re.match(reg, data_name): return public.returnMsg(False,'DATABASE_NAME_ERR_T')
|
||||
if not re.match(reg, username): return public.returnMsg(False,'DATABASE_NAME_ERR')
|
||||
if not hasattr(get,'db_user'): get.db_user = data_name
|
||||
|
||||
checks = ['root','mysql','test','sys','panel_logs']
|
||||
if username in checks or len(username) < 1: return public.returnMsg(False,'DATABASE_USER_NAME_ERR')
|
||||
if data_name in checks or len(data_name) < 1: return public.returnMsg(False,'DATABASE_NAME_ERR')
|
||||
@@ -299,6 +301,8 @@ SetLink
|
||||
find = public.M('databases').where("id=?",(id,)).field('id,pid,name,username,password,accept,ps,addtime').find()
|
||||
accept = find['accept']
|
||||
username = find['username']
|
||||
if "'" in username:
|
||||
username=re.sub("\'","\\'",username)
|
||||
#删除MYSQL
|
||||
result = panelMysql.panelMysql().execute("drop database `" + name + "`")
|
||||
isError=self.IsSqlError(result)
|
||||
@@ -481,7 +485,7 @@ SetLink
|
||||
try:
|
||||
password = public.M('config').where('id=?',(1,)).getField('mysql_root')
|
||||
os.environ["MYSQL_PWD"] = password
|
||||
public.ExecShell("/www/server/mysql/bin/mysqldump -R -E --default-character-set="+ public.get_database_character(name) +" --force --opt \"" + name + "\" -u root | gzip > " + backupName)
|
||||
public.ExecShell("/www/server/mysql/bin/mysqldump -R -E --triggers=false --default-character-set="+ public.get_database_character(name) +" --force --opt \"" + name + "\" -u root | gzip > " + backupName)
|
||||
except Exception as e:
|
||||
raise
|
||||
finally:
|
||||
@@ -748,6 +752,8 @@ SetLink
|
||||
#修改数据库目录
|
||||
def SetDataDir(self,get):
|
||||
if get.datadir[-1] == '/': get.datadir = get.datadir[0:-1]
|
||||
if len(get.datadir) > 32: return public.returnMsg(False,'The data directory length cannot exceed 32 bits')
|
||||
if not re.search(r"^[0-9A-Za-z_/\\]$+",get.datadir): return public.returnMsg(False,'Special symbols cannot be included in the database path')
|
||||
if not os.path.exists(get.datadir): public.ExecShell('mkdir -p ' + get.datadir)
|
||||
mysqlInfo = self.GetMySQLInfo(get)
|
||||
if mysqlInfo['datadir'] == get.datadir: return public.returnMsg(False,'DATABASE_MOVE_RE')
|
||||
|
||||
@@ -125,6 +125,8 @@ class FileExecuteDeny:
|
||||
reg = '\s*#BEGIN_DENY_{n}\n(.|\n)*#END_DENY_{n}\n'.format(n=name)
|
||||
conf = re.sub(reg,'',conf)
|
||||
else:
|
||||
if dir[0] != '/':dir = '/'+dir
|
||||
if dir[-1] != '/':dir = dir+'/'
|
||||
new = '''
|
||||
#BEGIN_DENY_%s
|
||||
location ~* ^%s.*.(%s)$ {
|
||||
@@ -146,9 +148,11 @@ class FileExecuteDeny:
|
||||
reg = '\s*#BEGIN_DENY_{n}\n(.|\n)*#END_DENY_{n}'.format(n=name)
|
||||
conf = re.sub(reg,'',conf)
|
||||
else:
|
||||
if dir[0] != '/':dir = '/'+dir
|
||||
if dir[-1] != '/':dir = dir+'/'
|
||||
new = '''
|
||||
#BEGIN_DENY_{n}
|
||||
<Directory ~ "{d}.*\.(s)$">
|
||||
<Directory ~ "{d}.*\.({s})$">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</Directory>
|
||||
|
||||
+204
-26
@@ -159,6 +159,10 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
text2 = html.escape(str_convert, quote=True)
|
||||
else:
|
||||
text2 = cgi.escape(str_convert, quote=True)
|
||||
|
||||
reps = {'&':'&'}
|
||||
for rep in reps.keys():
|
||||
if text2.find(rep) != -1: text2 = text2.replace(rep,reps[rep])
|
||||
return text2
|
||||
|
||||
# 上传文件
|
||||
@@ -184,6 +188,20 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
(filename, get['path']))
|
||||
return public.returnMsg(True, 'FILE_UPLOAD_SUCCESS')
|
||||
|
||||
def f_name_check(self,filename):
|
||||
'''
|
||||
@name 文件名检测2
|
||||
@author hwliang<2021-03-16>
|
||||
@param filename<string> 文件名
|
||||
@return bool
|
||||
'''
|
||||
f_strs = [';','&','<','>']
|
||||
for fs in f_strs:
|
||||
if filename.find(fs) != -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# 上传文件2
|
||||
def upload(self, args):
|
||||
if not 'f_name' in args:
|
||||
@@ -196,6 +214,9 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
args.f_name = args.f_name.encode('utf-8')
|
||||
args.f_path = args.f_path.encode('utf-8')
|
||||
|
||||
|
||||
if not self.f_name_check(args.f_name): return public.returnMsg(False,'FILE_NAME_ERR')
|
||||
|
||||
if args.f_path == '/':
|
||||
return public.returnMsg(False,'UPLOAD_DIR_ERR')
|
||||
|
||||
@@ -291,7 +312,8 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
if get.path == '':
|
||||
get.path = '/www'
|
||||
if not os.path.exists(get.path):
|
||||
return public.ReturnMsg(False,'DIR_NOT_EXISTS')
|
||||
get.path = '/www/wwwroot'
|
||||
#return public.ReturnMsg(False, '指定目录不存在!')
|
||||
if get.path == '/www/Recycle_bin':
|
||||
return public.returnMsg(False,'RECYCLE_BIN_ERR')
|
||||
if not os.path.isdir(get.path):
|
||||
@@ -670,6 +692,29 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
except:
|
||||
return public.returnMsg(False, 'FILE_CREATE_ERR')
|
||||
|
||||
#创建软链
|
||||
def CreateLink(self,get):
|
||||
'''
|
||||
@name 创建软链接
|
||||
@author hwliang<2021-03-23>
|
||||
@param get<dict_obj{
|
||||
sfile<string> 源文件
|
||||
dfile<string> 软链文件名
|
||||
}>
|
||||
@return dict
|
||||
'''
|
||||
|
||||
if not 'sfile' in get: return public.returnMsg(False,'INIT_ARGS_ERR')
|
||||
if not os.path.exists(get.sfile): return public.returnMsg(False,'FILE_NOT_EXISTS')
|
||||
if os.path.exists(get.dfile): return public.returnMsg(False,'SOFTCHAIN_FILE_EXIST')
|
||||
if get.dfile[0] != '/': return public.returnMsg(False,'SOFTCHAIN_FILE_PATH')
|
||||
public.ExecShell("ln -sf {} {}".format(get.sfile,get.dfile))
|
||||
if not os.path.exists(get.dfile): return public.returnMsg(False,'SOFTLINK_CREATE_ERR')
|
||||
public.WriteLog('TYPE_FIREWALL','CREATE_SOFTLINK',(get.dfile,get.sfile))
|
||||
return public.returnMsg(True,'SOFTLINK_CREATE_SUCCESS')
|
||||
|
||||
|
||||
|
||||
# 创建目录
|
||||
def CreateDir(self, get):
|
||||
if sys.version_info[0] == 2:
|
||||
@@ -828,6 +873,9 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
data['files'].append(tmp)
|
||||
except:
|
||||
continue
|
||||
|
||||
data['dirs'] = sorted(data['dirs'],key = lambda x: x['time'],reverse=True)
|
||||
data['files'] = sorted(data['files'],key = lambda x: x['time'],reverse=True)
|
||||
return data
|
||||
|
||||
# 彻底删除
|
||||
@@ -1494,11 +1542,6 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
os.remove(sfile)
|
||||
return True
|
||||
|
||||
|
||||
#创建软链
|
||||
def create_link(self,args):
|
||||
pass
|
||||
|
||||
# 复制目录
|
||||
def copytree(self, sfile, dfile):
|
||||
if sfile == dfile:
|
||||
@@ -1506,6 +1549,8 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
|
||||
if not os.path.exists(dfile):
|
||||
os.makedirs(dfile)
|
||||
for f_name in os.listdir(sfile):
|
||||
if not f_name.strip(): continue
|
||||
if f_name.find('./') != -1: continue
|
||||
src_filename = (sfile + '/' + f_name).replace('//', '/')
|
||||
dst_filename = (dfile + '/' + f_name).replace('//', '/')
|
||||
mode_info = public.get_mode_and_user(src_filename)
|
||||
@@ -1964,7 +2009,13 @@ cd %s
|
||||
return public.returnMsg(False, 'ADDRESS_NOT_EXIST')
|
||||
pdata = {}
|
||||
if 'expire' in get: pdata['expire'] = get.expire
|
||||
if 'password' in get: pdata['password'] = get.password
|
||||
if 'password' in get:
|
||||
pdata['password'] = get.password
|
||||
if len(pdata['password']) < 4 and len(pdata['password']) > 0:
|
||||
return public.returnMsg(False,'The length of the extracted password cannot be less than 4 digits')
|
||||
if not re.match('^\w+$',pdata['password']):
|
||||
return public.returnMsg(False,'No special symbols can be used in the extracted password')
|
||||
|
||||
if 'ps' in get: pdata['ps'] = get.ps
|
||||
public.M(my_table).where('id=?', (id,)).update(pdata)
|
||||
return public.returnMsg(True, 'EDIT_SUCCESS')
|
||||
@@ -1986,6 +2037,8 @@ cd %s
|
||||
}
|
||||
if len(pdata['password']) < 4 and len(pdata['password']) > 0:
|
||||
return public.returnMsg(False,'PASSWD_ERR')
|
||||
if not re.match('^\w+$',pdata['password']):
|
||||
return public.returnMsg(False,'No special symbols can be used in the extracted password')
|
||||
#更新 or 插入
|
||||
token = public.M(my_table).where('filename=?',(get.filename,)).getField('token')
|
||||
if token:
|
||||
@@ -2002,10 +2055,10 @@ cd %s
|
||||
|
||||
#取PHP-CLI执行命令
|
||||
def __get_php_bin(self,php_version=None):
|
||||
php_vs = ["80","74","73","72","71","70","56","55","54","53","52"]
|
||||
php_vs = ["80","74","73","72","71","70","56","55","54","53"]
|
||||
if php_version:
|
||||
if php_version != 'auto':
|
||||
if not php_version in php_vs: return False
|
||||
if not php_version in php_vs: return ''
|
||||
else:
|
||||
php_version = None
|
||||
|
||||
@@ -2020,9 +2073,9 @@ cd %s
|
||||
php_v = pv
|
||||
break
|
||||
# 如果没安装直接返回False
|
||||
if not php_v: return False
|
||||
# 处理PHP-CLI-INI配置文件
|
||||
php_ini = '/tmp/composer_php_cli_' + php_v + '.ini'
|
||||
if not php_v: return ''
|
||||
#处理PHP-CLI-INI配置文件
|
||||
php_ini = '/www/server/panel/tmp/composer_php_cli_'+php_v+'.ini'
|
||||
if not os.path.exists(php_ini):
|
||||
# 如果不存在,则从PHP安装目录下复制一份
|
||||
src_php_ini = php_path + php_v + '/etc/php.ini'
|
||||
@@ -2045,8 +2098,12 @@ cd %s
|
||||
# 安装composer
|
||||
def get_composer_bin(self):
|
||||
composer_bin = '/usr/bin/composer'
|
||||
download_addr = 'wget -O {} {}/install/src/composer.phper -T 5'.format(composer_bin,public.get_url())
|
||||
if not os.path.exists(composer_bin):
|
||||
public.ExecShell('wget -O {} {}/install/src/composer.phper -T 5'.format(composer_bin,public.get_url()))
|
||||
public.ExecShell(download_addr)
|
||||
elif os.path.getsize(composer_bin) < 100:
|
||||
public.ExecShell(download_addr)
|
||||
|
||||
public.ExecShell('chmod +x {}'.format(composer_bin))
|
||||
if not os.path.exists(composer_bin):
|
||||
return False
|
||||
@@ -2068,16 +2125,28 @@ cd %s
|
||||
return public.returnMsg(False,'PHP_VER_NOT_FOUND')
|
||||
if not os.path.exists(get.path + '/composer.json'):
|
||||
return public.returnMsg(False,'COMPOSER_CONF_NOT_FOUND')
|
||||
log_file = '/tmp/composer.log'
|
||||
user = ''
|
||||
if 'user' in get:
|
||||
user = 'sudo -u {} '.format(get.user)
|
||||
if not os.path.exists('/usr/bin/sudo'):
|
||||
if os.path.exists('/usr/bin/apt'):
|
||||
public.ExecShell("apt install sudo -y > {}".format(log_file))
|
||||
else:
|
||||
public.ExecShell("yum install sudo -y > {}".format(log_file))
|
||||
public.ExecShell("mkdir -p /home/www && chown -R www:www /home/www")
|
||||
|
||||
#设置指定源
|
||||
if 'repo' in get:
|
||||
if get.repo != 'repos.packagist':
|
||||
public.ExecShell('{} {} config -g repo.packagist composer {}'.format(php_bin,composer_bin,get.repo))
|
||||
public.ExecShell('export COMPOSER_HOME=/tmp && {}{} {} config -g repo.packagist composer {}'.format(user,php_bin,composer_bin,get.repo))
|
||||
else:
|
||||
public.ExecShell('{} {} config -g --unset repos.packagist'.format(php_bin,composer_bin))
|
||||
public.ExecShell('export COMPOSER_HOME=/tmp && {}{} {} config -g --unset repos.packagist'.format(user,php_bin,composer_bin))
|
||||
#执行composer命令
|
||||
composer_exec_str = '{} {} {} -vvv'.format(php_bin,composer_bin,get.composer_args)
|
||||
public.ExecShell("cd {} && nohup {} &> /tmp/panelExec.pl &".format(get.path,composer_exec_str))
|
||||
public.WriteLog('Composer',composer_exec_str)
|
||||
if os.path.exists(log_file): os.remove(log_file)
|
||||
public.ExecShell("cd {} && export COMPOSER_HOME=/tmp && {} nohup {} &> {} && echo 'BT-Exec-Completed' >> {} && rm -rf /home/www &".format(get.path,user,composer_exec_str,log_file,log_file))
|
||||
public.WriteLog('Composer',"EXEC_COMPOSER",(get.path,get.composer_args))
|
||||
return public.returnMsg(True,'COMMAND_SENT')
|
||||
|
||||
# 取composer版本
|
||||
@@ -2089,13 +2158,22 @@ cd %s
|
||||
try:
|
||||
bs = str(public.readFile(composer_bin,'rb'))
|
||||
result = re.findall(r"const VERSION\s*=\s*.{0,2}'([\d\.]+)",bs)[0]
|
||||
if not result: raise Exception('empty!')
|
||||
except:
|
||||
php_bin = self.__get_php_bin()
|
||||
composer_exec_str = php_bin + ' ' + composer_bin +' --version 2>/dev/null|grep \'Composer version\'|awk \'{print $3}\''
|
||||
if not php_bin: return public.returnMsg(False,'No available PHP version found!')
|
||||
composer_exec_str = 'export COMPOSER_HOME=/tmp && ' + php_bin + ' ' + composer_bin +' --version 2>/dev/null|grep \'Composer version\'|awk \'{print $3}\''
|
||||
result = public.ExecShell(composer_exec_str)[0].strip()
|
||||
data = public.returnMsg(True,result)
|
||||
import panelSite
|
||||
data['php_versions'] = panelSite.panelSite().GetPHPVersion(get)
|
||||
if 'path' in get:
|
||||
import panelSite
|
||||
data['php_versions'] = panelSite.panelSite().GetPHPVersion(get)
|
||||
data['comp_json'] = True
|
||||
data['comp_lock'] = False
|
||||
if not os.path.exists(get.path + '/composer.json'):
|
||||
data['comp_json'] = public.getMsg('COMPOSER_CONF_NOT_EXIST')
|
||||
if os.path.exists(get.path + '/composer.lock'):
|
||||
data['comp_lock'] = public.getMsg('COMPOSERLOGCK_EXIST')
|
||||
return data
|
||||
|
||||
# 升级composer版本
|
||||
@@ -2104,15 +2182,15 @@ cd %s
|
||||
if not composer_bin:
|
||||
return public.returnMsg(False,'NO_COMPOSER_AVAILABLE')
|
||||
php_bin = self.__get_php_bin()
|
||||
|
||||
if not php_bin: return public.returnMsg(False,'No available PHP version found!')
|
||||
#设置指定源
|
||||
if 'repo' in get:
|
||||
if get.repo:
|
||||
public.ExecShell('{} {} config -g repo.packagist composer {}'.format(php_bin,composer_bin,get.repo))
|
||||
# if 'repo' in get:
|
||||
# if get.repo:
|
||||
# public.ExecShell('{} {} config -g repo.packagist composer {}'.format(php_bin,composer_bin,get.repo))
|
||||
|
||||
version1 = self.get_composer_version(get)['msg']
|
||||
composer_exec_str = '{} {} self-update -vvv'.format(php_bin,composer_bin)
|
||||
public.ExecShell(composer_exec_str)[0]
|
||||
composer_exec_str = 'export COMPOSER_HOME=/tmp && {} {} self-update -vvv'.format(php_bin,composer_bin)
|
||||
public.ExecShell(composer_exec_str)
|
||||
version2 = self.get_composer_version(get)['msg']
|
||||
if version1 == version2:
|
||||
msg = public.getMsg("COMPOSER_UPDATE_ERR")
|
||||
@@ -2121,6 +2199,106 @@ cd %s
|
||||
public.WriteLog('Composer',msg)
|
||||
return public.returnMsg(True,msg)
|
||||
|
||||
# 计算文件HASH
|
||||
def get_file_hash(self,args=None,filename=None):
|
||||
if not filename: filename = args.filename
|
||||
import hashlib
|
||||
md5_obj = hashlib.md5()
|
||||
sha1_obj = hashlib.sha1()
|
||||
f = open(filename,'rb')
|
||||
while True:
|
||||
b = f.read(8096)
|
||||
if not b :
|
||||
break
|
||||
md5_obj.update(b)
|
||||
sha1_obj.update(b)
|
||||
f.close()
|
||||
return {'md5':md5_obj.hexdigest(),'sha1':sha1_obj.hexdigest()}
|
||||
|
||||
|
||||
# 取历史副本
|
||||
def get_history_info(self, filename):
|
||||
try:
|
||||
save_path = ('/www/backup/file_history/' +
|
||||
filename).replace('//', '/')
|
||||
if not os.path.exists(save_path):
|
||||
return []
|
||||
result = []
|
||||
for f in sorted(os.listdir(save_path)):
|
||||
f_name = (save_path + '/' + f).replace('//', '/')
|
||||
pdata = {}
|
||||
pdata['md5'] = public.FileMd5(f_name)
|
||||
f_stat = os.stat(f_name)
|
||||
pdata['st_mtime'] = int(f)
|
||||
pdata['st_size'] = f_stat.st_size
|
||||
pdata['history_file'] = f_name
|
||||
result.append(pdata)
|
||||
return result
|
||||
except:
|
||||
return []
|
||||
|
||||
#获取文件扩展名
|
||||
def get_file_ext(self,filename):
|
||||
ss_exts = ['tar.gz','tar.bz2','tar.bz']
|
||||
for s in ss_exts:
|
||||
e_len = len(s)
|
||||
f_len = len(filename)
|
||||
if f_len < e_len: continue
|
||||
if filename[-e_len:] == s:
|
||||
return s
|
||||
if filename.find('.') == -1: return ''
|
||||
return filename.split('.')[-1]
|
||||
|
||||
|
||||
# 取所属用户或组
|
||||
def get_mode_user(self,uid):
|
||||
import pwd
|
||||
try:
|
||||
return pwd.getpwuid(uid).pw_name
|
||||
except:
|
||||
return uid
|
||||
|
||||
|
||||
# 取指定文件属性
|
||||
def get_file_attribute(self,args):
|
||||
filename = args.filename.strip()
|
||||
if not os.path.exists(filename):
|
||||
return public.returnMsg(False,'the specified file does not exist!')
|
||||
attribute = {}
|
||||
attribute['name'] = os.path.basename(filename)
|
||||
attribute['path'] = os.path.dirname(filename)
|
||||
f_stat = os.stat(filename)
|
||||
attribute['st_atime'] = int(f_stat.st_atime) # 最后访问时间
|
||||
attribute['st_mtime'] = int(f_stat.st_mtime) # 最后修改时间
|
||||
attribute['st_ctime'] = int(f_stat.st_ctime) # 元数据修改时间/权限或数据者变更时间
|
||||
attribute['st_size'] = f_stat.st_size # 文件大小(bytes)
|
||||
attribute['st_gid'] = f_stat.st_gid # 用户组id
|
||||
attribute['st_uid'] = f_stat.st_uid # 用户id
|
||||
attribute['st_nlink'] = f_stat.st_nlink # inode 的链接数
|
||||
attribute['st_ino'] = f_stat.st_ino # inode 的节点号
|
||||
attribute['st_mode'] = f_stat.st_mode # inode 保护模式
|
||||
attribute['st_dev'] = f_stat.st_dev # inode 驻留设备
|
||||
attribute['user'] = self.get_mode_user(f_stat.st_uid) # 所属用户
|
||||
attribute['group'] = self.get_mode_user(f_stat.st_gid) # 所属组
|
||||
attribute['mode'] = str(oct(f_stat.st_mode)[-3:]) # 文件权限号
|
||||
attribute['md5'] = 'Do not count files or directories larger than 100MB' # 文件MD5
|
||||
attribute['sha1'] = 'Do not count files or directories larger than 100MB' # 文件sha1
|
||||
attribute['is_dir'] = os.path.isdir(filename) # 是否为目录
|
||||
attribute['is_link'] = os.path.islink(filename) # 是否为链接文件
|
||||
if attribute['is_link']:
|
||||
attribute['st_type'] = 'Link file'
|
||||
elif attribute['is_dir']:
|
||||
attribute['st_type'] = 'Dir'
|
||||
else:
|
||||
attribute['st_type'] = self.get_file_ext(filename)
|
||||
attribute['history'] = []
|
||||
if f_stat.st_size < 104857600 and not attribute['is_dir']:
|
||||
hash_info = self.get_file_hash(filename=filename)
|
||||
attribute['md5'] = hash_info['md5']
|
||||
attribute['sha1'] = hash_info['sha1']
|
||||
attribute['history'] = self.get_history_info(filename) # 历史文件
|
||||
return attribute
|
||||
|
||||
# 数据库对象
|
||||
def _get_sqlite_connect(self):
|
||||
try:
|
||||
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
from gzip import GzipFile
|
||||
from io import BytesIO
|
||||
|
||||
from flask import request, current_app
|
||||
from flask import request, current_app,session,Response
|
||||
|
||||
|
||||
if sys.version_info[:2] == (2, 6):
|
||||
@@ -78,7 +78,7 @@ class Compress(object):
|
||||
def after_request(self, response):
|
||||
app = self.app or current_app
|
||||
accept_encoding = request.headers.get('Accept-Encoding', '')
|
||||
|
||||
response.headers['Server'] = 'nginx'
|
||||
if (response.mimetype not in app.config['COMPRESS_MIMETYPES'] or
|
||||
'gzip' not in accept_encoding.lower() or
|
||||
not 200 <= response.status_code < 300 or
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flaskext.session
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Adds server session support to your application.
|
||||
|
||||
:copyright: (c) 2014 by Shipeng Feng.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
__version__ = '0.3.0'
|
||||
|
||||
import os
|
||||
|
||||
from .sessions import NullSessionInterface, RedisSessionInterface, \
|
||||
MemcachedSessionInterface, FileSystemSessionInterface, \
|
||||
MongoDBSessionInterface, SqlAlchemySessionInterface
|
||||
|
||||
|
||||
class Session(object):
|
||||
"""This class is used to add Server-side Session to one or more Flask
|
||||
applications.
|
||||
|
||||
There are two usage modes. One is initialize the instance with a very
|
||||
specific Flask application::
|
||||
|
||||
app = Flask(__name__)
|
||||
Session(app)
|
||||
|
||||
The second possibility is to create the object once and configure the
|
||||
application later::
|
||||
|
||||
sess = Session()
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
sess.init_app(app)
|
||||
return app
|
||||
|
||||
By default Flask-Session will use :class:`NullSessionInterface`, you
|
||||
really should configurate your app to use a different SessionInterface.
|
||||
|
||||
.. note::
|
||||
|
||||
You can not use ``Session`` instance directly, what ``Session`` does
|
||||
is just change the :attr:`~flask.Flask.session_interface` attribute on
|
||||
your Flask applications.
|
||||
"""
|
||||
|
||||
def __init__(self, app=None):
|
||||
self.app = app
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app):
|
||||
"""This is used to set up session for your app object.
|
||||
|
||||
:param app: the Flask app object with proper configuration.
|
||||
"""
|
||||
app.session_interface = self._get_interface(app)
|
||||
|
||||
def _get_interface(self, app):
|
||||
config = app.config.copy()
|
||||
config.setdefault('SESSION_TYPE', 'null')
|
||||
config.setdefault('SESSION_PERMANENT', True)
|
||||
config.setdefault('SESSION_USE_SIGNER', False)
|
||||
config.setdefault('SESSION_KEY_PREFIX', 'session:')
|
||||
config.setdefault('SESSION_REDIS', None)
|
||||
config.setdefault('SESSION_MEMCACHED', None)
|
||||
config.setdefault('SESSION_FILE_DIR',
|
||||
os.path.join(os.getcwd(), 'flask_sessionstore'))
|
||||
config.setdefault('SESSION_FILE_THRESHOLD', 500)
|
||||
config.setdefault('SESSION_FILE_MODE', 384)
|
||||
config.setdefault('SESSION_MONGODB', None)
|
||||
config.setdefault('SESSION_MONGODB_DB', 'flask_sessionstore')
|
||||
config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
|
||||
config.setdefault('SESSION_SQLALCHEMY', None)
|
||||
config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')
|
||||
|
||||
if config['SESSION_TYPE'] == 'redis':
|
||||
session_interface = RedisSessionInterface(
|
||||
config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
|
||||
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
|
||||
elif config['SESSION_TYPE'] == 'memcached':
|
||||
session_interface = MemcachedSessionInterface(
|
||||
config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
|
||||
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
|
||||
elif config['SESSION_TYPE'] == 'filesystem':
|
||||
session_interface = FileSystemSessionInterface(
|
||||
config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
|
||||
config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
|
||||
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
|
||||
elif config['SESSION_TYPE'] == 'mongodb':
|
||||
session_interface = MongoDBSessionInterface(
|
||||
config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
|
||||
config['SESSION_MONGODB_COLLECT'],
|
||||
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
|
||||
config['SESSION_PERMANENT'])
|
||||
elif config['SESSION_TYPE'] == 'sqlalchemy':
|
||||
session_interface = SqlAlchemySessionInterface(
|
||||
app, config['SESSION_SQLALCHEMY'],
|
||||
config['SESSION_SQLALCHEMY_TABLE'],
|
||||
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
|
||||
config['SESSION_PERMANENT'])
|
||||
else:
|
||||
session_interface = NullSessionInterface()
|
||||
|
||||
return session_interface
|
||||
@@ -0,0 +1,563 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flaskext.session.sessions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Server-side Sessions and SessionInterfaces.
|
||||
|
||||
:copyright: (c) 2014 by Shipeng Feng.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
from flask.sessions import SessionInterface as FlaskSessionInterface
|
||||
from flask.sessions import SessionMixin
|
||||
from werkzeug.datastructures import CallbackDict
|
||||
from itsdangerous import Signer, BadSignature, want_bytes
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
if not PY2:
|
||||
text_type = str
|
||||
else:
|
||||
text_type = unicode
|
||||
|
||||
|
||||
def total_seconds(td):
|
||||
return td.days * 60 * 60 * 24 + td.seconds
|
||||
|
||||
|
||||
class ServerSideSession(CallbackDict, SessionMixin):
|
||||
"""Baseclass for server-side based sessions."""
|
||||
|
||||
def __init__(self, initial=None, sid=None, permanent=None):
|
||||
def on_update(self):
|
||||
self.modified = True
|
||||
CallbackDict.__init__(self, initial, on_update)
|
||||
self.sid = sid
|
||||
if permanent:
|
||||
self.permanent = permanent
|
||||
self.modified = False
|
||||
|
||||
|
||||
class RedisSession(ServerSideSession):
|
||||
pass
|
||||
|
||||
|
||||
class MemcachedSession(ServerSideSession):
|
||||
pass
|
||||
|
||||
|
||||
class FileSystemSession(ServerSideSession):
|
||||
pass
|
||||
|
||||
|
||||
class MongoDBSession(ServerSideSession):
|
||||
pass
|
||||
|
||||
|
||||
class SqlAlchemySession(ServerSideSession):
|
||||
pass
|
||||
|
||||
|
||||
class SessionInterface(FlaskSessionInterface):
|
||||
|
||||
def _generate_sid(self):
|
||||
return str(uuid4())
|
||||
|
||||
def _get_signer(self, app):
|
||||
if not app.secret_key:
|
||||
return None
|
||||
return Signer(app.secret_key, salt='flask-sessions',
|
||||
key_derivation='hmac')
|
||||
|
||||
|
||||
class NullSessionInterface(SessionInterface):
|
||||
"""Used to open a :class:`flask.sessions.NullSession` instance.
|
||||
"""
|
||||
|
||||
def open_session(self, app, request):
|
||||
return None
|
||||
|
||||
|
||||
class RedisSessionInterface(SessionInterface):
|
||||
"""Uses the Redis key-value store as a session backend.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
The `use_signer` parameter was added.
|
||||
|
||||
:param redis: A ``redis.Redis`` instance.
|
||||
:param key_prefix: A prefix that is added to all Redis store keys.
|
||||
:param use_signer: Whether to sign the session id cookie or not.
|
||||
:param permanent: Whether to use permanent session or not.
|
||||
"""
|
||||
|
||||
serializer = pickle
|
||||
session_class = RedisSession
|
||||
|
||||
def __init__(self, redis, key_prefix, use_signer=False, permanent=True):
|
||||
if redis is None:
|
||||
from redis import Redis
|
||||
redis = Redis()
|
||||
self.redis = redis
|
||||
self.key_prefix = key_prefix
|
||||
self.use_signer = use_signer
|
||||
self.permanent = permanent
|
||||
|
||||
def open_session(self, app, request):
|
||||
sid = request.cookies.get(app.session_cookie_name)
|
||||
if not sid:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
if self.use_signer:
|
||||
signer = self._get_signer(app)
|
||||
if signer is None:
|
||||
return None
|
||||
try:
|
||||
sid_as_bytes = signer.unsign(sid)
|
||||
sid = sid_as_bytes.decode()
|
||||
except BadSignature:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
if not PY2 and not isinstance(sid, text_type):
|
||||
sid = sid.decode('utf-8', 'strict')
|
||||
val = self.redis.get(self.key_prefix + sid)
|
||||
if val is not None:
|
||||
try:
|
||||
data = self.serializer.loads(val)
|
||||
return self.session_class(data, sid=sid)
|
||||
except:
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
if not session:
|
||||
if session.modified:
|
||||
self.redis.delete(self.key_prefix + session.sid)
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain, path=path)
|
||||
return
|
||||
|
||||
# Modification case. There are upsides and downsides to
|
||||
# emitting a set-cookie header each request. The behavior
|
||||
# is controlled by the :meth:`should_set_cookie` method
|
||||
# which performs a quick check to figure out if the cookie
|
||||
# should be set or not. This is controlled by the
|
||||
# SESSION_REFRESH_EACH_REQUEST config flag as well as
|
||||
# the permanent flag on the session itself.
|
||||
# if not self.should_set_cookie(app, session):
|
||||
# return
|
||||
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
val = self.serializer.dumps(dict(session))
|
||||
self.redis.setex(name=self.key_prefix + session.sid, value=val,
|
||||
time=total_seconds(app.permanent_session_lifetime))
|
||||
if self.use_signer:
|
||||
session_id = self._get_signer(app).sign(want_bytes(session.sid))
|
||||
else:
|
||||
session_id = session.sid
|
||||
response.set_cookie(app.session_cookie_name, session_id,
|
||||
expires=expires, httponly=httponly,
|
||||
domain=domain, path=path, secure=secure)
|
||||
|
||||
|
||||
class MemcachedSessionInterface(SessionInterface):
|
||||
"""A Session interface that uses memcached as backend.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
The `use_signer` parameter was added.
|
||||
|
||||
:param client: A ``memcache.Client`` instance.
|
||||
:param key_prefix: A prefix that is added to all Memcached store keys.
|
||||
:param use_signer: Whether to sign the session id cookie or not.
|
||||
:param permanent: Whether to use permanent session or not.
|
||||
"""
|
||||
|
||||
serializer = pickle
|
||||
session_class = MemcachedSession
|
||||
|
||||
def __init__(self, client, key_prefix, use_signer=False, permanent=True):
|
||||
if client is None:
|
||||
client = self._get_preferred_memcache_client()
|
||||
if client is None:
|
||||
raise RuntimeError('no memcache module found')
|
||||
self.client = client
|
||||
self.key_prefix = key_prefix
|
||||
self.use_signer = use_signer
|
||||
self.permanent = permanent
|
||||
|
||||
def _get_preferred_memcache_client(self):
|
||||
servers = ['127.0.0.1:11211']
|
||||
try:
|
||||
import pylibmc
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
return pylibmc.Client(servers)
|
||||
|
||||
try:
|
||||
import memcache
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
return memcache.Client(servers)
|
||||
|
||||
def _get_memcache_timeout(self, timeout):
|
||||
"""
|
||||
Memcached deals with long (> 30 days) timeouts in a special
|
||||
way. Call this function to obtain a safe value for your timeout.
|
||||
"""
|
||||
if timeout > 2592000: # 60*60*24*30, 30 days
|
||||
# See http://code.google.com/p/memcached/wiki/FAQ
|
||||
# "You can set expire times up to 30 days in the future. After that
|
||||
# memcached interprets it as a date, and will expire the item after
|
||||
# said date. This is a simple (but obscure) mechanic."
|
||||
#
|
||||
# This means that we have to switch to absolute timestamps.
|
||||
timeout += int(time.time())
|
||||
return timeout
|
||||
|
||||
def open_session(self, app, request):
|
||||
sid = request.cookies.get(app.session_cookie_name)
|
||||
if not sid:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
if self.use_signer:
|
||||
signer = self._get_signer(app)
|
||||
if signer is None:
|
||||
return None
|
||||
try:
|
||||
sid_as_bytes = signer.unsign(sid)
|
||||
sid = sid_as_bytes.decode()
|
||||
except BadSignature:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
full_session_key = self.key_prefix + sid
|
||||
if PY2 and isinstance(full_session_key, unicode):
|
||||
full_session_key = full_session_key.encode('utf-8')
|
||||
val = self.client.get(full_session_key)
|
||||
if val is not None:
|
||||
try:
|
||||
if not PY2:
|
||||
val = want_bytes(val)
|
||||
data = self.serializer.loads(val)
|
||||
return self.session_class(data, sid=sid)
|
||||
except:
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
full_session_key = self.key_prefix + session.sid
|
||||
if PY2 and isinstance(full_session_key, unicode):
|
||||
full_session_key = full_session_key.encode('utf-8')
|
||||
if not session:
|
||||
if session.modified:
|
||||
self.client.delete(full_session_key)
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain, path=path)
|
||||
return
|
||||
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
if not PY2:
|
||||
val = self.serializer.dumps(dict(session), 0)
|
||||
else:
|
||||
val = self.serializer.dumps(dict(session))
|
||||
self.client.set(full_session_key, val, self._get_memcache_timeout(
|
||||
total_seconds(app.permanent_session_lifetime)))
|
||||
if self.use_signer:
|
||||
session_id = self._get_signer(app).sign(want_bytes(session.sid))
|
||||
else:
|
||||
session_id = session.sid
|
||||
response.set_cookie(app.session_cookie_name, session_id,
|
||||
expires=expires, httponly=httponly,
|
||||
domain=domain, path=path, secure=secure)
|
||||
|
||||
|
||||
class FileSystemSessionInterface(SessionInterface):
|
||||
"""Uses the :class:`werkzeug.contrib.cache.FileSystemCache` as a session
|
||||
backend.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
The `use_signer` parameter was added.
|
||||
|
||||
:param cache_dir: the directory where session files are stored.
|
||||
:param threshold: the maximum number of items the session stores before it
|
||||
starts deleting some.
|
||||
:param mode: the file mode wanted for the session files, default 0600
|
||||
:param key_prefix: A prefix that is added to FileSystemCache store keys.
|
||||
:param use_signer: Whether to sign the session id cookie or not.
|
||||
:param permanent: Whether to use permanent session or not.
|
||||
"""
|
||||
|
||||
session_class = FileSystemSession
|
||||
|
||||
def __init__(self, cache_dir, threshold, mode, key_prefix,
|
||||
use_signer=False, permanent=True):
|
||||
from werkzeug.contrib.cache import FileSystemCache
|
||||
self.cache = FileSystemCache(cache_dir, threshold=threshold, mode=mode)
|
||||
self.key_prefix = key_prefix
|
||||
self.use_signer = use_signer
|
||||
self.permanent = permanent
|
||||
|
||||
def open_session(self, app, request):
|
||||
sid = request.cookies.get(app.session_cookie_name)
|
||||
if not sid:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
if self.use_signer:
|
||||
signer = self._get_signer(app)
|
||||
if signer is None:
|
||||
return None
|
||||
try:
|
||||
sid_as_bytes = signer.unsign(sid)
|
||||
sid = sid_as_bytes.decode()
|
||||
except BadSignature:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
data = self.cache.get(self.key_prefix + sid)
|
||||
if data is not None:
|
||||
return self.session_class(data, sid=sid)
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
if not session:
|
||||
if session.modified:
|
||||
self.cache.delete(self.key_prefix + session.sid)
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain, path=path)
|
||||
return
|
||||
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
data = dict(session)
|
||||
self.cache.set(self.key_prefix + session.sid, data,
|
||||
total_seconds(app.permanent_session_lifetime))
|
||||
if self.use_signer:
|
||||
session_id = self._get_signer(app).sign(want_bytes(session.sid))
|
||||
else:
|
||||
session_id = session.sid
|
||||
response.set_cookie(app.session_cookie_name, session_id,
|
||||
expires=expires, httponly=httponly,
|
||||
domain=domain, path=path, secure=secure)
|
||||
|
||||
|
||||
class MongoDBSessionInterface(SessionInterface):
|
||||
"""A Session interface that uses mongodb as backend.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
The `use_signer` parameter was added.
|
||||
|
||||
:param client: A ``pymongo.MongoClient`` instance.
|
||||
:param db: The database you want to use.
|
||||
:param collection: The collection you want to use.
|
||||
:param key_prefix: A prefix that is added to all MongoDB store keys.
|
||||
:param use_signer: Whether to sign the session id cookie or not.
|
||||
:param permanent: Whether to use permanent session or not.
|
||||
"""
|
||||
|
||||
serializer = pickle
|
||||
session_class = MongoDBSession
|
||||
|
||||
def __init__(self, client, db, collection, key_prefix, use_signer=False,
|
||||
permanent=True):
|
||||
if client is None:
|
||||
from pymongo import MongoClient
|
||||
client = MongoClient()
|
||||
self.client = client
|
||||
self.store = client[db][collection]
|
||||
self.key_prefix = key_prefix
|
||||
self.use_signer = use_signer
|
||||
self.permanent = permanent
|
||||
|
||||
def open_session(self, app, request):
|
||||
sid = request.cookies.get(app.session_cookie_name)
|
||||
if not sid:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
if self.use_signer:
|
||||
signer = self._get_signer(app)
|
||||
if signer is None:
|
||||
return None
|
||||
try:
|
||||
sid_as_bytes = signer.unsign(sid)
|
||||
sid = sid_as_bytes.decode()
|
||||
except BadSignature:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
store_id = self.key_prefix + sid
|
||||
document = self.store.find_one({'id': store_id})
|
||||
if document and document.get('expiration') <= datetime.utcnow():
|
||||
# Delete expired session
|
||||
self.store.remove({'id': store_id})
|
||||
document = None
|
||||
if document is not None:
|
||||
try:
|
||||
val = document['val']
|
||||
data = self.serializer.loads(want_bytes(val))
|
||||
return self.session_class(data, sid=sid)
|
||||
except:
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
store_id = self.key_prefix + session.sid
|
||||
if not session:
|
||||
if session.modified:
|
||||
self.store.remove({'id': store_id})
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain, path=path)
|
||||
return
|
||||
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
val = self.serializer.dumps(dict(session))
|
||||
self.store.update({'id': store_id},
|
||||
{'id': store_id,
|
||||
'val': val,
|
||||
'expiration': expires}, True)
|
||||
if self.use_signer:
|
||||
session_id = self._get_signer(app).sign(want_bytes(session.sid))
|
||||
else:
|
||||
session_id = session.sid
|
||||
response.set_cookie(app.session_cookie_name, session_id,
|
||||
expires=expires, httponly=httponly,
|
||||
domain=domain, path=path, secure=secure)
|
||||
|
||||
|
||||
class SqlAlchemySessionInterface(SessionInterface):
|
||||
"""Uses the Flask-SQLAlchemy from a flask app as a session backend.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
|
||||
:param app: A Flask app instance.
|
||||
:param db: A Flask-SQLAlchemy instance.
|
||||
:param table: The table name you want to use.
|
||||
:param key_prefix: A prefix that is added to all store keys.
|
||||
:param use_signer: Whether to sign the session id cookie or not.
|
||||
:param permanent: Whether to use permanent session or not.
|
||||
"""
|
||||
|
||||
serializer = pickle
|
||||
session_class = SqlAlchemySession
|
||||
|
||||
def __init__(self, app, db, table, key_prefix, use_signer=False,
|
||||
permanent=True):
|
||||
if db is None:
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
db = SQLAlchemy(app)
|
||||
self.db = db
|
||||
self.key_prefix = key_prefix
|
||||
self.use_signer = use_signer
|
||||
self.permanent = permanent
|
||||
|
||||
class Session(self.db.Model):
|
||||
__tablename__ = table
|
||||
|
||||
id = self.db.Column(self.db.Integer, primary_key=True)
|
||||
session_id = self.db.Column(self.db.String(256), unique=True)
|
||||
data = self.db.Column(self.db.LargeBinary)
|
||||
expiry = self.db.Column(self.db.DateTime)
|
||||
|
||||
def __init__(self, session_id, data, expiry):
|
||||
self.session_id = session_id
|
||||
self.data = data
|
||||
self.expiry = expiry
|
||||
|
||||
def __repr__(self):
|
||||
return '<Session data %s>' % self.data
|
||||
|
||||
self.db.create_all()
|
||||
self.sql_session_model = Session
|
||||
|
||||
def open_session(self, app, request):
|
||||
sid = request.cookies.get(app.session_cookie_name)
|
||||
if not sid:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
if self.use_signer:
|
||||
signer = self._get_signer(app)
|
||||
if signer is None:
|
||||
return None
|
||||
try:
|
||||
sid_as_bytes = signer.unsign(sid)
|
||||
sid = sid_as_bytes.decode()
|
||||
except BadSignature:
|
||||
sid = self._generate_sid()
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
store_id = self.key_prefix + sid
|
||||
saved_session = self.sql_session_model.query.filter_by(
|
||||
session_id=store_id).first()
|
||||
if saved_session and saved_session.expiry <= datetime.utcnow():
|
||||
# Delete expired session
|
||||
self.db.session.delete(saved_session)
|
||||
self.db.session.commit()
|
||||
saved_session = None
|
||||
if saved_session:
|
||||
try:
|
||||
val = saved_session.data
|
||||
data = self.serializer.loads(want_bytes(val))
|
||||
return self.session_class(data, sid=sid)
|
||||
except:
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
return self.session_class(sid=sid, permanent=self.permanent)
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
store_id = self.key_prefix + session.sid
|
||||
saved_session = self.sql_session_model.query.filter_by(
|
||||
session_id=store_id).first()
|
||||
if not session:
|
||||
if session.modified:
|
||||
if saved_session:
|
||||
self.db.session.delete(saved_session)
|
||||
self.db.session.commit()
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain, path=path)
|
||||
return
|
||||
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
val = self.serializer.dumps(dict(session))
|
||||
if saved_session:
|
||||
saved_session.data = val
|
||||
saved_session.expiry = expires
|
||||
self.db.session.commit()
|
||||
else:
|
||||
new_session = self.sql_session_model(store_id, val, expires)
|
||||
self.db.session.add(new_session)
|
||||
self.db.session.commit()
|
||||
if self.use_signer:
|
||||
session_id = self._get_signer(app).sign(want_bytes(session.sid))
|
||||
else:
|
||||
session_id = session.sid
|
||||
response.set_cookie(app.session_cookie_name, session_id,
|
||||
expires=expires, httponly=httponly,
|
||||
domain=domain, path=path, secure=secure)
|
||||
@@ -34,6 +34,11 @@ class http:
|
||||
result = self._get_curl(url,timeout,headers,verify)
|
||||
elif type == 'php':
|
||||
result = self._get_php(url,timeout,headers,verify)
|
||||
elif type == 'src':
|
||||
if sys.version_info[0] == 2:
|
||||
result = self._get_py2(url,timeout,headers,verify)
|
||||
else:
|
||||
result = self._get_py3(url,timeout,headers,verify)
|
||||
return result
|
||||
|
||||
def post(self,url,data,timeout = 60,headers = {},verify = False,type = 'python'):
|
||||
@@ -51,6 +56,11 @@ class http:
|
||||
result = self._post_curl(url,data,timeout,headers,verify)
|
||||
elif type == 'php':
|
||||
result = self._post_php(url,data,timeout,headers,verify)
|
||||
elif type == 'src':
|
||||
if sys.version_info[0] == 2:
|
||||
result = self._post_py2(url,data,timeout,headers,verify)
|
||||
else:
|
||||
result = self._post_py3(url,data,timeout,headers,verify)
|
||||
return result
|
||||
|
||||
#POST请求 Python2
|
||||
|
||||
+41
-2
@@ -142,7 +142,7 @@ class nginx:
|
||||
log_format = loads(args.log_format)
|
||||
data = """
|
||||
#LOG_FORMAT_BEGIN_{n}
|
||||
log_format {n} '{c};'
|
||||
log_format {n} '{c}';
|
||||
#LOG_FORMAT_END_{n}
|
||||
""".format(n=args.log_format_name,c=' '.join(log_format))
|
||||
data = data.replace('$http_user_agent','"$http_user_agent"')
|
||||
@@ -171,6 +171,7 @@ class nginx:
|
||||
return public.returnMsg(False, 'NGINX_CONF_NOT_EXISTS')
|
||||
reg = '\s*#LOG_FORMAT_BEGIN_{n}(\n|.)+#LOG_FORMAT_END_{n}\n?'.format(n=args.log_format_name)
|
||||
conf = re.sub(reg,'',conf)
|
||||
self._del_format_log_of_website(args.log_format_name)
|
||||
public.writeFile(self.nginxconf,conf)
|
||||
public.serviceReload()
|
||||
return public.returnMsg(True, 'SET_SUCCESS')
|
||||
@@ -245,7 +246,10 @@ class nginx:
|
||||
if not conf:
|
||||
return public.returnMsg(False, 'NGINX_CONF_NOT_EXISTS')
|
||||
format_exist_reg = '(access_log\s+/www.*\.log).*;'
|
||||
access_log = re.search(format_exist_reg, conf).groups()[0] + ' ' + args.log_format_name + ';'
|
||||
access_log = self.get_nginx_access_log(conf)
|
||||
if not access_log:
|
||||
continue
|
||||
access_log = 'access_log '+ access_log + ' ' + args.log_format_name + ';'
|
||||
if site['name'] not in sites and re.search(format_exist_reg,conf):
|
||||
access_log = ' '.join(access_log.split()[:-1])+';'
|
||||
conf = re.sub(reg, access_log, conf)
|
||||
@@ -257,6 +261,22 @@ class nginx:
|
||||
except:
|
||||
return public.returnMsg(False, str(public.get_error_info()))
|
||||
|
||||
def get_nginx_access_log(self,nginx_conf):
|
||||
try:
|
||||
reg = 'access_log\s+(.*\.log)'
|
||||
log_path = re.findall(reg, nginx_conf)
|
||||
if not log_path:
|
||||
return False
|
||||
for i in log_path:
|
||||
if 'purge_cache' in i:
|
||||
continue
|
||||
if not os.path.exists(i):
|
||||
continue
|
||||
return i
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def _get_format_log_to_website(self,log_format_name):
|
||||
tmp = public.M('sites').field('name').select()
|
||||
reg = 'access_log.*{};'.format(log_format_name)
|
||||
@@ -272,3 +292,22 @@ class nginx:
|
||||
else:
|
||||
data[i['name']] = False
|
||||
return data
|
||||
|
||||
def _del_format_log_of_website(self,log_format_name):
|
||||
site_format_log_status = self._get_format_log_to_website(log_format_name)
|
||||
try:
|
||||
for s in site_format_log_status.keys():
|
||||
if not site_format_log_status[s]:
|
||||
continue
|
||||
website_conf_file = '/www/server/panel/vhost/nginx/{}.conf'.format(s)
|
||||
format_exist_reg = 'access_log\s+/www.*\.log\s+{};'.format(log_format_name)
|
||||
conf = public.readFile(website_conf_file)
|
||||
if not conf:continue
|
||||
if not re.search(format_exist_reg,conf):continue
|
||||
access_log = re.search(format_exist_reg,conf).group().split()
|
||||
access_log = access_log[0] + ' ' +access_log[1] +';'
|
||||
conf = re.sub(format_exist_reg,access_log,conf)
|
||||
public.writeFile(website_conf_file,conf)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
+2
-2
@@ -24,7 +24,7 @@ class panelApi:
|
||||
|
||||
data['limit_addr'] = '\n'.join(data['limit_addr'])
|
||||
data['bind'] = self.get_bind_token()
|
||||
qrcode = (public.getPanelAddr() + "|" + data['token'] + "|" + data['key'] + '|' + data['bind']['token']).encode('utf-8')
|
||||
qrcode = (public.getPanelAddr() + "|" + data['token'] + "|" + data['key'] + '|' + data['bind']['token']+'|aapanel').encode('utf-8')
|
||||
data['qrcode'] = public.base64.b64encode(qrcode).decode('utf-8')
|
||||
data['apps'] = sorted(data['apps'],key=lambda x: x['time'],reverse=True)
|
||||
del(data['key'])
|
||||
@@ -216,7 +216,7 @@ class panelApi:
|
||||
data['token'] = public.md5(token)
|
||||
data['token_crypt'] = public.en_crypt(data['token'],token).decode('utf-8')
|
||||
public.WriteLog('SET_API','%s API interface' % stats[data['open']])
|
||||
token = stats[data['open']] + 'success!'
|
||||
token = stats[data['open']] + ' success!'
|
||||
elif get.t_type == '3':
|
||||
data['limit_addr'] = get.limit_addr.split('\n')
|
||||
public.WriteLog('SET_API','Change IP limit to [%s]' % get.limit_addr)
|
||||
|
||||
+43
-4
@@ -12,7 +12,7 @@
|
||||
#------------------------------
|
||||
|
||||
import public,time,json,os,requests
|
||||
from BTPanel import session
|
||||
from BTPanel import session,cache
|
||||
|
||||
class panelAuth:
|
||||
__product_list_path = 'data/product_list.pl'
|
||||
@@ -63,11 +63,16 @@ class panelAuth:
|
||||
def get_plugin_price(self, get):
|
||||
try:
|
||||
userPath = 'data/userInfo.json'
|
||||
if not 'pluginName' in get: return public.returnMsg(False,'INIT_ARGS_ERR')
|
||||
if not 'pluginName' in get and not 'product_id' in get: return public.returnMsg(False,'INIT_ARGS_ERR')
|
||||
if not os.path.exists(userPath): return public.returnMsg(False,'LOGIN_FIRST')
|
||||
params = {}
|
||||
params['product_id'] = self.get_plugin_info(get.pluginName)['id']
|
||||
if not hasattr(get,'product_id'):
|
||||
params['product_id'] = self.get_plugin_info(get.pluginName)['id']
|
||||
else:
|
||||
params['product_id'] = get.product_id
|
||||
data = self.send_cloud('{}/api/product/prices'.format(self.__official_url), params)
|
||||
if len(data['res']) > 3:
|
||||
return data['res'][-3:]
|
||||
return data['res']
|
||||
except:
|
||||
del(session['get_product_list'])
|
||||
@@ -291,4 +296,38 @@ class panelAuth:
|
||||
data = self.send_cloud('{}/api/authorize/product/activate'.format(self.__official_url), params)
|
||||
if not data['success']: return public.returnMsg(False,'Activate Failed')
|
||||
session['focre_cloud'] = True
|
||||
return public.returnMsg(True,'Activate successfully')
|
||||
return public.returnMsg(True,'Activate successfully')
|
||||
|
||||
def renew_product_auth(self,get):
|
||||
params = {}
|
||||
params['serial_no'] = get.serial_no
|
||||
params['pay_channel'] = get.pay_channel
|
||||
params['cycle'] = get.cycle
|
||||
params['cycle_unit'] = get.cycle_unit
|
||||
params['src'] = 2
|
||||
params['environment_info'] = json.dumps(public.fetch_env_info())
|
||||
if hasattr(get,'coupon_id') and get.pay_channel == '10':
|
||||
params['coupon_id'] = get.coupon_id
|
||||
data = self.send_cloud('{}/api/authorize/product/renew'.format(self.__official_url), params)
|
||||
session['focre_cloud'] = True
|
||||
# 使用抵扣券续费直接返回续费结果
|
||||
if get.pay_channel == '10':
|
||||
if not data['success']:
|
||||
return public.returnMsg(False, 'Renew Failed')
|
||||
return public.returnMsg(True,'Renew successfully')
|
||||
# 使用支付续费返回stripe的请求数据
|
||||
return data['res']
|
||||
|
||||
def free_trial(self,get):
|
||||
"""
|
||||
每个账号有一次免费试用专业版15天的机会
|
||||
:return:
|
||||
"""
|
||||
params = {}
|
||||
params['environment_info'] = json.dumps(public.fetch_env_info())
|
||||
data = self.send_cloud('{}/api/product/obtainProfessionalMemberFree'.format(self.__official_url), params)
|
||||
session['focre_cloud'] = True
|
||||
# 使用抵扣券续费直接返回续费结果
|
||||
if not data['success']:
|
||||
return public.returnMsg(False, 'Apply Failed')
|
||||
return public.returnMsg(True,'Apply successfully')
|
||||
|
||||
+337
-34
@@ -30,19 +30,28 @@ class backup:
|
||||
_db_mysql = None
|
||||
_cloud = None
|
||||
_is_save_local = os.path.exists('data/is_save_local_backup.pl')
|
||||
def __init__(self,cloud_object = None):
|
||||
_error_msg = ""
|
||||
_backup_all = False
|
||||
def __init__(self,cloud_object=None, cron_info={}):
|
||||
'''
|
||||
@name 数据备份对象
|
||||
@param cloud_object 远程上传对象,需具备以下几个属性和方法:
|
||||
_title = '中文名称,如:阿里云OSS'
|
||||
_name = '英文名称,如:alioss'
|
||||
|
||||
upload_file(filename,data_type = None)
|
||||
文件名 , 数据类型 site/database/path
|
||||
|
||||
delete_file(filename,data_type = None)
|
||||
文件名 , 数据类型 site/database/path
|
||||
|
||||
给_error_msg赋值,传递错误消息:
|
||||
_error_msg = "错误消息"
|
||||
'''
|
||||
self._cloud = cloud_object
|
||||
self.cron_info = None
|
||||
if cron_info and 'echo' in cron_info.keys():
|
||||
self.cron_info = self.get_cron_info(cron_info["echo"])
|
||||
self._path = public.M('config').where("id=?",(1,)).getField('backup_path')
|
||||
|
||||
def echo_start(self):
|
||||
@@ -62,6 +71,9 @@ class backup:
|
||||
def echo_error(self,msg):
|
||||
print("=" * 90)
|
||||
print("|-Error:{}".format(msg))
|
||||
if self._error_msg:
|
||||
self._error_msg += "\n"
|
||||
self._error_msg += msg
|
||||
|
||||
#构造排除
|
||||
def get_exclude(self,exclude = []):
|
||||
@@ -103,13 +115,15 @@ class backup:
|
||||
arr['path'] = disk[6]
|
||||
tmp1 = [disk[2],disk[3],disk[4],disk[5]]
|
||||
arr['size'] = tmp1
|
||||
arr['inodes'] = [inodes[1],inodes[2],inodes[3],inodes[4]]
|
||||
if int(inodes[1]) == 0 and int(inodes[2]) == 0:
|
||||
arr['inodes'] = [inodes[1],10000,10000,0]
|
||||
else:
|
||||
arr['inodes'] = [inodes[1],inodes[2],inodes[3],inodes[4]]
|
||||
diskInfo.append(arr)
|
||||
except:
|
||||
continue
|
||||
return diskInfo
|
||||
|
||||
|
||||
#取磁盘可用空间
|
||||
def get_disk_free(self,dfile):
|
||||
diskInfo = self.GetDiskInfo2()
|
||||
@@ -125,24 +139,29 @@ class backup:
|
||||
return _root['path'],float(_root['size'][2]) * 1024,int(_root['inodes'][2])
|
||||
return '',0,0
|
||||
|
||||
|
||||
#备份指定目录
|
||||
def backup_path(self,spath,dfile = None,exclude=[],save=3):
|
||||
|
||||
error_msg = ""
|
||||
self.echo_start()
|
||||
if not os.path.exists(spath):
|
||||
self.echo_error(public.getMsg('BACKUP_DIR_NOT_EXIST',(spath,)))
|
||||
error_msg= public.getMsg('BACKUP_DIR_NOT_EXIST',(spath,))
|
||||
self.echo_error(error_msg)
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
if spath[-1] == '/':
|
||||
spath = spath[:-1]
|
||||
|
||||
dirname = os.path.basename(spath)
|
||||
|
||||
if not dfile:
|
||||
fname = 'path_{}_{}.tar.gz'.format(dirname,public.format_date("%Y%m%d_%H%M%S"))
|
||||
dfile = os.path.join(self._path,'path',fname)
|
||||
|
||||
if not self.backup_path_to(spath,dfile,exclude):
|
||||
if self._error_msg:
|
||||
error_msg = self._error_msg
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
if self._cloud:
|
||||
@@ -150,17 +169,23 @@ class backup:
|
||||
if self._cloud.upload_file(dfile,'path'):
|
||||
self.echo_info(public.getMsg("BACKUP_UPLOAD_SUCCESS",(self._cloud._title,)))
|
||||
else:
|
||||
self.echo_error(public.getMsg('BACKUP_UPLOAD_FAILED'))
|
||||
if hasattr(self._cloud, "error_msg"):
|
||||
if self._cloud.error_msg:
|
||||
error_msg = self._cloud.error_msg
|
||||
if not error_msg:
|
||||
error_msg = public.getMsg('BACKUP_UPLOAD_FAILED')
|
||||
self.echo_error(error_msg)
|
||||
if os.path.exists(dfile):
|
||||
os.remove(dfile)
|
||||
return False
|
||||
|
||||
remark = "Backup to " + self._cloud._title
|
||||
self.send_failture_notification(error_msg, remark=remark)
|
||||
return False
|
||||
|
||||
filename = dfile
|
||||
if self._cloud:
|
||||
filename = dfile + '|' + self._cloud._name + '|' + fname
|
||||
|
||||
|
||||
pdata = {
|
||||
'type': '2',
|
||||
'name': spath,
|
||||
@@ -172,10 +197,22 @@ class backup:
|
||||
public.M('backup').insert(pdata)
|
||||
|
||||
if self._cloud:
|
||||
if not self._is_save_local:
|
||||
_not_save_local = True
|
||||
save_local = 0
|
||||
if self.cron_info:
|
||||
save_local = self.cron_info["save_local"]
|
||||
if save_local:
|
||||
_not_save_local = False
|
||||
else:
|
||||
if self._is_save_local:
|
||||
_not_save_local = False
|
||||
|
||||
if _not_save_local:
|
||||
if os.path.exists(dfile):
|
||||
os.remove(dfile)
|
||||
self.echo_info(public.getMsg("BACKUP_DEL",(dfile,)))
|
||||
else:
|
||||
self.echo_info(public.getMsg('KEEP_LOCAL'))
|
||||
|
||||
if not self._cloud:
|
||||
backups = public.M('backup').where("type=? and pid=? and name=? and filename NOT LIKE '%|%'",('2',0,spath)).field('id,name,filename').select()
|
||||
@@ -245,10 +282,10 @@ class backup:
|
||||
exclude_config = "Not set"
|
||||
|
||||
if siteName:
|
||||
self.echo_info(public.getMsg('BACKUP_SITE',(siteName)))
|
||||
self.echo_info(public.getMsg('BACKUP_SITE',(siteName,)))
|
||||
self.echo_info(public.getMsg('WEBSITE_DIR',(spath,)))
|
||||
else:
|
||||
self.echo_info(public.getMsg('BACKUP_DIR',(spath)))
|
||||
self.echo_info(public.getMsg('BACKUP_DIR',(spath,)))
|
||||
|
||||
self.echo_info(public.getMsg(
|
||||
"DIR_SIZE",
|
||||
@@ -306,17 +343,28 @@ class backup:
|
||||
pid = find['id']
|
||||
fname = 'web_{}_{}.tar.gz'.format(siteName,public.format_date("%Y%m%d_%H%M%S"))
|
||||
dfile = os.path.join(self._path,'site',fname)
|
||||
error_msg = ""
|
||||
if not self.backup_path_to(spath,dfile,exclude,siteName=siteName):
|
||||
if self._error_msg:
|
||||
error_msg = self._error_msg
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
if self._cloud:
|
||||
self.echo_info(public.getMsg("BACKUP_UPLOADING",(self._cloud._title,)))
|
||||
if self._cloud.upload_file(dfile,'site'):
|
||||
self.echo_info(public.getMsg("BACKUP_UPLOAD_SUCCESS",(self._cloud._title,)))
|
||||
else:
|
||||
self.echo_error('BACKUP_UPLOAD_FAILED')
|
||||
if hasattr(self._cloud, "error_msg"):
|
||||
if self._cloud.error_msg:
|
||||
error_msg = self._cloud.error_msg
|
||||
if not error_msg:
|
||||
error_msg = public.getMsg('BACKUP_UPLOAD_FAILED')
|
||||
self.echo_error(error_msg)
|
||||
if os.path.exists(dfile):
|
||||
os.remove(dfile)
|
||||
|
||||
remark = "Backup to " + self._cloud._title
|
||||
self.send_failture_notification(error_msg, remark=remark)
|
||||
return False
|
||||
|
||||
filename = dfile
|
||||
@@ -333,10 +381,22 @@ class backup:
|
||||
}
|
||||
public.M('backup').insert(pdata)
|
||||
if self._cloud:
|
||||
if not self._is_save_local:
|
||||
_not_save_local = True
|
||||
save_local = 0
|
||||
if self.cron_info:
|
||||
save_local = self.cron_info["save_local"]
|
||||
if save_local:
|
||||
_not_save_local = False
|
||||
else:
|
||||
if self._is_save_local:
|
||||
_not_save_local = False
|
||||
|
||||
if _not_save_local:
|
||||
if os.path.exists(dfile):
|
||||
os.remove(dfile)
|
||||
self.echo_info(public.getMsg("BACKUP_DEL",(dfile,)))
|
||||
else:
|
||||
self.echo_info(public.getMsg('KEEP_LOCAL'))
|
||||
|
||||
#清理多余备份
|
||||
if not self._cloud:
|
||||
@@ -347,13 +407,40 @@ class backup:
|
||||
self.delete_old(backups,save,'site')
|
||||
self.echo_end()
|
||||
return dfile
|
||||
|
||||
|
||||
#备份所有数据库
|
||||
def backup_database_all(self,save = 3):
|
||||
databases = public.M('databases').field('name').select()
|
||||
self._backup_all = True
|
||||
failture_count = 0
|
||||
results = []
|
||||
for database in databases:
|
||||
self._error_msg = ""
|
||||
result = self.backup_database(database['name'],save=save)
|
||||
if not result:
|
||||
failture_count += 1
|
||||
results.append((database['name'], result, self._error_msg,))
|
||||
|
||||
if failture_count > 0:
|
||||
self.send_all_failture_notification("database", results)
|
||||
self._backup_all = False
|
||||
|
||||
#备份所有站点
|
||||
def backup_site_all(self,save = 3):
|
||||
sites = public.M('sites').field('name').select()
|
||||
self._backup_all = True
|
||||
failture_count = 0
|
||||
results = []
|
||||
for site in sites:
|
||||
self.backup_site(site['name'],save)
|
||||
self._error_msg = ""
|
||||
result = self.backup_site(site['name'],save)
|
||||
if not result:
|
||||
failture_count += 1
|
||||
results.append((site['name'], result, self._error_msg,))
|
||||
|
||||
if failture_count > 0:
|
||||
self.send_all_failture_notification("site", results)
|
||||
self._backup_all = False
|
||||
|
||||
#配置
|
||||
def mypass(self,act):
|
||||
@@ -401,14 +488,23 @@ class backup:
|
||||
if not os.path.exists(dpath):
|
||||
os.makedirs(dpath,384)
|
||||
|
||||
error_msg = ""
|
||||
import panelMysql
|
||||
if not self._db_mysql:self._db_mysql = panelMysql.panelMysql()
|
||||
d_tmp = self._db_mysql.query("select sum(DATA_LENGTH)+sum(INDEX_LENGTH) from information_schema.tables where table_schema='%s'" % db_name)
|
||||
p_size = self.map_to_list(d_tmp)[0][0]
|
||||
try:
|
||||
p_size = self.map_to_list(d_tmp)[0][0]
|
||||
except:
|
||||
error_msg = public.getMsg('DB_CONN_ERR')
|
||||
self.echo_error(error_msg)
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
if p_size == None:
|
||||
self.echo_error(public.getMsg('DB_BACKUP_ERR',(db_name,)))
|
||||
return
|
||||
error_msg = public.getMsg('DB_BACKUP_ERR',(db_name,))
|
||||
self.echo_error(error_msg)
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
character = public.get_database_character(db_name)
|
||||
|
||||
@@ -423,14 +519,17 @@ class backup:
|
||||
))
|
||||
if disk_path:
|
||||
if disk_free < p_size:
|
||||
self.echo_error(
|
||||
public.getMsg("PARTITION_LESS_THEN",(
|
||||
error_msg = public.getMsg("PARTITION_LESS_THEN",(
|
||||
str(public.to_size(p_size),)
|
||||
)))
|
||||
))
|
||||
self.echo_error(error_msg)
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
if disk_inode < self._inode_min:
|
||||
self.echo_error(public.getMsg("INODE_LESS_THEN",(self._inode_min,)))
|
||||
error_msg = public.getMsg("INODE_LESS_THEN",(self._inode_min,))
|
||||
self.echo_error(error_msg)
|
||||
self.send_failture_notification(error_msg)
|
||||
return False
|
||||
|
||||
stime = time.time()
|
||||
@@ -451,7 +550,9 @@ class backup:
|
||||
#self.mypass(False)
|
||||
gz_size = os.path.getsize(dfile)
|
||||
if gz_size < 400:
|
||||
self.echo_error(public.getMsg("EXPORT_DB_ERR"))
|
||||
error_msg = public.getMsg("EXPORT_DB_ERR")
|
||||
self.echo_error(error_msg)
|
||||
self.send_failture_notification(error_msg)
|
||||
self.echo_info(public.readFile(self._err_log))
|
||||
return False
|
||||
compressed_time = str('{:.2f}'.format(time.time() - stime))
|
||||
@@ -465,9 +566,17 @@ class backup:
|
||||
if self._cloud.upload_file(dfile, 'database'):
|
||||
self.echo_info(public.getMsg("BACKUP_UPLOAD_SUCCESS",(self._cloud._title,)))
|
||||
else:
|
||||
self.echo_error('BACKUP_UPLOAD_FAILED')
|
||||
if hasattr(self._cloud, "error_msg"):
|
||||
if self._cloud.error_msg:
|
||||
error_msg = self._cloud.error_msg
|
||||
if not error_msg:
|
||||
error_msg = public.getMsg('BACKUP_UPLOAD_FAILED')
|
||||
self.echo_error(error_msg)
|
||||
if os.path.exists(dfile):
|
||||
os.remove(dfile)
|
||||
|
||||
remark = "Backup to " + self._cloud._title
|
||||
self.send_failture_notification(error_msg, remark=remark)
|
||||
return False
|
||||
|
||||
filename = dfile
|
||||
@@ -488,12 +597,23 @@ class backup:
|
||||
}
|
||||
public.M('backup').insert(pdata)
|
||||
|
||||
|
||||
if self._cloud:
|
||||
if not self._is_save_local:
|
||||
_not_save_local = True
|
||||
save_local = 0
|
||||
if self.cron_info:
|
||||
save_local = self.cron_info["save_local"]
|
||||
if save_local:
|
||||
_not_save_local = False
|
||||
else:
|
||||
if self._is_save_local:
|
||||
_not_save_local = False
|
||||
|
||||
if _not_save_local:
|
||||
if os.path.exists(dfile):
|
||||
os.remove(dfile)
|
||||
self.echo_info(public.getMsg("BACKUP_DEL",(dfile)))
|
||||
self.echo_info(public.getMsg("BACKUP_DEL",(dfile,)))
|
||||
else:
|
||||
self.echo_info(public.getMsg('KEEP_LOCAL'))
|
||||
|
||||
#清理多余备份
|
||||
if not self._cloud:
|
||||
@@ -504,11 +624,194 @@ class backup:
|
||||
self.echo_end()
|
||||
return dfile
|
||||
|
||||
def generate_success_title(self, task_name):
|
||||
from send_mail import send_mail
|
||||
sm = send_mail()
|
||||
now = public.format_date(format="%Y-%m-%d %H:%M")
|
||||
server_ip = sm.GetLocalIp()
|
||||
title = public.getMsg("BACKUP_TASK_TITLE",(server_ip, task_name))
|
||||
return title
|
||||
|
||||
#备份所有数据库
|
||||
def backup_database_all(self,save = 3):
|
||||
databases = public.M('databases').field('name').select()
|
||||
for database in databases:
|
||||
self.backup_database(database['name'],save=save)
|
||||
def generate_failture_title(self):
|
||||
title = "aaPanel backup task failed reminder"
|
||||
return title
|
||||
|
||||
def generate_all_failture_notice(self, task_name, msg, backup_type, remark=""):
|
||||
# from send_mail import send_mail
|
||||
# sm = send_mail()
|
||||
now = public.format_date(format="%Y-%m-%d %H:%M:%S")
|
||||
server_ip = public.GetLocalIp()
|
||||
if remark:
|
||||
remark = "\n* Task notes: {}".format(remark)
|
||||
|
||||
notice_content = """Hello,
|
||||
aaPanel reminds you that the cron you set failed to execute:
|
||||
* Server IP: {}
|
||||
* Time: {}
|
||||
* Task name: {}{}
|
||||
* The following is a list of {} that failed to backup:
|
||||
<table style="color:red;">
|
||||
{}
|
||||
</table>
|
||||
Please deal with it as soon as possible to avoid unnecessary trouble due to the failure of the backup task.
|
||||
- Notification by aaPanel""".format(
|
||||
server_ip, now, task_name, remark, backup_type, msg)
|
||||
return notice_content
|
||||
|
||||
def generate_failture_notice(self, task_name, msg, remark):
|
||||
# from send_mail import send_mail
|
||||
# sm = send_mail()
|
||||
now = public.format_date(format="%Y-%m-%d %H:%M:%S")
|
||||
server_ip = public.GetLocalIp()
|
||||
if remark:
|
||||
remark = "\n* Task notes: {}".format(remark)
|
||||
|
||||
notice_content = """Hello,
|
||||
aaPanel reminds you that the cron you set failed to execute:
|
||||
* Server IP: {}
|
||||
* Time: {}
|
||||
* Task name:{}{}
|
||||
* Error messages:
|
||||
<span style="color:red;">
|
||||
{}
|
||||
</span>
|
||||
Please deal with it as soon as possible to avoid unnecessary trouble due to the failure of the backup task.
|
||||
-- Notification by aaPanel""".format(
|
||||
server_ip, now, task_name, remark, msg)
|
||||
return notice_content
|
||||
|
||||
def get_cron_info(self, cron_name):
|
||||
""" 通过计划任务名称查找计划任务配置参数 """
|
||||
try:
|
||||
cron_info = public.M('crontab').where('echo=?',(cron_name,))\
|
||||
.field('name,save_local,notice,notice_channel').find()
|
||||
return cron_info
|
||||
except Exception as e:
|
||||
pass
|
||||
return {}
|
||||
|
||||
def send_failture_notification(self, error_msg, remark=""):
|
||||
"""发送任务失败消息
|
||||
|
||||
:error_msg 错误信息
|
||||
:remark 备注
|
||||
"""
|
||||
if self._backup_all:
|
||||
return
|
||||
if not self.cron_info:
|
||||
return
|
||||
cron_info = self.cron_info
|
||||
cron_title = cron_info["name"]
|
||||
save_local = cron_info["save_local"]
|
||||
notice = cron_info["notice"]
|
||||
notice_channel = cron_info["notice_channel"]
|
||||
if notice == 0 or not notice_channel:
|
||||
return
|
||||
|
||||
if notice == 1 or notice == 2:
|
||||
title = self.generate_failture_title()
|
||||
task_name = cron_title
|
||||
msg = self.generate_failture_notice(task_name, error_msg, remark)
|
||||
res = self.send_notification(notice_channel, title, msg)
|
||||
if res:
|
||||
self.echo_info(public.getMsg('NOTIFICATION_SENT'))
|
||||
|
||||
def send_all_failture_notification(self, backup_type, results, remark=""):
|
||||
"""统一发送任务失败消息
|
||||
|
||||
:results [(备份对象, 备份结果,错误信息),...]
|
||||
:remark 备注
|
||||
"""
|
||||
if not self.cron_info:
|
||||
return
|
||||
cron_info = self.cron_info
|
||||
cron_title = cron_info["name"]
|
||||
save_local = cron_info["save_local"]
|
||||
notice = cron_info["notice"]
|
||||
notice_channel = cron_info["notice_channel"]
|
||||
if notice == 0 or not notice_channel:
|
||||
return
|
||||
|
||||
if notice == 1 or notice == 2:
|
||||
title = self.generate_failture_title()
|
||||
type_desc = {
|
||||
"site": "site",
|
||||
"database": "database"
|
||||
}
|
||||
backup_type_desc = type_desc[backup_type]
|
||||
task_name = cron_title
|
||||
failture_count = 0
|
||||
total = 0
|
||||
content = ""
|
||||
|
||||
for obj in results:
|
||||
total += 1
|
||||
obj_name = obj[0]
|
||||
result = obj[1]
|
||||
if not result:
|
||||
failture_count += 1
|
||||
content += "<tr><td style='color:red'>{}</td><tr>".format(obj_name)
|
||||
|
||||
if failture_count > 0:
|
||||
if self._cloud:
|
||||
remark = public.getMsg("BACKUP_MSG"),(
|
||||
self._cloud._title, total, backup_type_desc, failture_count)
|
||||
else:
|
||||
remark = public.getMsg("BACKUP_MSG1"),(
|
||||
failture_count, total, backup_type_desc)
|
||||
|
||||
msg = self.generate_all_failture_notice(task_name, content, backup_type_desc, remark)
|
||||
res = self.send_notification(notice_channel, title, msg)
|
||||
if res:
|
||||
self.echo_info(public.getMsg('NOTIFICATION_SENT'))
|
||||
else:
|
||||
self.echo_error(public.getMsg('NOTIFICATION_ERR'))
|
||||
|
||||
def send_notification(self, channel, title, msg = ""):
|
||||
try:
|
||||
from send_mail import send_mail
|
||||
tondao = []
|
||||
if channel.find(",") >= 0:
|
||||
tongdao = channel.split(",")
|
||||
else:
|
||||
tongdao = [channel]
|
||||
|
||||
sm = send_mail()
|
||||
send_res = []
|
||||
error_count = 0
|
||||
channel_names = {
|
||||
"mail": "email",
|
||||
# "dingidng": "钉钉"
|
||||
}
|
||||
error_channel = []
|
||||
settings = sm.get_settings()
|
||||
for td in tongdao:
|
||||
_res = False
|
||||
if td == "mail":
|
||||
if len(settings["user_mail"]['mail_list']) == 0:
|
||||
continue
|
||||
mail_list = settings['user_mail']['mail_list']
|
||||
if len(mail_list) == 1:
|
||||
mail_list = mail_list[0]
|
||||
_res = sm.qq_smtp_send(mail_list, title=title, body=msg.replace("\n", "<br/>"))
|
||||
if not _res:
|
||||
error_count += 1
|
||||
error_channel.append(channel_names[td])
|
||||
if td == "dingding":
|
||||
if len(settings["dingding"]['info']) == 0:
|
||||
continue
|
||||
_res = sm.dingding_send(msg)
|
||||
send_res.append(_res)
|
||||
if not _res:
|
||||
error_count += 1
|
||||
error_channel.append(channel_names[td])
|
||||
if error_count > 0:
|
||||
print("Notification:{} failed to send".format(",".join(error_channel)))
|
||||
if error_count == len(tongdao):
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
|
||||
|
||||
+24
-7
@@ -201,9 +201,16 @@ class CloudFlareDns(BaseDns):
|
||||
self.CLOUDFLARE_API_BASE_URL = CLOUDFLARE_API_BASE_URL
|
||||
super(CloudFlareDns, self).__init__()
|
||||
|
||||
def get_headers(self):
|
||||
if os.path.exists('/www/server/panel/data/cf_limit_api.pl'):
|
||||
headers = {"Authorization": "Bearer "+self.CLOUDFLARE_API_KEY}
|
||||
else:
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
return headers
|
||||
|
||||
def find_dns_zone(self, domain_name):
|
||||
url = urljoin(self.CLOUDFLARE_API_BASE_URL, "zones?status=active&name={0}".format(domain_name))
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
headers = self.get_headers()
|
||||
find_dns_zone_response = requests.get(url, headers=headers, timeout=self.HTTP_TIMEOUT)
|
||||
if find_dns_zone_response.status_code != 200:
|
||||
raise ValueError(
|
||||
@@ -231,7 +238,11 @@ class CloudFlareDns(BaseDns):
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
"zones/{0}/dns_records".format(self.CLOUDFLARE_DNS_ZONE_ID),
|
||||
)
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
# if '_' in self.CLOUDFLARE_API_KEY or '-' in self.CLOUDFLARE_API_KEY:
|
||||
# headers = {"Authorization": "Bearer "+self.CLOUDFLARE_API_KEY}
|
||||
# else:
|
||||
# headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
headers = self.get_headers()
|
||||
body = {
|
||||
"type": s_type,
|
||||
"name": domain_name,
|
||||
@@ -257,7 +268,11 @@ class CloudFlareDns(BaseDns):
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
"zones/{0}/dns_records".format(self.CLOUDFLARE_DNS_ZONE_ID),
|
||||
)
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
# if '_' in self.CLOUDFLARE_API_KEY or '-' in self.CLOUDFLARE_API_KEY:
|
||||
# headers = {"Authorization": "Bearer "+self.CLOUDFLARE_API_KEY}
|
||||
# else:
|
||||
# headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
headers = self.get_headers()
|
||||
body = {
|
||||
"type": "TXT",
|
||||
"name": "_acme-challenge" + "." + domain_name + ".",
|
||||
@@ -278,8 +293,11 @@ class CloudFlareDns(BaseDns):
|
||||
|
||||
|
||||
def remove_record(self,domain_name,dns_name,s_type):
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
|
||||
# if '_' in self.CLOUDFLARE_API_KEY or '-' in self.CLOUDFLARE_API_KEY:
|
||||
# headers = {"Authorization": "Bearer "+self.CLOUDFLARE_API_KEY}
|
||||
# else:
|
||||
# headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
headers = self.get_headers()
|
||||
list_dns_payload = {"type": s_type, "name": dns_name}
|
||||
list_dns_url = urljoin(
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
@@ -296,7 +314,6 @@ class CloudFlareDns(BaseDns):
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
"zones/{0}/dns_records/{1}".format(self.CLOUDFLARE_DNS_ZONE_ID, dns_record_id),
|
||||
)
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
requests.delete(
|
||||
url, headers=headers, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
@@ -447,7 +464,7 @@ class CloudxnsDns(object):
|
||||
headers = self.get_headers(url)
|
||||
req = requests.get(url=url, headers=headers,verify=False)
|
||||
req = req.json()
|
||||
|
||||
|
||||
return req
|
||||
|
||||
def get_domain_id(self, domain_name):
|
||||
|
||||
+59
-3
@@ -4,26 +4,81 @@
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: hwliang <hwl@bt.cn>
|
||||
# | Author: hwliang <2020-05-18>
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
# +-------------------------------------------------------------------
|
||||
# | 消息提醒
|
||||
# +-------------------------------------------------------------------
|
||||
import os,sys,time
|
||||
import public
|
||||
class panelMessage:
|
||||
import public,json
|
||||
if os.environ.get('BT_TASK') != '1':
|
||||
from BTPanel import cache
|
||||
else:
|
||||
import cachelib
|
||||
cache = cachelib.SimpleCache()
|
||||
|
||||
class panelMessage:
|
||||
os = 'linux'
|
||||
|
||||
def __init__(self):
|
||||
# if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'messages','%retry_num%')).count():
|
||||
# public.M('messages').execute("alter TABLE messages add send integer DEFAULT 0",())
|
||||
# public.M('messages').execute("alter TABLE messages add retry_num integer DEFAULT 0",())
|
||||
pass
|
||||
|
||||
|
||||
def set_send_status(self, id, data):
|
||||
'''
|
||||
@name 设置消息发送状态
|
||||
@author cjxin <2021-04-12>
|
||||
@param args dict_obj{
|
||||
id: 消息标识,
|
||||
data
|
||||
}
|
||||
@return dict
|
||||
'''
|
||||
|
||||
public.M('messages').where('id=?',id).update(data)
|
||||
return public.returnMsg(True,'设置成功!')
|
||||
|
||||
|
||||
"""
|
||||
获取官网推送消息,一小时获取一次
|
||||
"""
|
||||
def get_cloud_messages(self,args):
|
||||
#ret = cache.get('get_cloud_messages')
|
||||
#if ret: return public.returnMsg(True,'同步成功1!')
|
||||
data = {}
|
||||
data['version'] = public.version()
|
||||
data['os'] = self.os
|
||||
sUrl = public.GetConfigValue('home') + '/api/wpanel/get_messages'
|
||||
import http_requests
|
||||
http_requests.DEFAULT_TYPE = 'src'
|
||||
info = http_requests.post(sUrl,data).json()
|
||||
# info = json.loads(public.httpPost(sUrl,data))
|
||||
for x in info:
|
||||
count = public.M('messages').where('level=? and msg=?',(x['level'],x['msg'],)).count()
|
||||
if count: continue
|
||||
|
||||
pdata = {
|
||||
"level":x['level'],
|
||||
"msg":x['msg'],
|
||||
"state":1,
|
||||
"expire":int(time.time()) + (int(x['expire']) * 86400),
|
||||
"addtime": int(time.time())
|
||||
}
|
||||
public.M('messages').insert(pdata)
|
||||
#cache.set('get_cloud_messages',3600)
|
||||
return public.returnMsg(True,'同步成功!')
|
||||
|
||||
def get_messages(self,args = None):
|
||||
'''
|
||||
@name 获取消息列表
|
||||
@author hwliang <2020-05-18>
|
||||
@return list
|
||||
'''
|
||||
self.get_cloud_messages(args)
|
||||
data = public.M('messages').where('state=? and expire>?',(1,int(time.time()))).order("id desc").select()
|
||||
return data
|
||||
|
||||
@@ -33,6 +88,7 @@ class panelMessage:
|
||||
@author hwliang <2020-05-18>
|
||||
@return list
|
||||
'''
|
||||
self.get_cloud_messages(args)
|
||||
data = public.M('messages').order("id desc").select()
|
||||
return data
|
||||
|
||||
|
||||
+35
-27
@@ -153,7 +153,6 @@ class panelPlugin:
|
||||
'Depend on the following software, please install first [%s]' %
|
||||
versionInfo[
|
||||
'dependent'])
|
||||
|
||||
#安装插件
|
||||
def install_plugin(self,get):
|
||||
if not self.check_sys_write(): return public.returnMsg(False,'CANT_WRITE_SYS_DIR')
|
||||
@@ -163,6 +162,8 @@ class panelPlugin:
|
||||
check_result = self.check_install_limit(get)
|
||||
if check_result:
|
||||
return check_result
|
||||
if pluginInfo['name'] in ['dns_manager','mail_sys']:
|
||||
pluginInfo['type'] = 5
|
||||
if pluginInfo['type'] != 5:
|
||||
result = self.install_sync(pluginInfo,get)
|
||||
else:
|
||||
@@ -170,15 +171,20 @@ class panelPlugin:
|
||||
try:
|
||||
if 'status' in result:
|
||||
if result['status']:
|
||||
public.httpPost('{}/api/setupCount/setupPlugin'.format(self.__official_url),{"pid":pluginInfo['id'],'p_name':pluginInfo['name']},3)
|
||||
get.force = 1
|
||||
self.get_cloud_list(get)
|
||||
public.arequests('post','{}/api/setupCount/setupPlugin'.format(self.__official_url),data={"pid":pluginInfo['id'],'p_name':pluginInfo['name']},timeout=3)
|
||||
# get.force = 1
|
||||
# self.get_cloud_list(get)
|
||||
except:pass
|
||||
return result
|
||||
|
||||
#同步安装
|
||||
def install_sync(self,pluginInfo,get):
|
||||
import panelAuth
|
||||
try:
|
||||
token = panelAuth.panelAuth().create_serverid(None)['token']
|
||||
except:
|
||||
# return public.returnMsg(False,'Please log in as aaPanel account first')
|
||||
token = None
|
||||
if 'download' in pluginInfo['versions'][0]:
|
||||
tmp_path = '/www/server/panel/temp'
|
||||
if not os.path.exists(tmp_path): os.makedirs(tmp_path,mode=384)
|
||||
@@ -187,14 +193,14 @@ class panelPlugin:
|
||||
public.downloadFile('{}/api/plugin/download?filename={}&token={}'.format(
|
||||
self.__official_url,
|
||||
pluginInfo['versions'][0]['download'],
|
||||
panelAuth.panelAuth().create_serverid(None)['token']
|
||||
token
|
||||
),toFile)
|
||||
if public.FileMd5(toFile) != pluginInfo['versions'][0]['md5']: return public.returnMsg(False,'CHECK_FILE_HASH')
|
||||
update = False
|
||||
if os.path.exists(pluginInfo['install_checks']): update =pluginInfo['versions'][0]['version_msg']
|
||||
return self.update_zip(None,toFile,update)
|
||||
else:
|
||||
download_url = public.get_url() + '/install/plugin/' + pluginInfo['name'] + '/install.sh'
|
||||
download_url = public.get_url() + '/install/plugin/' + pluginInfo['name'] + '_en/install.sh'
|
||||
toFile = '/tmp/%s.sh' % pluginInfo['name']
|
||||
public.downloadFile(download_url,toFile)
|
||||
self.set_pyenv(toFile)
|
||||
@@ -418,30 +424,23 @@ class panelPlugin:
|
||||
is_plugin = True
|
||||
import panelMessage #引用消息提醒模块
|
||||
pm = panelMessage.panelMessage()
|
||||
pm.remove_message_all()
|
||||
|
||||
#企业版到期提醒
|
||||
if not data['ltd'] in [-1]:
|
||||
level,expire_day = self.get_level_msg('ltd',s_time,data['ltd'])
|
||||
self.add_expire_msg('企业版',level,'ltd',expire_day,100000032,data['ltd'])
|
||||
pm.remove_message_level('pro')
|
||||
return True
|
||||
if not data['ltd'] in [-1] :
|
||||
|
||||
if data['pro'] < 0 or (data['pro'] - s_time) / 86400 < 15 :
|
||||
level,expire_day = self.get_level_msg('ltd',s_time,data['ltd'])
|
||||
print(level,expire_day)
|
||||
self.add_expire_msg('企业版',level,'ltd',expire_day,100000046,data['ltd'])
|
||||
pm.remove_message_level('pro')
|
||||
return True
|
||||
|
||||
#专业版到期提醒
|
||||
if not data['pro'] in [-1,0]:
|
||||
level,expire_day = self.get_level_msg('pro',s_time,data['pro'])
|
||||
self.add_expire_msg('专业版',level,'pro',expire_day,100000011,data['pro'])
|
||||
self.add_expire_msg('专业版',level,'pro',expire_day,100000030,data['pro'])
|
||||
pm.remove_message_level('ltd')
|
||||
is_plugin = False
|
||||
|
||||
#单独购买的插件到期提醒
|
||||
# for p in data['list']:
|
||||
# #跳过非企业版或专业版插件
|
||||
# if not p['type'] in [8,12]: continue
|
||||
# #已经是专业版的情况下跳过专业版插件
|
||||
# if not is_plugin and p['type'] == 8: continue
|
||||
# if not p['endtime'] in [-1,0]:
|
||||
# level,expire_day = self.get_level_msg(p['name'],s_time,p['endtime'])
|
||||
# self.add_expire_msg(p['title'],level,p['name'],expire_day,p['pid'],p['endtime'])
|
||||
return True
|
||||
|
||||
|
||||
@@ -1672,10 +1671,11 @@ class panelPlugin:
|
||||
if conf.find('/www/server/stop') == -1: pstatus = True
|
||||
if os.path.exists('/usr/local/lsws/bin/lswsctrl'):
|
||||
result = self._get_ols_myphpadmin_info()
|
||||
phpversion = result['php_version']
|
||||
phpport = result['php_port']
|
||||
pauth = result['pauth']
|
||||
pstatus = result['pstatus']
|
||||
if result:
|
||||
phpversion = result['php_version']
|
||||
phpport = result['php_port']
|
||||
pauth = result['pauth']
|
||||
pstatus = result['pstatus']
|
||||
try:
|
||||
vfile = setupPath + '/phpmyadmin/version.pl'
|
||||
if os.path.exists(vfile):
|
||||
@@ -1699,6 +1699,7 @@ class panelPlugin:
|
||||
def _get_ols_myphpadmin_info(self):
|
||||
filename = "/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf"
|
||||
conf = public.readFile(filename)
|
||||
if not conf:return False
|
||||
reg = '/usr/local/lsws/lsphp(\d+)/bin/lsphp'
|
||||
php_v = re.search(reg,conf)
|
||||
phpversion = '73'
|
||||
@@ -2050,6 +2051,13 @@ class panelPlugin:
|
||||
p_info = public.ReadFile(plugin_path + '/info.json')
|
||||
public.ExecShell("rm -rf /www/server/panel/temp/*")
|
||||
if p_info:
|
||||
#----- 增加图标复制 hwliang<2021-03-23> -----#
|
||||
icon_sfile = plugin_path + '/icon.png'
|
||||
icon_dfile = '/www/server/panel/BTPanel/static/img/soft_ico/ico-{}.png'.format(get.plugin_name)
|
||||
if os.path.exists(plugin_path + '/icon.png'):
|
||||
import shutil
|
||||
shutil.copyfile(icon_sfile,icon_dfile)
|
||||
#----- 增加图标复制 END -----#
|
||||
public.WriteLog('TYPE_SOFT','INSTALL_THIRD_PARDY_PLUG' ,(json.loads(p_info)['title'],))
|
||||
return public.returnMsg(True,'PLUGIN_INSTALL_SUCCESS')
|
||||
public.ExecShell("rm -rf " + plugin_path)
|
||||
|
||||
+11
-1
@@ -663,7 +663,17 @@ class panelSSL:
|
||||
if not tmp: continue
|
||||
tmp1 = json.loads(tmp)
|
||||
data.append(tmp1)
|
||||
return data
|
||||
if not data:
|
||||
lets_file = '/www/server/panel/config/letsencrypt.json'
|
||||
tmp = public.readFile(ltes_file)
|
||||
if not tmp:
|
||||
return []
|
||||
tmp = json(tmp)
|
||||
for i in tmp['orders']:
|
||||
data.append({"domains":tmp['orders'][i]['domains'],
|
||||
"notAfter":tmp['orders'][i]['cert_timeout'],
|
||||
"save_path":tmp['orders'][i]['save_path']
|
||||
})
|
||||
except:
|
||||
return []
|
||||
|
||||
|
||||
+1542
-1453
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -67,7 +67,7 @@ class bt_task:
|
||||
data = sql.field('id,name,type,shell,other,status,exectime,endtime,addtime').order(
|
||||
'id asc').limit('10').select()
|
||||
if type(data) == str:
|
||||
public.WriteLog('TASK_QUEUE',data)
|
||||
public.WriteLog('TASK_QUEUE', data,not_web = self.not_web)
|
||||
return []
|
||||
if not 'num' in get:
|
||||
get.num = 15
|
||||
|
||||
+41
-36
@@ -22,7 +22,7 @@ class panelWarning:
|
||||
|
||||
|
||||
def get_list(self,args):
|
||||
self.sync_rule()
|
||||
#self.sync_rule()
|
||||
p = public.get_modules('class/safe_warning')
|
||||
|
||||
data = {
|
||||
@@ -32,6 +32,9 @@ class panelWarning:
|
||||
}
|
||||
|
||||
for m_name in p.__dict__.keys():
|
||||
# 忽略的检查项
|
||||
if p[m_name]._level == 0: continue
|
||||
|
||||
m_info = {
|
||||
'title': p[m_name]._title,
|
||||
'm_name': m_name,
|
||||
@@ -82,45 +85,47 @@ class panelWarning:
|
||||
@author hwliang<2020-08-05>
|
||||
@return void
|
||||
'''
|
||||
try:
|
||||
dep_path = '/www/server/panel/class/safe_warning'
|
||||
local_version_file = self.__path + '/version.pl'
|
||||
last_sync_time = local_version_file = self.__path + '/last_sync.pl'
|
||||
if os.path.exists(dep_path):
|
||||
if os.path.exists(last_sync_time):
|
||||
if int(public.readFile(last_sync_time)) > time.time():
|
||||
return
|
||||
else:
|
||||
if os.path.exists(local_version_file): os.remove(local_version_file)
|
||||
# try:
|
||||
# dep_path = '/www/server/panel/class/safe_warning'
|
||||
# local_version_file = self.__path + '/version.pl'
|
||||
# last_sync_time = local_version_file = self.__path + '/last_sync.pl'
|
||||
# if os.path.exists(dep_path):
|
||||
# if os.path.exists(last_sync_time):
|
||||
# if int(public.readFile(last_sync_time)) > time.time():
|
||||
# return
|
||||
# else:
|
||||
# if os.path.exists(local_version_file): os.remove(local_version_file)
|
||||
|
||||
download_url = public.get_url()
|
||||
version_url = download_url + '/install/warning/version.txt'
|
||||
cloud_version = public.httpGet(version_url)
|
||||
if cloud_version: cloud_version = cloud_version.strip()
|
||||
# download_url = public.get_url()
|
||||
# version_url = download_url + '/install/warning/version.txt'
|
||||
# cloud_version = public.httpGet(version_url)
|
||||
# if cloud_version: cloud_version = cloud_version.strip()
|
||||
|
||||
local_version = public.readFile(local_version_file)
|
||||
if local_version:
|
||||
if cloud_version == local_version:
|
||||
return
|
||||
# local_version = public.readFile(local_version_file)
|
||||
# if local_version:
|
||||
# if cloud_version == local_version:
|
||||
# return
|
||||
|
||||
# tmp_file = '/tmp/bt_safe_warning.zip'
|
||||
# public.ExecShell('wget -O {} {} -T 5'.format(tmp_file,download_url + '/install/warning/safe_warning.zip'))
|
||||
# if not os.path.exists(tmp_file):
|
||||
# return
|
||||
|
||||
# if os.path.getsize(tmp_file) < 2129:
|
||||
# os.remove(tmp_file)
|
||||
# return
|
||||
|
||||
tmp_file = '/tmp/bt_safe_warning.zip'
|
||||
public.ExecShell('wget -O {} {} -T 5'.format(tmp_file,download_url + '/install/warning/safe_warning_en.zip'))
|
||||
if not os.path.exists(tmp_file):
|
||||
return
|
||||
# if not os.path.exists(dep_path):
|
||||
# os.makedirs(dep_path,384)
|
||||
# public.ExecShell("unzip -o {} -d {}/ >/dev/null".format(tmp_file,dep_path))
|
||||
# public.writeFile(local_version_file,cloud_version)
|
||||
# public.writeFile(last_sync_time,str(int(time.time() + 7200)))
|
||||
# if os.path.exists(tmp_file): os.remove(tmp_file)
|
||||
# public.ExecShell("chmod -R 600 {}".format(dep_path))
|
||||
# except:
|
||||
# pass
|
||||
|
||||
|
||||
if os.path.getsize(tmp_file) < 2129:
|
||||
os.remove(tmp_file)
|
||||
return
|
||||
|
||||
if not os.path.exists(dep_path):
|
||||
os.makedirs(dep_path,384)
|
||||
public.ExecShell("unzip -o {} -d {}/ >/dev/null".format(tmp_file,dep_path))
|
||||
public.writeFile(local_version_file,cloud_version)
|
||||
public.writeFile(last_sync_time,str(int(time.time() + 7200)))
|
||||
if os.path.exists(tmp_file): os.remove(tmp_file)
|
||||
public.ExecShell("chmod -R 600 {}".format(dep_path))
|
||||
except:
|
||||
pass
|
||||
|
||||
def set_ignore(self,args):
|
||||
'''
|
||||
|
||||
@@ -31,6 +31,7 @@ function get_info(){
|
||||
$data['0db']['PDO-MySQL'] = in_array('pdo_mysql',$data['modules']);
|
||||
$data['0db']['SqlServer'] =in_array('mssql',$data['modules']);
|
||||
$data['0db']['PDO-SqlServer'] = in_array('pdo_mssql',$data['modules']);
|
||||
if(!$data['0db']['PDO-SqlServer']) $data['0db']['PDO-SqlServer'] = in_array('pdo_sqlsrv',$data['modules']);
|
||||
$data['0db']['Sqlite3'] = in_array('sqlite3',$data['modules']);
|
||||
$data['0db']['PDO-Sqlite'] = in_array('pdo_sqlite',$data['modules']);
|
||||
$data['0db']['PgSQL'] = get_extension_funcs('pg_query')?true:false;
|
||||
|
||||
+139
-138
@@ -13,25 +13,25 @@
|
||||
|
||||
import public,json,os,time,sys,re
|
||||
from BTPanel import session
|
||||
class obj: id=0;
|
||||
class obj: id=0
|
||||
class plugin_deployment:
|
||||
__setupPath = 'data';
|
||||
__panelPath = '/www/server/panel';
|
||||
__setupPath = 'data'
|
||||
__panelPath = '/www/server/panel'
|
||||
logPath = 'data/deployment_speed.json'
|
||||
__tmp = '/www/server/panel/temp/'
|
||||
timeoutCount = 0;
|
||||
oldTime = 0;
|
||||
timeoutCount = 0
|
||||
oldTime = 0
|
||||
|
||||
#获取列表
|
||||
def GetList(self,get):
|
||||
self.GetCloudList(get);
|
||||
jsonFile = self.__panelPath + '/data/deployment_list.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'Profile does not exist!');
|
||||
self.GetCloudList(get)
|
||||
jsonFile = self.__panelPath + '/data/deployment_list.json'
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'Profile does not exist!')
|
||||
data = {}
|
||||
data = self.get_input_list(json.loads(public.readFile(jsonFile)));
|
||||
data = self.get_input_list(json.loads(public.readFile(jsonFile)))
|
||||
|
||||
if not hasattr(get,'type'):
|
||||
get.type = 0;
|
||||
get.type = 0
|
||||
else:
|
||||
get.type = int(get.type)
|
||||
if not hasattr(get,'search'):
|
||||
@@ -39,32 +39,32 @@ class plugin_deployment:
|
||||
m = 0
|
||||
else:
|
||||
if sys.version_info[0] == 2:
|
||||
search = get.search.encode('utf-8').lower();
|
||||
search = get.search.encode('utf-8').lower()
|
||||
else:
|
||||
search = get.search.lower();
|
||||
search = get.search.lower()
|
||||
m = 1
|
||||
|
||||
tmp = [];
|
||||
tmp = []
|
||||
for d in data['list']:
|
||||
i=0;
|
||||
i=0
|
||||
if get.type > 0:
|
||||
if get.type == d['type']: i+=1
|
||||
else:
|
||||
i+=1
|
||||
if search:
|
||||
if d['name'].lower().find(search) != -1: i+=1;
|
||||
if d['title'].lower().find(search) != -1: i+=1;
|
||||
if d['ps'].lower().find(search) != -1: i+=1;
|
||||
if get.type > 0 and get.type != d['type']: i -= 1;
|
||||
if d['name'].lower().find(search) != -1: i+=1
|
||||
if d['title'].lower().find(search) != -1: i+=1
|
||||
if d['ps'].lower().find(search) != -1: i+=1
|
||||
if get.type > 0 and get.type != d['type']: i -= 1
|
||||
|
||||
if i>m:
|
||||
del(d['versions'][0]['download'])
|
||||
del(d['versions'][0]['md5'])
|
||||
d = self.get_icon(d)
|
||||
tmp.append(d);
|
||||
tmp.append(d)
|
||||
|
||||
data['list'] = tmp;
|
||||
return data;
|
||||
data['list'] = tmp
|
||||
return data
|
||||
|
||||
#获取图标
|
||||
def get_icon(self,pinfo):
|
||||
@@ -81,16 +81,16 @@ class plugin_deployment:
|
||||
|
||||
#获取插件列表
|
||||
def GetDepList(self,get):
|
||||
jsonFile = self.__setupPath + '/deployment_list.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'Profile does not exist!');
|
||||
jsonFile = self.__setupPath + '/deployment_list.json'
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'Profile does not exist!')
|
||||
data = {}
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
return self.get_input_list(data);
|
||||
data = json.loads(public.readFile(jsonFile))
|
||||
return self.get_input_list(data)
|
||||
|
||||
#获取本地导入的插件
|
||||
def get_input_list(self,data):
|
||||
try:
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json';
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json'
|
||||
if not os.path.exists(jsonFile): return data
|
||||
i_data = json.loads(public.readFile(jsonFile))
|
||||
for d in i_data:
|
||||
@@ -101,33 +101,34 @@ class plugin_deployment:
|
||||
#从云端获取列表
|
||||
def GetCloudList(self,get):
|
||||
try:
|
||||
jsonFile = self.__setupPath + '/deployment_list.json';
|
||||
jsonFile = self.__setupPath + '/deployment_list.json'
|
||||
if not 'package' in session or not os.path.exists(jsonFile) or hasattr(get,'force'):
|
||||
downloadUrl = 'http://www.bt.cn/api/panel/get_deplist';
|
||||
tmp = json.loads(public.httpGet(downloadUrl,3));
|
||||
if not tmp: return public.returnMsg(False,'Failed to get from the cloud!');
|
||||
public.writeFile(jsonFile,json.dumps(tmp));
|
||||
downloadUrl = 'http://www.bt.cn/api/panel/get_deplist'
|
||||
pdata = public.get_pdata()
|
||||
tmp = json.loads(public.httpPost(downloadUrl,pdata,3))
|
||||
if not tmp: return public.returnMsg(False,'Failed to get from the cloud!')
|
||||
public.writeFile(jsonFile,json.dumps(tmp))
|
||||
session['package'] = True
|
||||
return public.returnMsg(True,'Update completed!');
|
||||
return public.returnMsg(True,'No need to update!');
|
||||
return public.returnMsg(True,'Update completed!')
|
||||
return public.returnMsg(True,'No need to update!')
|
||||
except:
|
||||
return public.returnMsg(False,'Failed to get from the cloud!');
|
||||
return public.returnMsg(False,'Failed to get from the cloud!')
|
||||
|
||||
|
||||
|
||||
#导入程序包
|
||||
def AddPackage(self,get):
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json';
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json'
|
||||
if not os.path.exists(jsonFile):
|
||||
public.writeFile(jsonFile,'[]')
|
||||
pinfo = {}
|
||||
pinfo['name'] = get.name;
|
||||
pinfo['title'] = get.title;
|
||||
pinfo['version'] = get.version;
|
||||
pinfo['php'] = get.php;
|
||||
pinfo['ps'] = get.ps;
|
||||
pinfo['name'] = get.name
|
||||
pinfo['title'] = get.title
|
||||
pinfo['version'] = get.version
|
||||
pinfo['php'] = get.php
|
||||
pinfo['ps'] = get.ps
|
||||
pinfo['official'] = '#'
|
||||
pinfo['sort'] = 1000;
|
||||
pinfo['sort'] = 1000
|
||||
pinfo['min_image'] = ''
|
||||
pinfo['id'] = 0
|
||||
pinfo['type'] = 100
|
||||
@@ -154,24 +155,24 @@ class plugin_deployment:
|
||||
"version_msg": "test2"}
|
||||
version['md5'] = self.GetFileMd5(s_file)
|
||||
pinfo['versions'].append(version)
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
data = json.loads(public.readFile(jsonFile))
|
||||
is_exists = False
|
||||
for i in range(len(data)):
|
||||
if data[i]['name'] == pinfo['name']:
|
||||
data[i] = pinfo
|
||||
is_exists = True
|
||||
|
||||
if not is_exists: data.append(pinfo);
|
||||
if not is_exists: data.append(pinfo)
|
||||
|
||||
public.writeFile(jsonFile,json.dumps(data));
|
||||
return public.returnMsg(True,'Import Success!');
|
||||
public.writeFile(jsonFile,json.dumps(data))
|
||||
return public.returnMsg(True,'Import Success!')
|
||||
|
||||
#取本地包信息
|
||||
def GetPackageOther(self,get):
|
||||
p_name = get.p_name
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json';
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json'
|
||||
if not os.path.exists(jsonFile): public.returnMsg(False,'could not find [%s]' % p_name)
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
data = json.loads(public.readFile(jsonFile))
|
||||
|
||||
for i in range(len(data)):
|
||||
if data[i]['name'] == p_name: return data[i]
|
||||
@@ -180,23 +181,23 @@ class plugin_deployment:
|
||||
|
||||
#删除程序包
|
||||
def DelPackage(self,get):
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'Profile does not exist!');
|
||||
jsonFile = self.__setupPath + '/deployment_list_other.json'
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'Profile does not exist!')
|
||||
|
||||
data = {}
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
data = json.loads(public.readFile(jsonFile))
|
||||
|
||||
tmp = [];
|
||||
tmp = []
|
||||
for d in data:
|
||||
if d['name'] == get.dname:
|
||||
s_file = self.__panelPath + '/package/' + d['name'] + '.zip'
|
||||
if os.path.exists(s_file): os.remove(s_file)
|
||||
continue;
|
||||
tmp.append(d);
|
||||
continue
|
||||
tmp.append(d)
|
||||
|
||||
data = tmp;
|
||||
public.writeFile(jsonFile,json.dumps(data));
|
||||
return public.returnMsg(True,'Successfully deleted!');
|
||||
data = tmp
|
||||
public.writeFile(jsonFile,json.dumps(data))
|
||||
return public.returnMsg(True,'Successfully deleted!')
|
||||
|
||||
#下载文件
|
||||
def DownloadFile(self,url,filename):
|
||||
@@ -205,15 +206,15 @@ class plugin_deployment:
|
||||
if not os.path.exists(path): os.makedirs(path)
|
||||
import urllib,socket
|
||||
socket.setdefaulttimeout(10)
|
||||
self.pre = 0;
|
||||
self.oldTime = time.time();
|
||||
self.pre = 0
|
||||
self.oldTime = time.time()
|
||||
if sys.version_info[0] == 2:
|
||||
urllib.urlretrieve(url,filename=filename,reporthook= self.DownloadHook)
|
||||
else:
|
||||
urllib.request.urlretrieve(url,filename=filename,reporthook= self.DownloadHook)
|
||||
self.WriteLogs(json.dumps({'name':'Download File','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Download File','total':0,'used':0,'pre':0,'speed':0}))
|
||||
except:
|
||||
if self.timeoutCount > 5: return;
|
||||
if self.timeoutCount > 5: return
|
||||
self.timeoutCount += 1
|
||||
time.sleep(5)
|
||||
self.DownloadFile(url,filename)
|
||||
@@ -223,14 +224,14 @@ class plugin_deployment:
|
||||
used = count * blockSize
|
||||
pre1 = int((100.0 * used / totalSize))
|
||||
if self.pre != pre1:
|
||||
dspeed = used / (time.time() - self.oldTime);
|
||||
dspeed = used / (time.time() - self.oldTime)
|
||||
speed = {'name':'Download File','total':totalSize,'used':used,'pre':self.pre,'speed':dspeed}
|
||||
self.WriteLogs(json.dumps(speed))
|
||||
self.pre = pre1
|
||||
|
||||
#写输出日志
|
||||
def WriteLogs(self,logMsg):
|
||||
fp = open(self.logPath,'w+');
|
||||
fp = open(self.logPath,'w+')
|
||||
fp.write(logMsg)
|
||||
fp.close()
|
||||
|
||||
@@ -240,8 +241,8 @@ class plugin_deployment:
|
||||
#param string php_version PHP版本
|
||||
def SetupPackage(self,get):
|
||||
name = get.dname
|
||||
site_name = get.site_name;
|
||||
php_version = get.php_version;
|
||||
site_name = get.site_name
|
||||
php_version = get.php_version
|
||||
#取基础信息
|
||||
find = public.M('sites').where('name=?',(site_name,)).field('id,path,name').find()
|
||||
if not 'path' in find:
|
||||
@@ -249,57 +250,57 @@ class plugin_deployment:
|
||||
path = find['path']
|
||||
if path.replace('//','/') == '/': return public.returnMsg(False,'Dangerous website root directory!')
|
||||
#获取包信息
|
||||
pinfo = self.GetPackageInfo(name);
|
||||
pinfo = self.GetPackageInfo(name)
|
||||
id = pinfo['id']
|
||||
if not pinfo: return public.returnMsg(False,'The specified package does not exist.!');
|
||||
if not pinfo: return public.returnMsg(False,'The specified package does not exist.!')
|
||||
|
||||
#检查本地包
|
||||
self.WriteLogs(json.dumps({'name':'Verifying package...','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Verifying package...','total':0,'used':0,'pre':0,'speed':0}))
|
||||
pack_path = self.__panelPath + '/package'
|
||||
if not os.path.exists(pack_path): os.makedirs(pack_path,384)
|
||||
packageZip = pack_path + '/'+ name + '.zip';
|
||||
isDownload = False;
|
||||
packageZip = pack_path + '/'+ name + '.zip'
|
||||
isDownload = False
|
||||
if os.path.exists(packageZip):
|
||||
md5str = self.GetFileMd5(packageZip);
|
||||
if md5str != pinfo['versions'][0]['md5']: isDownload = True;
|
||||
md5str = self.GetFileMd5(packageZip)
|
||||
if md5str != pinfo['versions'][0]['md5']: isDownload = True
|
||||
else:
|
||||
isDownload = True;
|
||||
isDownload = True
|
||||
|
||||
#下载文件
|
||||
if isDownload:
|
||||
self.WriteLogs(json.dumps({'name':'Downloading file ...','total':0,'used':0,'pre':0,'speed':0}));
|
||||
if pinfo['versions'][0]['download']: self.DownloadFile('http://www.bt.cn/api/Pluginother/get_file?fname=' + pinfo['versions'][0]['download'], packageZip);
|
||||
self.WriteLogs(json.dumps({'name':'Downloading file ...','total':0,'used':0,'pre':0,'speed':0}))
|
||||
if pinfo['versions'][0]['download']: self.DownloadFile('http://www.bt.cn/api/Pluginother/get_file?fname=' + pinfo['versions'][0]['download'], packageZip)
|
||||
|
||||
if not os.path.exists(packageZip): return public.returnMsg(False,'File download failed!' + packageZip);
|
||||
if not os.path.exists(packageZip): return public.returnMsg(False,'File download failed!' + packageZip)
|
||||
|
||||
pinfo = self.set_temp_file(packageZip,path)
|
||||
if not pinfo: return public.returnMsg(False,'Cannot find [aaPanel Auto Deployment Configuration File] in the installation package')
|
||||
|
||||
#设置权限
|
||||
self.WriteLogs(json.dumps({'name':'Setting permissions','total':0,'used':0,'pre':0,'speed':0}));
|
||||
public.ExecShell('chmod -R 755 ' + path);
|
||||
public.ExecShell('chown -R www.www ' + path);
|
||||
self.WriteLogs(json.dumps({'name':'Setting permissions','total':0,'used':0,'pre':0,'speed':0}))
|
||||
public.ExecShell('chmod -R 755 ' + path)
|
||||
public.ExecShell('chown -R www.www ' + path)
|
||||
if pinfo['chmod']:
|
||||
for chm in pinfo['chmod']:
|
||||
public.ExecShell('chmod -R ' + str(chm['mode']) + ' ' + (path + '/' + chm['path']).replace('//','/'));
|
||||
public.ExecShell('chmod -R ' + str(chm['mode']) + ' ' + (path + '/' + chm['path']).replace('//','/'))
|
||||
|
||||
#安装PHP扩展
|
||||
self.WriteLogs(json.dumps({'name':'Install the necessary PHP extensions','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Install the necessary PHP extensions','total':0,'used':0,'pre':0,'speed':0}))
|
||||
import files
|
||||
mfile = files.files();
|
||||
if type(pinfo['php_ext']) != list : pinfo['php_ext'] = pinfo['php_ext'].strip().split(',')
|
||||
for ext in pinfo['php_ext']:
|
||||
if ext == 'pathinfo':
|
||||
import config
|
||||
con = config.config();
|
||||
get.version = php_version;
|
||||
get.type = 'on';
|
||||
con.setPathInfo(get);
|
||||
con = config.config()
|
||||
get.version = php_version
|
||||
get.type = 'on'
|
||||
con.setPathInfo(get)
|
||||
else:
|
||||
get.name = ext
|
||||
get.version = php_version
|
||||
get.type = '1';
|
||||
mfile.InstallSoft(get);
|
||||
get.type = '1'
|
||||
mfile.InstallSoft(get)
|
||||
|
||||
#解禁PHP函数
|
||||
if 'enable_functions' in pinfo:
|
||||
@@ -308,48 +309,48 @@ class plugin_deployment:
|
||||
php_f = public.GetConfigValue('setup_path') + '/php/' + php_version + '/etc/php.ini'
|
||||
php_c = public.readFile(php_f)
|
||||
rep = "disable_functions\s*=\s{0,1}(.*)\n"
|
||||
tmp = re.search(rep,php_c).groups();
|
||||
disable_functions = tmp[0].split(',');
|
||||
tmp = re.search(rep,php_c).groups()
|
||||
disable_functions = tmp[0].split(',')
|
||||
for fun in pinfo['enable_functions']:
|
||||
fun = fun.strip()
|
||||
if fun in disable_functions: disable_functions.remove(fun)
|
||||
disable_functions = ','.join(disable_functions)
|
||||
php_c = re.sub(rep, 'disable_functions = ' + disable_functions + "\n", php_c);
|
||||
php_c = re.sub(rep, 'disable_functions = ' + disable_functions + "\n", php_c)
|
||||
public.writeFile(php_f,php_c)
|
||||
public.phpReload(php_version)
|
||||
except:pass
|
||||
|
||||
|
||||
#执行额外shell进行依赖安装
|
||||
self.WriteLogs(json.dumps({'name':'Execute extra SHELL','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Execute extra SHELL','total':0,'used':0,'pre':0,'speed':0}))
|
||||
if os.path.exists(path+'/install.sh'):
|
||||
public.ExecShell('cd '+path+' && bash ' + 'install.sh ' + find['name'] + " &> install.log");
|
||||
public.ExecShell('cd '+path+' && bash ' + 'install.sh ' + find['name'] + " &> install.log")
|
||||
public.ExecShell('rm -f ' + path+'/install.sh')
|
||||
|
||||
#是否执行Composer
|
||||
if os.path.exists(path + '/composer.json'):
|
||||
self.WriteLogs(json.dumps({'name':'Execute Composer','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Execute Composer','total':0,'used':0,'pre':0,'speed':0}))
|
||||
if not os.path.exists(path + '/composer.lock'):
|
||||
execPHP = '/www/server/php/' + php_version +'/bin/php';
|
||||
execPHP = '/www/server/php/' + php_version +'/bin/php'
|
||||
if execPHP:
|
||||
if public.get_url().find('125.88'):
|
||||
public.ExecShell('cd ' +path+' && '+execPHP+' /usr/bin/composer config repo.packagist composer https://packagist.phpcomposer.com');
|
||||
import panelSite;
|
||||
public.ExecShell('cd ' +path+' && '+execPHP+' /usr/bin/composer config repo.packagist composer https://packagist.phpcomposer.com')
|
||||
import panelSite
|
||||
phpini = '/www/server/php/' + php_version + '/etc/php.ini'
|
||||
phpiniConf = public.readFile(phpini);
|
||||
phpiniConf = phpiniConf.replace('proc_open,proc_get_status,','');
|
||||
public.writeFile(phpini,phpiniConf);
|
||||
public.ExecShell('nohup cd '+path+' && '+execPHP+' /usr/bin/composer install -vvv > /tmp/composer.log 2>&1 &');
|
||||
phpiniConf = public.readFile(phpini)
|
||||
phpiniConf = phpiniConf.replace('proc_open,proc_get_status,','')
|
||||
public.writeFile(phpini,phpiniConf)
|
||||
public.ExecShell('nohup cd '+path+' && '+execPHP+' /usr/bin/composer install -vvv > /tmp/composer.log 2>&1 &')
|
||||
|
||||
#写伪静态
|
||||
self.WriteLogs(json.dumps({'name':'Set URL rewrite','total':0,'used':0,'pre':0,'speed':0}));
|
||||
swfile = path + '/nginx.rewrite';
|
||||
self.WriteLogs(json.dumps({'name':'Set URL rewrite','total':0,'used':0,'pre':0,'speed':0}))
|
||||
swfile = path + '/nginx.rewrite'
|
||||
if os.path.exists(swfile):
|
||||
rewriteConf = public.readFile(swfile);
|
||||
dwfile = self.__panelPath + '/vhost/rewrite/' + site_name + '.conf';
|
||||
public.writeFile(dwfile,rewriteConf);
|
||||
rewriteConf = public.readFile(swfile)
|
||||
dwfile = self.__panelPath + '/vhost/rewrite/' + site_name + '.conf'
|
||||
public.writeFile(dwfile,rewriteConf)
|
||||
|
||||
swfile = path + '/.htaccess';
|
||||
swfile = path + '/.htaccess'
|
||||
if os.path.exists(swfile):
|
||||
swpath = (path + '/'+ pinfo['run_path'] + '/.htaccess').replace('//','/')
|
||||
if pinfo['run_path'] != '/' and not os.path.exists(swpath):
|
||||
@@ -366,23 +367,23 @@ class plugin_deployment:
|
||||
if rm_file_body.find('panel-heading') != -1: os.remove(rm_file)
|
||||
|
||||
#设置运行目录
|
||||
self.WriteLogs(json.dumps({'name':'Set the run directory','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Set the run directory','total':0,'used':0,'pre':0,'speed':0}))
|
||||
if pinfo['run_path'] != '/':
|
||||
import panelSite;
|
||||
siteObj = panelSite.panelSite();
|
||||
mobj = obj();
|
||||
mobj.id = find['id'];
|
||||
mobj.runPath = pinfo['run_path'];
|
||||
siteObj.SetSiteRunPath(mobj);
|
||||
import panelSite
|
||||
siteObj = panelSite.panelSite()
|
||||
mobj = obj()
|
||||
mobj.id = find['id']
|
||||
mobj.runPath = pinfo['run_path']
|
||||
siteObj.SetSiteRunPath(mobj)
|
||||
|
||||
#导入数据
|
||||
self.WriteLogs(json.dumps({'name':'Import database','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'Import database','total':0,'used':0,'pre':0,'speed':0}))
|
||||
if os.path.exists(path+'/import.sql'):
|
||||
databaseInfo = public.M('databases').where('pid=?',(find['id'],)).field('username,password').find();
|
||||
databaseInfo = public.M('databases').where('pid=?',(find['id'],)).field('username,password').find()
|
||||
if databaseInfo:
|
||||
public.ExecShell('/www/server/mysql/bin/mysql -u' + databaseInfo['username'] + ' -p' + databaseInfo['password'] + ' ' + databaseInfo['username'] + ' < ' + path + '/import.sql');
|
||||
public.ExecShell('rm -f ' + path + '/import.sql');
|
||||
siteConfigFile = (path + '/' + pinfo['db_config']).replace('//','/');
|
||||
public.ExecShell('/www/server/mysql/bin/mysql -u' + databaseInfo['username'] + ' -p' + databaseInfo['password'] + ' ' + databaseInfo['username'] + ' < ' + path + '/import.sql')
|
||||
public.ExecShell('rm -f ' + path + '/import.sql')
|
||||
siteConfigFile = (path + '/' + pinfo['db_config']).replace('//','/')
|
||||
if os.path.exists(siteConfigFile):
|
||||
siteConfig = public.readFile(siteConfigFile)
|
||||
siteConfig = siteConfig.replace('BT_DB_USERNAME',databaseInfo['username'])
|
||||
@@ -391,7 +392,7 @@ class plugin_deployment:
|
||||
public.writeFile(siteConfigFile,siteConfig)
|
||||
|
||||
#清理文件和目录
|
||||
self.WriteLogs(json.dumps({'name':'清理多余的文件','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.WriteLogs(json.dumps({'name':'清理多余的文件','total':0,'used':0,'pre':0,'speed':0}))
|
||||
if type(pinfo['remove_file']) == str : pinfo['remove_file'] = pinfo['remove_file'].strip().split(',')
|
||||
print(pinfo['remove_file'])
|
||||
for f_path in pinfo['remove_file']:
|
||||
@@ -405,17 +406,17 @@ class plugin_deployment:
|
||||
else:
|
||||
public.ExecShell("rm -rf " + filename)
|
||||
|
||||
public.serviceReload();
|
||||
if id: self.depTotal(id);
|
||||
self.WriteLogs(json.dumps({'name':'Ready to deploy','total':0,'used':0,'pre':0,'speed':0}));
|
||||
return public.returnMsg(True,pinfo);
|
||||
public.serviceReload()
|
||||
if id: self.depTotal(id)
|
||||
self.WriteLogs(json.dumps({'name':'Ready to deploy','total':0,'used':0,'pre':0,'speed':0}))
|
||||
return public.returnMsg(True,pinfo)
|
||||
|
||||
|
||||
#处理临时文件
|
||||
def set_temp_file(self,filename,path):
|
||||
public.ExecShell("rm -rf " + self.__tmp + '/*')
|
||||
self.WriteLogs(json.dumps({'name':'Unpacking the package...','total':0,'used':0,'pre':0,'speed':0}));
|
||||
public.ExecShell('unzip -o '+filename+' -d ' + self.__tmp);
|
||||
self.WriteLogs(json.dumps({'name':'Unpacking the package...','total':0,'used':0,'pre':0,'speed':0}))
|
||||
public.ExecShell('unzip -o '+filename+' -d ' + self.__tmp)
|
||||
auto_config = 'auto_install.json'
|
||||
p_info = self.__tmp + '/' + auto_config
|
||||
p_tmp = self.__tmp
|
||||
@@ -475,33 +476,33 @@ class plugin_deployment:
|
||||
#获取进度
|
||||
def GetSpeed(self,get):
|
||||
try:
|
||||
if not os.path.exists(self.logPath):return public.returnMsg(False,'There are currently no deployment tasks!');
|
||||
return json.loads(public.readFile(self.logPath));
|
||||
if not os.path.exists(self.logPath):return public.returnMsg(False,'There are currently no deployment tasks!')
|
||||
return json.loads(public.readFile(self.logPath))
|
||||
except:
|
||||
return {'name':'Ready to deploy','total':0,'used':0,'pre':0,'speed':0}
|
||||
|
||||
#获取包信息
|
||||
def GetPackageInfo(self,name):
|
||||
data = self.GetDepList(None);
|
||||
if not data: return False;
|
||||
data = self.GetDepList(None)
|
||||
if not data: return False
|
||||
for info in data['list']:
|
||||
if info['name'] == name:
|
||||
return info;
|
||||
return False;
|
||||
return info
|
||||
return False
|
||||
|
||||
#检查指定包是否存在
|
||||
def CheckPackageExists(self,name):
|
||||
data = self.GetDepList(None);
|
||||
if not data: return False;
|
||||
data = self.GetDepList(None)
|
||||
if not data: return False
|
||||
for info in data['list']:
|
||||
if info['name'] == name: return True;
|
||||
if info['name'] == name: return True
|
||||
|
||||
return False;
|
||||
return False
|
||||
|
||||
#文件的MD5值
|
||||
def GetFileMd5(self,filename):
|
||||
if not os.path.isfile(filename): return False;
|
||||
import hashlib;
|
||||
if not os.path.isfile(filename): return False
|
||||
import hashlib
|
||||
myhash = hashlib.md5()
|
||||
f = open(filename,'rb')
|
||||
while True:
|
||||
@@ -510,8 +511,8 @@ class plugin_deployment:
|
||||
break
|
||||
myhash.update(b)
|
||||
f.close()
|
||||
return myhash.hexdigest();
|
||||
return myhash.hexdigest()
|
||||
|
||||
#获取站点标识
|
||||
def GetSiteId(self,get):
|
||||
return public.M('sites').where('name=?',(get.webname,)).getField('id');
|
||||
return public.M('sites').where('name=?',(get.webname,)).getField('id')
|
||||
|
||||
+326
-36
@@ -46,14 +46,6 @@ def HttpGet(url,timeout = 6,headers = {}):
|
||||
@return string
|
||||
"""
|
||||
if is_local(): return False
|
||||
home = 'www.bt.cn'
|
||||
host_home = 'data/home_host.pl'
|
||||
old_url = url
|
||||
if url.find(home) != -1:
|
||||
if os.path.exists(host_home):
|
||||
headers['host'] = home
|
||||
url = url.replace(home,readFile(host_home))
|
||||
|
||||
import http_requests
|
||||
res = http_requests.get(url,timeout=timeout,headers = headers)
|
||||
if res.status_code == 0:
|
||||
@@ -118,14 +110,6 @@ def HttpPost(url,data,timeout = 6,headers = {}):
|
||||
return string
|
||||
"""
|
||||
if is_local(): return False
|
||||
home = 'www.bt.cn'
|
||||
host_home = 'data/home_host.pl'
|
||||
old_url = url
|
||||
if url.find(home) != -1:
|
||||
if os.path.exists(host_home):
|
||||
headers['host'] = home
|
||||
url = url.replace(home, readFile(host_home))
|
||||
|
||||
import http_requests
|
||||
res = http_requests.post(url,data=data,timeout=timeout,headers = headers)
|
||||
if res.status_code == 0:
|
||||
@@ -343,8 +327,10 @@ def ReadFile(filename,mode = 'r'):
|
||||
fp = open(filename, mode,encoding="utf-8")
|
||||
f_body = fp.read()
|
||||
fp.close()
|
||||
except Exception as ex2:
|
||||
return False
|
||||
except:
|
||||
fp = open(filename, mode,encoding="GBK")
|
||||
f_body = fp.read()
|
||||
fp.close()
|
||||
else:
|
||||
return False
|
||||
return f_body
|
||||
@@ -544,7 +530,10 @@ def GetLocalIp():
|
||||
if not ipaddress:
|
||||
url = 'http://pv.sohu.com/cityjson?ie=utf-8'
|
||||
m_str = HttpGet(url)
|
||||
ipaddress = re.search(r'\d+.\d+.\d+.\d+',m_str).group(0)
|
||||
if isinstance(m_str,bytes):
|
||||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str.decode('utf-8')).group(0)
|
||||
else:
|
||||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str).group(0)
|
||||
WriteFile(filename,ipaddress)
|
||||
c_ip = check_ip(ipaddress)
|
||||
if not c_ip: return GetHost()
|
||||
@@ -1356,11 +1345,18 @@ def get_page(count, p=1, rows=12, callback='', result='1,2,3,4,5,8'):
|
||||
# 取面板版本
|
||||
def version():
|
||||
try:
|
||||
from BTPanel import g
|
||||
return g.version
|
||||
except:
|
||||
comm = ReadFile('/www/server/panel/class/common.py')
|
||||
return re.search("g\.version\s*=\s*'(\d+\.\d+\.\d+)'", comm).groups()[0]
|
||||
return re.search("g\.version\s*=\s*'(\d+\.\d+\.\d+)'",comm).groups()[0]
|
||||
except:
|
||||
return get_panel_version()
|
||||
|
||||
def get_panel_version():
|
||||
comm = ReadFile('/www/server/panel/class/common.py')
|
||||
s_key = 'g.version = '
|
||||
s_len = len(s_key)
|
||||
s_leff = comm.find(s_key) + s_len
|
||||
version = comm[s_leff:s_leff+6].strip().strip("'")
|
||||
return version
|
||||
|
||||
|
||||
# 取文件或目录大小
|
||||
@@ -1544,7 +1540,9 @@ def check_ip_panel():
|
||||
iplist = ReadFile(ip_file)
|
||||
if iplist:
|
||||
iplist = iplist.strip()
|
||||
if not GetClientIp() in iplist.split(','):
|
||||
client_ip = GetClientIp()
|
||||
if client_ip in ['127.0.0.1','localhost','::1']: return False
|
||||
if not client_ip in iplist.split(','):
|
||||
errorStr = ReadFile('./BTPanel/templates/' + GetConfigValue('template') + '/error2.html')
|
||||
try:
|
||||
errorStr = errorStr.format(getMsg('PAGE_ERR_TITLE'),getMsg('PAGE_ERR_IP_H1'),getMsg('PAGE_ERR_IP_P1',(GetClientIp(),)),getMsg('PAGE_ERR_IP_P2'),getMsg('PAGE_ERR_IP_P3'),getMsg('NAME'),getMsg('PAGE_ERR_HELP'))
|
||||
@@ -1557,6 +1555,8 @@ def check_domain_panel():
|
||||
tmp = GetHost()
|
||||
domain = ReadFile('data/domain.conf')
|
||||
if domain:
|
||||
client_ip = GetClientIp()
|
||||
if client_ip in ['127.0.0.1','localhost','::1']: return False
|
||||
if tmp.strip().lower() != domain.strip().lower():
|
||||
errorStr = ReadFile('./BTPanel/templates/' + GetConfigValue('template') + '/error2.html')
|
||||
try:
|
||||
@@ -1586,15 +1586,16 @@ def auto_backup_panel():
|
||||
shutil.copytree(panel_paeh + '/data',backup_path + '/data')
|
||||
shutil.copytree(panel_paeh + '/config',backup_path + '/config')
|
||||
shutil.copytree(panel_paeh + '/vhost',backup_path + '/vhost')
|
||||
ExecShell("chmod -R 600 {path};chown -R root.root {path}".format(paht=b_path))
|
||||
ExecShell("chmod -R 600 {path};chown -R root.root {path}".format(path=b_path))
|
||||
time_now = time.time() - (86400 * 15)
|
||||
for f in os.listdir(b_path):
|
||||
try:
|
||||
if time.mktime(time.strptime(f, "%Y-%m-%d")) < time_now:
|
||||
path = b_path + '/' + f
|
||||
if os.path.exists(path): shutil.rmtree(path)
|
||||
except: continue
|
||||
except:pass
|
||||
if time.mktime(time.strptime(f, "%Y-%m-%d")) < time_now:
|
||||
path = b_path + '/' + f
|
||||
if os.path.exists(path): shutil.rmtree(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
#检查端口状态
|
||||
@@ -1818,6 +1819,7 @@ def sub_php_address(conf_file,rep,tsub,php_version):
|
||||
@param php_version string 指定PHP版本
|
||||
@return bool
|
||||
'''
|
||||
if not os.path.isfile(conf_file): return False
|
||||
if not os.path.exists(conf_file): return False
|
||||
conf = readFile(conf_file)
|
||||
if not conf: return False
|
||||
@@ -2020,10 +2022,7 @@ def get_linux_distribution():
|
||||
if os.path.exists(redhat_file):
|
||||
try:
|
||||
tmp = readFile(redhat_file).split()[3][0]
|
||||
if int(tmp) > 7:
|
||||
distribution = 'centos8'
|
||||
else:
|
||||
distribution = 'centos7'
|
||||
distribution = 'centos{}'.format(tmp)
|
||||
except:
|
||||
distribution = 'centos7'
|
||||
return distribution
|
||||
@@ -2053,6 +2052,17 @@ def ip2long(ip):
|
||||
iplong = 2 ** 24 * int(ips[0]) + 2 ** 16 * int(ips[1]) + 2 ** 8 * int(ips[2]) + int(ips[3])
|
||||
return iplong
|
||||
|
||||
def is_local_ip(ip):
|
||||
'''
|
||||
@name 判断是否为本地(内网)IP地址
|
||||
@author hwliang<2021-03-26>
|
||||
@param ip string(ipv4)
|
||||
@return bool
|
||||
'''
|
||||
patt = r"^(192\.168|127|10|172\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31))\."
|
||||
if re.match(patt,ip): return True
|
||||
return False
|
||||
|
||||
#获取debug日志
|
||||
def get_debug_log():
|
||||
from BTPanel import request
|
||||
@@ -2362,6 +2372,26 @@ def run_thread(fun,args = (),daemon=False):
|
||||
p.start()
|
||||
return True
|
||||
|
||||
def check_domain_cloud(domain):
|
||||
run_thread(cloud_check_domain,(domain,))
|
||||
|
||||
def cloud_check_domain(domain):
|
||||
'''
|
||||
@name 从云端验证域名的可访问性,并将结果保存到文件
|
||||
@author hwliang<2020-12-10>
|
||||
@param domain {string} 被验证的域名
|
||||
@return void
|
||||
'''
|
||||
try:
|
||||
check_domain_path = '/www/server/panel/data/check_domain/'
|
||||
if not os.path.exists(check_domain_path):
|
||||
os.makedirs(check_domain_path,384)
|
||||
result = httpPost('https://www.aapanel.com/api/panel/checkDomain',{"domain":domain})
|
||||
cd_file = check_domain_path + domain +'.pl'
|
||||
writeFile(cd_file,result)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def send_file(data,fname='',mimetype = ''):
|
||||
'''
|
||||
@@ -2412,6 +2442,19 @@ def get_ipaddress():
|
||||
iplist = ipa_tmp.split('\n')
|
||||
return iplist
|
||||
|
||||
def get_oem_name():
|
||||
'''
|
||||
@name 获取OEM名称
|
||||
@author hwliang<2021-03-24>
|
||||
@return string
|
||||
'''
|
||||
oem = ''
|
||||
oem_file = '/www/server/panel/data/o.pl'
|
||||
if os.path.exists(oem_file):
|
||||
oem = readFile(oem_file)
|
||||
if oem: oem = oem.strip()
|
||||
return oem
|
||||
|
||||
def fetch_disk_SN():
|
||||
r,e = ExecShell("fdisk -l |grep 'Disk identifier' |awk {'print $3'}")
|
||||
if r:
|
||||
@@ -2428,7 +2471,7 @@ def get_hostname():
|
||||
|
||||
def get_platform():
|
||||
import platform
|
||||
return platform.version()
|
||||
return platform.platform()
|
||||
|
||||
def get_memory():
|
||||
import psutil
|
||||
@@ -2451,6 +2494,13 @@ def fetch_env_info():
|
||||
except:
|
||||
return {}
|
||||
|
||||
def arequests(method,url,data=None,timeout=3):
|
||||
import threading
|
||||
if method == 'post':
|
||||
method = httpPost
|
||||
else:
|
||||
method = httpGet
|
||||
threading.Thread(target=method, args=(url,data,timeout)).start()
|
||||
|
||||
#取通用对象
|
||||
class dict_obj:
|
||||
@@ -2461,6 +2511,98 @@ class dict_obj:
|
||||
def __delitem__(self,key): delattr(self,key)
|
||||
def __delattr__(self, key): delattr(self,key)
|
||||
def get_items(self): return self
|
||||
def get(self,key,default='',format='',limit = []):
|
||||
'''
|
||||
@name 获取指定参数
|
||||
@param key<string> 参数名称,允许在/后面限制参数格式,请参考参数值格式(format)
|
||||
@param default<string> 默认值,默认空字符串
|
||||
@param format<string> 参数值格式(int|str|float|json|xss|path|url|ip|ipv4|ipv6|letter|mail|phone|正则表达式|>1|<1|=1),默认为空
|
||||
@param limit<list> 限制参数值内容
|
||||
@param return mixed
|
||||
'''
|
||||
if key.find('/') != -1:
|
||||
key,format = key.split('/')
|
||||
result = getattr(self,key,default).strip()
|
||||
if format:
|
||||
if format in ['str','string','s']:
|
||||
result = str(result)
|
||||
elif format in ['int','d']:
|
||||
try:
|
||||
result = int(result)
|
||||
except:
|
||||
raise ValueError("Parameters: {}, requires int type data".format(key))
|
||||
elif format in ['float','f']:
|
||||
try:
|
||||
result = float(result)
|
||||
except:
|
||||
raise ValueError("Parameters: {}, float type data required".format(key))
|
||||
elif format in ['json','j']:
|
||||
try:
|
||||
result = json.loads(result)
|
||||
except:
|
||||
raise ValueError("Parameters: {}, requires JSON string".format(key))
|
||||
elif format in ['xss','x']:
|
||||
result = xssencode(result)
|
||||
elif format in ['path','p']:
|
||||
if not path_safe_check(result):
|
||||
raise ValueError("Parameters: {}, the correct path format is required".format(key))
|
||||
result = result.replace('//','/')
|
||||
elif format in ['url','u']:
|
||||
regex = re.compile(
|
||||
r'^(?:http|ftp)s?://'
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
|
||||
r'localhost|'
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
|
||||
r'(?::\d+)?'
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
if not re.match(regex,result):
|
||||
raise ValueError('Parameters: {}, the correct URL format is required'.format(key))
|
||||
elif format in ['ip','ipaddr','i','ipv4','ipv6']:
|
||||
if format is 'ipv4':
|
||||
if not is_ipv4(result):
|
||||
raise ValueError('Parameters: {}, the correct ipv4 address is required'.format(key))
|
||||
elif format is 'ipv6':
|
||||
if not is_ipv6(result):
|
||||
raise ValueError('Parameters: {}, the correct ipv6 address is required'.format(key))
|
||||
else:
|
||||
if not is_ipv4(result) and not is_ipv6(result):
|
||||
raise ValueError('Parameters: {}, the correct ipv4/ipv6 address is required'.format(key))
|
||||
elif format in ['w','letter']:
|
||||
if not re.match(r'^\w+$',result):
|
||||
raise ValueError('Parameters: {}, the requirement can only be composed of English letters'.format(key))
|
||||
elif format in ['email','mail','m']:
|
||||
if not re.match(r"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",result):
|
||||
raise ValueError("Parameters: {}, the correct email address format is required".format(key))
|
||||
elif format in ['phone','mobile','m']:
|
||||
if not re.match("^[0-9]{11,11}$",result):
|
||||
raise ValueError("Parameters: {}, mobile phone number format required".format(key))
|
||||
elif re.match(r"^[<>=]\d+$",result):
|
||||
operator = format[0]
|
||||
length = int(format[1:].strip())
|
||||
result_len = len(result)
|
||||
error_obj = ValueError("Parameters: {}, the required length is {}".format(key,format))
|
||||
if operator is '=':
|
||||
if result_len != length:
|
||||
raise error_obj
|
||||
elif operator is '>':
|
||||
if result_len < length:
|
||||
raise error_obj
|
||||
else:
|
||||
if result_len > length:
|
||||
raise error_obj
|
||||
elif format[0] in ['^','(','[','\\','.'] or format[-1] in ['$',')',']','+','}']:
|
||||
if not re.match(format,result):
|
||||
raise ValueError("The format of the specified parameter is incorrect, {}:{}".format(key,format))
|
||||
|
||||
if limit:
|
||||
if not result in limit:
|
||||
raise ValueError("The specified parameter value range is incorrect, {}:{}".format(key,limit))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2574,4 +2716,152 @@ def check_app(check='app'):
|
||||
if not app_info: return False
|
||||
return True
|
||||
|
||||
#宝塔邮件报警
|
||||
def send_mail(title,body,is_logs=False,is_type="aapanel login reminder"):
|
||||
if is_logs:
|
||||
try:
|
||||
import send_mail
|
||||
send_mail22 = send_mail.send_mail()
|
||||
tongdao = send_mail22.get_settings()
|
||||
if tongdao['user_mail']['mail_list']==0:return false
|
||||
if not tongdao['user_mail']['info']: return false
|
||||
if len(tongdao['user_mail']['mail_list'])==1:
|
||||
send_mail=tongdao['user_mail']['mail_list'][0]
|
||||
send_mail22.qq_smtp_send(send_mail, title=title, body=body)
|
||||
else:
|
||||
send_mail22.qq_smtp_send(tongdao['user_mail']['mail_list'], title=title, body=body)
|
||||
if is_logs:
|
||||
WriteLog2(is_type, body)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
import send_mail
|
||||
send_mail22 = send_mail.send_mail()
|
||||
tongdao = send_mail22.get_settings()
|
||||
if tongdao['user_mail']['mail_list'] == 0: return false
|
||||
if not tongdao['user_mail']['info']: return false
|
||||
if len(tongdao['user_mail']['mail_list']) == 1:
|
||||
send_mail = tongdao['user_mail']['mail_list'][0]
|
||||
return send_mail22.qq_smtp_send(send_mail, title=title, body=body)
|
||||
else:
|
||||
return send_mail22.qq_smtp_send(tongdao['user_mail']['mail_list'], title=title, body=body)
|
||||
except:
|
||||
return False
|
||||
|
||||
#宝塔钉钉 or 微信告警
|
||||
def send_dingding(body,is_logs=False,is_type="aapanel login reminder"):
|
||||
if is_logs:
|
||||
try:
|
||||
import send_mail
|
||||
send_mail22 = send_mail.send_mail()
|
||||
tongdao = send_mail22.get_settings()
|
||||
if not tongdao['dingding']['info']: return false
|
||||
tongdao = send_mail22.get_settings()
|
||||
if is_logs:
|
||||
WriteLog2(is_type,body)
|
||||
return send_mail22.dingding_send(body)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
import send_mail
|
||||
send_mail22 = send_mail.send_mail()
|
||||
tongdao = send_mail22.get_settings()
|
||||
if not tongdao['dingding']['info']: return false
|
||||
tongdao = send_mail22.get_settings()
|
||||
return send_mail22.dingding_send(body)
|
||||
except:return False
|
||||
|
||||
#获取服务器IP
|
||||
def get_ip():
|
||||
if os.path.exists('/www/server/panel/data/iplist.txt'):
|
||||
data=ReadFile('/www/server/panel/data/iplist.txt')
|
||||
return data.strip()
|
||||
else:return '127.0.0.1'
|
||||
|
||||
#获取服务器内网Ip
|
||||
def get_local_ip():
|
||||
try:
|
||||
ret=ExecShell("ip addr | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -E -v \"^127\.|^255\.|^0\.\" | head -n 1")
|
||||
local_ip=ret[0].strip()
|
||||
return local_ip
|
||||
except:return '127.0.0.1'
|
||||
|
||||
def create_logs():
|
||||
import db
|
||||
sql = db.Sql()
|
||||
if not sql.table('sqlite_master').where('type=? AND name=?', ('table', 'logs2')).count():
|
||||
csql = '''CREATE TABLE `logs2` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`type` TEXT,
|
||||
`log` TEXT,
|
||||
`addtime` TEXT
|
||||
, uid integer DEFAULT '1', username TEXT DEFAULT 'system')'''
|
||||
sql.execute(csql, ())
|
||||
|
||||
def WriteLog2(type,logMsg,args=(),not_web = False):
|
||||
import db
|
||||
create_logs()
|
||||
username = 'system'
|
||||
uid = 1
|
||||
tmp_msg = ''
|
||||
sql = db.Sql()
|
||||
mDate = time.strftime('%Y-%m-%d %X',time.localtime())
|
||||
data = (uid,username,type,logMsg + tmp_msg,mDate)
|
||||
result = sql.table('logs2').add('uid,username,type,log,addtime',data)
|
||||
|
||||
def check_ip_white(path,ip):
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
path_json=json.loads(ReadFile(path))
|
||||
except:
|
||||
WriteFile(path,'[]')
|
||||
return False
|
||||
if ip in path_json:return True
|
||||
else:return False
|
||||
else:
|
||||
return False
|
||||
|
||||
#登陆告警
|
||||
def login_send_body(is_type,username,login_ip,port):
|
||||
if os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||||
if check_ip_white('/www/server/panel/data/send_login_white.json',login_ip):return False
|
||||
send_mail("aapanel login reminder","aapanel login reminder:Your server "+get_ip()+" successfully logged in via "+is_type+", account number: "+username+", login IP: "+login_ip+":"+port+", login time: "+time.strftime('%Y -%m-%d %X',time.localtime()), True)
|
||||
if os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||||
if check_ip_white('/www/server/panel/data/send_login_white.json',login_ip):return False
|
||||
send_dingding("aapanel login reminder:Your server "+get_ip()+" successfully logged in via "+is_type+", account number: "+username+", login IP: "+login_ip+":"+port+", login time: "+time.strftime('%Y -%m-%d %X',time.localtime()), True)
|
||||
|
||||
#普通模式下调用发送消息【设置登陆告警后的设置】
|
||||
#title= 发送的title
|
||||
#body= 发送的body
|
||||
#is_logs= 是否记录日志
|
||||
#is_type=发送告警的类型
|
||||
def send_to_body(title,body,is_logs=False,is_type="aaPanel email alert"):
|
||||
if os.path.exists("/www/server/panel/data/login_send_mail.pl"):
|
||||
if is_logs:
|
||||
send_mail(title, body,True,is_type)
|
||||
send_mail(title,body)
|
||||
if os.path.exists("/www/server/panel/data/login_send_dingding.pl"):
|
||||
if is_logs:
|
||||
send_dingding(body,True,is_type)
|
||||
send_dingding(body)
|
||||
|
||||
#普通发送消息
|
||||
#send_type= ["mail","dingding"]
|
||||
#title =发送的头
|
||||
#body= 发送消息的内容
|
||||
def send_body_words(send_type,title,body):
|
||||
if send_type=='mail':
|
||||
return send_mail(title,body)
|
||||
if send_type=='dingding':
|
||||
return send_dingding(body)
|
||||
|
||||
def return_is_send_info():
|
||||
import send_mail
|
||||
send_mail22 = send_mail.send_mail()
|
||||
tongdao = send_mail22.get_settings()
|
||||
ret={}
|
||||
ret['mail']=tongdao['user_mail']['user_name']
|
||||
ret['dingding']=tongdao['dingding']['dingding']
|
||||
return ret
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: linxiao
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 数据库备份权限检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os, re, public, panelMysql
|
||||
|
||||
_title = 'Database backup permission detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the MySQL root user has database backup permissions" # 描述
|
||||
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-09-19' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_database_priv.pl")
|
||||
_tips = [
|
||||
"To temporarily access the database without authorization, it is recommended to restore all permissions of the root user.",
|
||||
]
|
||||
|
||||
_help = ''
|
||||
|
||||
|
||||
def check_run():
|
||||
"""检测root用户是否具备数据库备份权限
|
||||
|
||||
@author linxiao<2020-9-18>
|
||||
@return (bool, msg)
|
||||
"""
|
||||
mycnf_file = '/etc/my.cnf'
|
||||
if not os.path.exists(mycnf_file):
|
||||
return True, 'Risk-free'
|
||||
mycnf = public.readFile(mycnf_file)
|
||||
port_tmp = re.findall(r"port\s*=\s*(\d+)", mycnf)
|
||||
if not port_tmp:
|
||||
return True, 'Risk-free'
|
||||
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
|
||||
return True, 'Risk-free'
|
||||
|
||||
base_backup_privs = ["Lock_tables_priv", "Select_priv"]
|
||||
select_sql = "Select {} FROM mysql.user WHERE user='root' and " \
|
||||
"host=SUBSTRING_INDEX((select current_user()),'@', " \
|
||||
"-1);".format(",".join(base_backup_privs))
|
||||
select_result = panelMysql.panelMysql().query(select_sql)
|
||||
if not select_result:
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
select_result = select_result[0]
|
||||
for priv in select_result:
|
||||
if priv.lower() != "y":
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
return True, 'Risk-free'
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'System directory permissions'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Checks if the System directory permissions are correct" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_dir_mode.pl")
|
||||
_tips = [
|
||||
@@ -45,8 +45,8 @@ def check_run():
|
||||
['/usr/local',755,'root'],
|
||||
['/etc',755,'root'],
|
||||
['/etc/passwd',644,'root'],
|
||||
['/etc/shadow',000,'root'],
|
||||
['/etc/gshadow',000,'root'],
|
||||
['/etc/shadow',600,'root'],
|
||||
['/etc/gshadow',600,'root'],
|
||||
['/etc/cron.deny',600,'root'],
|
||||
['/etc/anacrontab',600,'root'],
|
||||
['/var',755,'root'],
|
||||
@@ -56,6 +56,7 @@ def check_run():
|
||||
['/var/spool/cron/crontabs/root',600,'root'],
|
||||
['/www',755,'root'],
|
||||
['/www/server',755,'root'],
|
||||
['/www/wwwroot',755,'root'],
|
||||
['/root',550,'root'],
|
||||
['/mnt',755,'root'],
|
||||
['/home',755,'root'],
|
||||
@@ -67,15 +68,15 @@ def check_run():
|
||||
]
|
||||
|
||||
not_mode_list = []
|
||||
for d in dir_list:
|
||||
if not os.path.exists(d[0]): continue
|
||||
u_mode = public.get_mode_and_user(d[0])
|
||||
if u_mode['user'] != d[2]:
|
||||
not_mode_list.append("{} Current permissions: {} : {} Security permissions: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
if int(u_mode['mode']) != d[1]:
|
||||
not_mode_list.append("{} Current permissions: {} : {} Security permissions: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
# for d in dir_list:
|
||||
# if not os.path.exists(d[0]): continue
|
||||
# u_mode = public.get_mode_and_user(d[0])
|
||||
# if u_mode['user'] != d[2]:
|
||||
# not_mode_list.append("{} 当前权限: {} : {} 安全权限: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
# if int(u_mode['mode']) != d[1]:
|
||||
# not_mode_list.append("{} 当前权限: {} : {} 安全权限: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
|
||||
if not_mode_list:
|
||||
return False,'The following system file or directory permissions are incorrect: <br />' + ("<br />".join(not_mode_list))
|
||||
# if not_mode_list:
|
||||
# return False,'以下关键文件或目录权限错误: <br />' + ("<br />".join(not_mode_list))
|
||||
|
||||
return True,'Risk-free'
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: linxiao
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# FTP弱口令检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os, re#, public
|
||||
|
||||
_title = 'FTP service weak password detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Detect weak passwords for the enabled FTP service" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-09-19' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_ftp_pass.pl")
|
||||
_tips = [
|
||||
"Please go to [FTP] page to change FTP password",
|
||||
"Note: Please do not use too simple account and password, so as not to cause security risks",
|
||||
"Use [Fail2ban] plug-in to protect FTP service"
|
||||
]
|
||||
|
||||
_help = ''
|
||||
_topic = "ftp"
|
||||
|
||||
|
||||
def check_run():
|
||||
"""检测FTP弱口令
|
||||
|
||||
@author linxiao<2020-9-19>
|
||||
@return (bool, msg)
|
||||
"""
|
||||
|
||||
ftp_list = public.M("ftps").field("name,password,status").select()
|
||||
if not ftp_list:
|
||||
return True, 'Risk-free'
|
||||
weak_pass_ftp = []
|
||||
for ftp_info in ftp_list:
|
||||
status = ftp_info["status"]
|
||||
if status == "0" or status == 0:
|
||||
continue
|
||||
login_name = ftp_info["name"]
|
||||
login_pass = ftp_info["password"]
|
||||
if not is_strong_password(login_pass):
|
||||
weak_pass_ftp.append(login_name)
|
||||
|
||||
if weak_pass_ftp:
|
||||
return False, "The following FTP service password settings are too simple and pose security risks: <br />" + \
|
||||
"<br />".join(weak_pass_ftp)
|
||||
return True, 'Risk-free'
|
||||
|
||||
|
||||
def is_strong_password(password):
|
||||
"""判断密码复杂度是否安全
|
||||
|
||||
非弱口令标准:长度大于等于7,分别包含数字、小写、大写、特殊字符。
|
||||
@password: 密码文本
|
||||
@return: True/False
|
||||
@author: linxiao<2020-9-19>
|
||||
"""
|
||||
|
||||
if len(password) < 7:
|
||||
return False
|
||||
|
||||
import re
|
||||
digit_reg = "[0-9]" # 匹配数字 +1
|
||||
lower_case_letters_reg = "[a-z]" # 匹配小写字母 +1
|
||||
upper_case_letters_reg = "[A-Z]" # 匹配大写字母 +1
|
||||
special_characters_reg = r"((?=[\x21-\x7e]+)[^A-Za-z0-9])" # 匹配特殊字符 +1
|
||||
|
||||
regs = [digit_reg,
|
||||
lower_case_letters_reg,
|
||||
upper_case_letters_reg,
|
||||
special_characters_reg]
|
||||
|
||||
grade = 0
|
||||
for reg in regs:
|
||||
if re.search(reg, password):
|
||||
grade += 1
|
||||
|
||||
if grade == 4 or (grade >= 2 and len(password) >= 9):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# passwords = ["000000", "aaaaaaa", "Ab2aaaaaa"]
|
||||
# for p in passwords:
|
||||
# if is_strong_password(p):
|
||||
# print("密码:{} 安全性高。".format(p))
|
||||
# else:
|
||||
# print("密码:{} 安全性弱, 建议更换密码。".format(p))
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'SSH user login notification'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether SSH user login notification is enabled" # 描述
|
||||
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_login_message.pl")
|
||||
_tips = [
|
||||
@@ -28,6 +28,12 @@ _tips = [
|
||||
_help = ''
|
||||
|
||||
|
||||
def return_bashrc():
|
||||
if os.path.exists('/root/.bashrc'):return '/root/.bashrc'
|
||||
if os.path.exists('/etc/bashrc'):return '/etc/bashrc'
|
||||
if os.path.exists('/etc/bash.bashrc'):return '/etc/bash.bashrc'
|
||||
return '/root/.bashrc'
|
||||
|
||||
def check_run():
|
||||
'''
|
||||
@name 开始检测
|
||||
@@ -35,9 +41,9 @@ def check_run():
|
||||
@return tuple (status<bool>,msg<string>)
|
||||
'''
|
||||
|
||||
data = public.ReadFile('/etc/bashrc')
|
||||
data = public.ReadFile(return_bashrc())
|
||||
if not data: return True,'Risk-free'
|
||||
if re.search('python /www/server/panel/class/ssh_security.py login', data):
|
||||
if re.search('ssh_security.py login', data):
|
||||
return True,'Risk-free'
|
||||
else:
|
||||
return False,'SSH user login notification is not configured, so it is impossible to know whether the server has been illegally logged in in the first place'
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'Risk User'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Detect if there is a risk user in the system user list" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_login_user.pl")
|
||||
_tips = [
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# MySQL端口安全检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os,sys,re,public
|
||||
import os,sys,re,public,json
|
||||
|
||||
_title = 'MySQL security'
|
||||
_version = 1.0 # 版本
|
||||
@@ -50,9 +50,17 @@ def check_run():
|
||||
return True,'MySQL is not installed'
|
||||
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
|
||||
return True,'MySQL is not installed'
|
||||
result = public.check_port_stat(int(port_tmp[0]),public.GetClientIp())
|
||||
result = public.check_port_stat(int(port_tmp[0]),public.GetLocalIp())
|
||||
if result == 0:
|
||||
return True,'Risk-free'
|
||||
|
||||
return False,'The current MySQL port: {}, which can be accessed by any server, which may cause MySQL to be cracked by brute force, posing security risks'.format(port_tmp[0])
|
||||
|
||||
fail2ban_file = '/www/server/panel/plugin/fail2ban/config.json'
|
||||
if os.path.exists(fail2ban_file):
|
||||
try:
|
||||
fail2ban_config = json.loads(public.readFile(fail2ban_file))
|
||||
if 'mysql' in fail2ban_config.keys():
|
||||
if fail2ban_config['mysql']['act'] == 'true':
|
||||
return True,'Fail2ban is enabled'
|
||||
except: pass
|
||||
|
||||
return False,'当前MySQL端口: {},可被任意服务器访问,这可能导致MySQL被暴力破解,存在安全隐患'.format(port_tmp[0])
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: linxiao
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 数据库备份权限检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os, re, public, panelMysql
|
||||
|
||||
_title = 'Database backup permission detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the MySQL root user has database backup permissions" # 描述
|
||||
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-09-19' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_database_priv.pl")
|
||||
_tips = [
|
||||
"To temporarily access the database without authorization, it is recommended to restore all permissions of the root user.",
|
||||
]
|
||||
|
||||
_help = ''
|
||||
|
||||
|
||||
def check_run():
|
||||
"""检测root用户是否具备数据库备份权限
|
||||
|
||||
@author linxiao<2020-9-18>
|
||||
@return (bool, msg)
|
||||
"""
|
||||
mycnf_file = '/etc/my.cnf'
|
||||
if not os.path.exists(mycnf_file):
|
||||
return True, 'Risk-free'
|
||||
mycnf = public.readFile(mycnf_file)
|
||||
port_tmp = re.findall(r"port\s*=\s*(\d+)", mycnf)
|
||||
if not port_tmp:
|
||||
return True, 'Risk-free'
|
||||
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
|
||||
return True, 'Risk-free'
|
||||
|
||||
base_backup_privs = ["Lock_tables_priv", "Select_priv"]
|
||||
select_sql = "Select {} FROM mysql.user WHERE user='root' and " \
|
||||
"host=SUBSTRING_INDEX((select current_user()),'@', " \
|
||||
"-1);".format(",".join(base_backup_privs))
|
||||
select_result = panelMysql.panelMysql().query(select_sql)
|
||||
if not select_result:
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
select_result = select_result[0]
|
||||
for priv in select_result:
|
||||
if priv.lower() != "y":
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
return True, 'Risk-free'
|
||||
@@ -17,7 +17,7 @@ import os,sys,re,public
|
||||
_title = 'Panel password'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the panel account password is safe" # 描述
|
||||
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-04' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_panel_pass.pl")
|
||||
_tips = [
|
||||
@@ -44,6 +44,7 @@ def check_run():
|
||||
return False,'The default password of the panel has not been modified, and there is a security risk'
|
||||
|
||||
lower_pass_txt = '''12123
|
||||
china
|
||||
test
|
||||
test12
|
||||
test11
|
||||
@@ -1097,6 +1098,8 @@ winner
|
||||
p1 = password_salt(public.md5(lp),uid=1)
|
||||
if p1 == find['password']:
|
||||
return False,'The current panel password is too simple and there is a security risk'
|
||||
if not is_strong_password(find["password"]):
|
||||
return False, 'The current panel password is too simple and there is a security risk'
|
||||
return True,'Risk-free'
|
||||
|
||||
salt = None
|
||||
@@ -1113,5 +1116,41 @@ def password_salt(password,username=None,uid=None):
|
||||
global salt
|
||||
if not salt:
|
||||
salt = public.M('users').where('id=?',(uid,)).getField('salt')
|
||||
if salt:
|
||||
salt = salt[0]
|
||||
else:
|
||||
salt = ""
|
||||
return public.md5(public.md5(password+'_bt.cn')+salt)
|
||||
|
||||
|
||||
|
||||
def is_strong_password(password):
|
||||
"""判断密码复杂度是否安全
|
||||
|
||||
非弱口令标准:长度大于等于7,分别包含数字、小写、大写、特殊字符。
|
||||
@password: 密码文本
|
||||
@return: True/False
|
||||
@author: linxiao<2020-9-19>
|
||||
"""
|
||||
|
||||
if len(password) < 7:
|
||||
return False
|
||||
|
||||
import re
|
||||
digit_reg = "[0-9]" # 匹配数字 +1
|
||||
lower_case_letters_reg = "[a-z]" # 匹配小写字母 +1
|
||||
upper_case_letters_reg = "[A-Z]" # 匹配大写字母 +1
|
||||
special_characters_reg = r"((?=[\x21-\x7e]+)[^A-Za-z0-9])" # 匹配特殊字符 +1
|
||||
|
||||
regs = [digit_reg,
|
||||
lower_case_letters_reg,
|
||||
upper_case_letters_reg,
|
||||
special_characters_reg]
|
||||
|
||||
grade = 0
|
||||
for reg in regs:
|
||||
if re.search(reg, password):
|
||||
grade += 1
|
||||
|
||||
if grade == 4 or (grade == 3 and len(password) >= 9):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'ICMP detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether ICMP access is allowed (Block ICMP)" # 描述
|
||||
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_ping.pl")
|
||||
_tips = [
|
||||
|
||||
+33
-1046
File diff suppressed because it is too large
Load Diff
@@ -13,12 +13,12 @@
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
|
||||
import os,sys,re,public
|
||||
import os,sys,re,public,json
|
||||
|
||||
_title = 'SSH security'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the SSH port of the current server is safe" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-04' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_ssh_port.pl")
|
||||
_tips = [
|
||||
@@ -72,7 +72,16 @@ def check_run():
|
||||
status = public.ExecShell("systemctl status sshd.service | grep 'dead'|grep -v grep")
|
||||
else:
|
||||
status = public.ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'|grep -v grep")
|
||||
|
||||
|
||||
fail2ban_file = '/www/server/panel/plugin/fail2ban/config.json'
|
||||
if os.path.exists(fail2ban_file):
|
||||
try:
|
||||
fail2ban_config = json.loads(public.readFile(fail2ban_file))
|
||||
if 'sshd' in fail2ban_config.keys():
|
||||
if fail2ban_config['sshd']['act'] == 'true':
|
||||
return True,'Fail2ban is enable'
|
||||
except: pass
|
||||
|
||||
if len(status[0]) > 3:
|
||||
status = False
|
||||
else:
|
||||
@@ -83,7 +92,7 @@ def check_run():
|
||||
if port != '22':
|
||||
return True,'The default SSH port has been modified'
|
||||
|
||||
result = public.check_port_stat(int(port),public.GetClientIp())
|
||||
result = public.check_port_stat(int(port),public.GetLocalIp())
|
||||
if result == 0:
|
||||
return True,'Rick-free'
|
||||
|
||||
|
||||
+17
-5
@@ -123,17 +123,22 @@ class send_mail:
|
||||
ret = True
|
||||
if not 'port' in self.__qq_mail_user: self.__qq_mail_user['port'] = 465
|
||||
try:
|
||||
|
||||
msg = MIMEText(body, 'html', 'utf-8')
|
||||
msg['From'] = formataddr([self.__qq_mail_user['qq_mail'], self.__qq_mail_user['qq_mail']])
|
||||
msg['To'] = formataddr([self.__qq_mail_user['qq_mail'], email.strip()])
|
||||
if type(email)==str:
|
||||
msg['To'] = formataddr([self.__qq_mail_user['qq_mail'], email.strip()])
|
||||
elif type(email)==list:
|
||||
msg['To']=formataddr(email)
|
||||
msg['Subject'] = title
|
||||
if int(self.__qq_mail_user['port']) == 465:
|
||||
server = smtplib.SMTP_SSL(str(self.__qq_mail_user['hosts']), str(self.__qq_mail_user['port']))
|
||||
else:
|
||||
server = smtplib.SMTP(str(self.__qq_mail_user['hosts']), str(self.__qq_mail_user['port']))
|
||||
server.login(self.__qq_mail_user['qq_mail'], self.__qq_mail_user['qq_stmp_pwd'])
|
||||
server.sendmail(self.__qq_mail_user['qq_mail'], [email.strip(), ], msg.as_string())
|
||||
if type(email)==str:
|
||||
server.sendmail(self.__qq_mail_user['qq_mail'], [email.strip()], msg.as_string())
|
||||
elif type(email)==list:
|
||||
server.sendmail(self.__qq_mail_user['qq_mail'], email, msg.as_string())
|
||||
server.quit()
|
||||
except Exception:
|
||||
ret = False
|
||||
@@ -166,11 +171,18 @@ class send_mail:
|
||||
filename = '/www/server/panel/data/iplist.txt'
|
||||
ipaddress = public.readFile(filename)
|
||||
if not ipaddress:
|
||||
import urllib2
|
||||
try:
|
||||
import urllib2
|
||||
except:
|
||||
import urllib as urllib2
|
||||
urllib2 = urllib2.request
|
||||
url = 'http://pv.sohu.com/cityjson?ie=utf-8'
|
||||
opener = urllib2.urlopen(url)
|
||||
m_str = opener.read()
|
||||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str).group(0)
|
||||
if isinstance(m_str, bytes):
|
||||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str.decode('utf-8')).group(0)
|
||||
else:
|
||||
ipaddress = re.search('\d+.\d+.\d+.\d+', m_str).group(0)
|
||||
public.WriteFile(filename, ipaddress)
|
||||
c_ip = public.check_ip(ipaddress)
|
||||
if not c_ip:
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
# coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板 x3
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2017 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: lkq <1249648969@qq.com>
|
||||
# +-------------------------------------------------------------------
|
||||
# +--------------------------------------------------------------------
|
||||
# | 告警消息队列
|
||||
# +--------------------------------------------------------------------
|
||||
import public,send_mail
|
||||
import time,os,sys,json
|
||||
class send_to_user:
|
||||
'''
|
||||
建立数据库
|
||||
'''
|
||||
def __init__(self):
|
||||
self.mail = send_mail.send_mail()
|
||||
if not public.M('sqlite_master').where('type=? AND name=?', ('table', 'send_settings')).count():
|
||||
public.M('').execute('''CREATE TABLE "send_settings" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,"name" TEXT,"type" TEXT,"path" TEXT,"send_type" TEXT,"last_time" TEXT ,"time_frame" TEXT,"inser_time" TEXT DEFAULT'');''')
|
||||
if not public.M('sqlite_master').where('type=? AND name=?', ('table', 'send_msg')).count():
|
||||
public.M('').execute('''CREATE TABLE "send_msg" ("id" INTEGER PRIMARY KEY AUTOINCREMENT,"name" TEXT,"send_type" TEXT,"msg" TEXT,"is_send" TEXT,"type" TEXT,"inser_time" TEXT DEFAULT '');''')
|
||||
|
||||
'''设置表插入数据'''
|
||||
def insert_settings(self,name,type,path,send_type,time_frame=180):
|
||||
inser_time = self.dtchg(int(time.time()))
|
||||
last_time=int(time.time())
|
||||
if public.M('send_settings').where('name=?',(name,)).count(): return False
|
||||
data={"name":name,"type":type,"path":path,"send_type":send_type,"time_frame":time_frame,"inser_time":inser_time,"last_time":last_time}
|
||||
return public.M('send_settings').insert(data)
|
||||
|
||||
'''数据库插入'''
|
||||
def inser_send_msg(self,name,send_type,msg,type,inser_time):
|
||||
inser_time=self.dtchg(inser_time)
|
||||
if not inser_time:return False
|
||||
if public.M('send_msg').where('name=? and send_type=? and type=? and inser_time=?',(name,send_type,type,inser_time)).count():return False
|
||||
data={"name":name,"send_type":send_type,"msg":msg,"is_send":False,"type":type,"inser_time":inser_time}
|
||||
return public.M('send_msg').insert(data)
|
||||
|
||||
def dtchg(self,x):
|
||||
try:
|
||||
time_local = time.localtime(float(x))
|
||||
dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
|
||||
return dt
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_ip(self):
|
||||
if os.path.exists('/www/server/panel/data/iplist.txt'):
|
||||
data=public.ReadFile('/www/server/panel/data/iplist.txt')
|
||||
return data.strip()
|
||||
else:return '127.0.0.1'
|
||||
|
||||
def get_safe_logs(self, path,p=1,num=11):
|
||||
try:
|
||||
import cgi
|
||||
pythonV = sys.version_info[0]
|
||||
if not os.path.exists(path): return '111';
|
||||
start_line = (p - 1) * num
|
||||
count = start_line + num
|
||||
fp = open(path, 'rb')
|
||||
buf = ""
|
||||
try:
|
||||
fp.seek(-1, 2)
|
||||
except:
|
||||
return []
|
||||
if fp.read(1) == "\n": fp.seek(-1, 2)
|
||||
data = []
|
||||
b = True
|
||||
n = 0
|
||||
for i in range(count):
|
||||
while True:
|
||||
newline_pos = str.rfind(buf, "\n")
|
||||
pos = fp.tell()
|
||||
if newline_pos != -1:
|
||||
if n >= start_line:
|
||||
line = buf[newline_pos + 1:]
|
||||
try:
|
||||
tmp_data = json.loads(cgi.escape(line))
|
||||
data.append(tmp_data)
|
||||
except:
|
||||
pass
|
||||
buf = buf[:newline_pos]
|
||||
n += 1
|
||||
break
|
||||
else:
|
||||
if pos == 0:
|
||||
b = False
|
||||
break
|
||||
to_read = min(4096, pos)
|
||||
fp.seek(-to_read, 1)
|
||||
t_buf = fp.read(to_read)
|
||||
if pythonV == 3: t_buf = t_buf.decode('utf-8')
|
||||
buf = t_buf + buf
|
||||
fp.seek(-to_read, 1)
|
||||
if pos - to_read == 0:
|
||||
buf = "\n" + buf
|
||||
if not b: break;
|
||||
fp.close()
|
||||
except:
|
||||
data = []
|
||||
return data
|
||||
|
||||
'''
|
||||
读取数据库中的值、写入到数据库中
|
||||
'''
|
||||
def read_thread(self):
|
||||
if not public.M('send_settings').count():return False
|
||||
send_data=public.M('send_settings').field('id,name,type,path,send_type,inser_time,last_time,time_frame').select()
|
||||
print(send_data)
|
||||
for i in send_data:
|
||||
if (int(time.time())-int(i['last_time']))<int(i['time_frame']):continue
|
||||
if i['type']=='json':
|
||||
if os.path.exists(i['path']):
|
||||
read_file=self.get_safe_logs(i['path'],p=1,num=100)
|
||||
if not read_file:continue
|
||||
if not read_file[0]:continue
|
||||
for i2 in read_file:
|
||||
self.inser_send_msg(i['name'],i['send_type'],self.get_ip()+'服务器存在问题-->'+i2[1]+',触发告警时间:'+self.dtchg(int(time.time())),'json',i2[0])
|
||||
public.writeFile(i['path'], '')
|
||||
public.M('send_settings').where("id=?", (i['id'],)).update({"last_time": int(time.time())})
|
||||
continue
|
||||
if i['type']=='file':
|
||||
if os.path.exists(i['path']):
|
||||
self.inser_send_msg(i['name'], i['send_type'], '堡塔'+i['name']+'提醒您服务器'+self.get_ip()+'存在异常,详情请登陆面板查看'+i['name']+',触发告警时间:'+self.dtchg(int(time.time())), 'file', int(time.time()))
|
||||
public.M('send_settings').where("id=?", (i['id'],)).update({"last_time": int(time.time())})
|
||||
os.system('rm -rf %s'%i['path'])
|
||||
if os.path.exists(i['path']):os.system('rm -rf %s'%i['path'])
|
||||
else:
|
||||
continue
|
||||
def send(self,title,body):
|
||||
tongdao = self.mail.get_settings()
|
||||
return self.mail.qq_smtp_send(tongdao['user_mail']['mail_list'], title=title, body=body)
|
||||
def send_dingding(self,count):
|
||||
return self.mail.dingding_send(count)
|
||||
|
||||
def __write_log(self,name, msg):
|
||||
public.WriteLog(name+'告警', msg)
|
||||
|
||||
'''发送消息线程'''
|
||||
def send_msg(self):
|
||||
if not public.M('send_msg').count(): return False
|
||||
send_msg=public.M('send_msg').where("is_send=?",(False,)).field('id,name,send_type,msg,is_send,type,inser_time').select()
|
||||
count=1
|
||||
for i in send_msg:
|
||||
if count>=4:break
|
||||
settings=self.mail.get_settings()
|
||||
if i['send_type']=='mail':
|
||||
if not settings['user_mail']['user_name']:continue
|
||||
if i['name']=='Nginx防火墙' or i['name'] == 'Apache防火墙':
|
||||
if self.send(i['name'] + '提醒您' + self.get_ip() + '服务器正在遭受攻击', i['msg']):
|
||||
self.__write_log(i['name'], i['msg'])
|
||||
public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True})
|
||||
else:
|
||||
if self.send(i['name']+'提醒您'+self.get_ip()+'服务器存在风险', i['msg']):
|
||||
self.__write_log(i['name'],i['msg'])
|
||||
public.M('send_msg').where("id=?", (i['id'],)).update({"is_send":True})
|
||||
if i['send_type']=='dingding':
|
||||
if not settings['dingding']['dingding']: continue
|
||||
if i['name'] == 'Nginx防火墙' or i['name'] == 'Apache防火墙':
|
||||
if self.send(i['name'] + '提醒您' + self.get_ip() + '服务器正在遭受攻击', i['msg']):
|
||||
self.__write_log(i['name'], i['msg'])
|
||||
public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True})
|
||||
else:
|
||||
if self.send_dingding(i['msg']):
|
||||
self.__write_log(i['name'], i['msg'])
|
||||
public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True})
|
||||
count += 1
|
||||
public.M('send_msg').where("is_send=?", (True,)).delete()
|
||||
|
||||
def main(self):
|
||||
try:
|
||||
self.read_thread()
|
||||
self.send_msg()
|
||||
except:
|
||||
pass
|
||||
|
||||
+16
-8
@@ -84,18 +84,26 @@ class setPanelLets:
|
||||
pssl = panelSSL.panelSSL()
|
||||
gcl = pssl.GetCertList(get)
|
||||
for i in gcl:
|
||||
if get.domain in i.values():
|
||||
time_array = time.strptime(i['notAfter'],"%Y-%m-%d")
|
||||
time_stamp = int(time.mktime(time_array))
|
||||
now = time.time()
|
||||
if time_stamp > int(now):
|
||||
return i
|
||||
for v in i.values():
|
||||
if get.domain in v:
|
||||
try:
|
||||
time_stamp = int(i['notAfter'])
|
||||
except:
|
||||
time_array = time.strptime(i['notAfter'],"%Y-%m-%d")
|
||||
time_stamp = int(time.mktime(time_array))
|
||||
now = time.time()
|
||||
if time_stamp > int(now):
|
||||
return i
|
||||
|
||||
# 读取可用站点证书
|
||||
def __read_site_cert(self,domain_cert):
|
||||
key_file = "{path}{domain}/{key}".format(path=self.__vhost_cert_path,domain=domain_cert["subject"],key="privkey.pem")
|
||||
cert_file = "{path}{domain}/{cert}".format(path=self.__vhost_cert_path, domain=domain_cert["subject"],
|
||||
try:
|
||||
key_file = "{path}{domain}/{key}".format(path=self.__vhost_cert_path,domain=domain_cert["subject"],key="privkey.pem")
|
||||
cert_file = "{path}{domain}/{cert}".format(path=self.__vhost_cert_path, domain=domain_cert["subject"],
|
||||
cert="fullchain.pem")
|
||||
except:
|
||||
key_file = "/www/server/panel/{}/privkey.pem".format(domain_cert['save_path'])
|
||||
cert_file = "/www/server/panel/{}/fullchain.pem".format(domain_cert['save_path'])
|
||||
if not os.path.exists(key_file):
|
||||
key_file = "{path}{domain}/{key}".format(path="/www/server/panel/vhost/ssl/",domain=domain_cert["subject"],key="privkey.pem")
|
||||
cert_file = "{path}{domain}/{cert}".format(path="/www/server/panel/vhost/ssl/", domain=domain_cert["subject"],
|
||||
|
||||
+16
-2
@@ -139,6 +139,16 @@ class SiteDirAuth:
|
||||
site_info = public.M('sites').where('id=?', (id,)).field('name,path').find()
|
||||
return {"site_name":site_info["name"],"site_path":site_info["path"]}
|
||||
|
||||
def change_dir_auth_file_nginx_phpver(self,site_name,phpv,auth_name):
|
||||
file_path = "{setup_path}/panel/vhost/nginx/dir_auth/{site_name}/{auth_name}.conf".format(
|
||||
setup_path=self.setup_path,site_name=site_name,auth_name=auth_name)
|
||||
conf = public.readFile(file_path)
|
||||
if not conf:
|
||||
return False
|
||||
rep = "include\s+enable-php-\d+\.conf;"
|
||||
conf = re.sub(rep,'include enable-php-{}.conf;'.format(phpv),conf)
|
||||
public.writeFile(file_path,conf)
|
||||
|
||||
# 设置独立认证文件
|
||||
def set_dir_auth_file(self,site_path,site_name,name,username,site_dir,auth_file):
|
||||
php_ver = self.get_site_php_version(site_name)
|
||||
@@ -277,11 +287,15 @@ class SiteDirAuth:
|
||||
def get_dir_auth(self,get):
|
||||
'''
|
||||
get.id
|
||||
get.sitename
|
||||
:param get:
|
||||
:return:
|
||||
'''
|
||||
site_info = self.get_site_info(get.id)
|
||||
site_name = site_info["site_name"]
|
||||
if not hasattr(get, 'siteName'):
|
||||
site_info = self.get_site_info(get.id)
|
||||
site_name = site_info["site_name"]
|
||||
else:
|
||||
site_name = get.siteName
|
||||
conf = self._read_conf()
|
||||
if site_name in conf:
|
||||
return {site_name:conf[site_name]}
|
||||
|
||||
+68
-9
@@ -99,6 +99,19 @@ class ssh_security:
|
||||
if os.path.exists('/www/server/panel/pyenv'):
|
||||
self.__pyenv = 'btpython'
|
||||
|
||||
def return_python(self):
|
||||
if os.path.exists('/www/server/panel/pyenv/bin/python'):return '/www/server/panel/pyenv/bin/python'
|
||||
if os.path.exists('/usr/bin/python'):return '/usr/bin/python'
|
||||
if os.path.exists('/usr/bin/python3'):return '/usr/bin/python3'
|
||||
return 'python'
|
||||
|
||||
def return_bashrc(self):
|
||||
if os.path.exists('/root/.bashrc'):return '/root/.bashrc'
|
||||
if os.path.exists('/etc/bashrc'):return '/etc/bashrc'
|
||||
if os.path.exists('/etc/bash.bashrc'):return '/etc/bash.bashrc'
|
||||
fd = open('/root/.bashrc', mode="w", encoding="utf-8")
|
||||
fd.close()
|
||||
return '/root/.bashrc'
|
||||
|
||||
def check_files(self):
|
||||
try:
|
||||
@@ -230,7 +243,7 @@ class ssh_security:
|
||||
|
||||
#获取ROOT当前登陆的IP
|
||||
def get_ip(self):
|
||||
data = public.ExecShell(''' echo $SSH_CLIENT |awk ' { print $1 }' ''')
|
||||
data = public.ExecShell(''' who am i |awk ' {print $5 }' ''')
|
||||
data = re.findall("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",data[0])
|
||||
return data
|
||||
|
||||
@@ -278,24 +291,27 @@ class ssh_security:
|
||||
|
||||
#开启监控
|
||||
def start_jian(self,get):
|
||||
data=public.ReadFile('/etc/bashrc')
|
||||
data=public.ReadFile(self.return_bashrc())
|
||||
if not re.search('{}\/www\/server\/panel\/class\/ssh_security.py'.format(".*python\s+"),data):
|
||||
public.WriteFile('/etc/bashrc',data.strip()+'\n{} /www/server/panel/class/ssh_security.py login\n'.format(self.__pyenv))
|
||||
public.WriteFile(self.return_bashrc(),data.strip()+'\n'+self.return_python()+ ' /www/server/panel/class/ssh_security.py login\n')
|
||||
return public.returnMsg(True, 'Open successfully')
|
||||
return public.returnMsg(False, 'Open failed')
|
||||
|
||||
#关闭监控
|
||||
def stop_jian(self,get):
|
||||
data = public.ReadFile('/etc/bashrc')
|
||||
if re.search('{}\/www\/server\/panel\/class\/ssh_security.py'.format(".*python\s+"), data):
|
||||
public.WriteFile('/etc/bashrc',data.replace('python /www/server/panel/class/ssh_security.py login',''))
|
||||
data = public.ReadFile(self.return_bashrc())
|
||||
if re.search(self.return_python()+' /www/server/panel/class/ssh_security.py', data):
|
||||
public.WriteFile(self.return_bashrc(),data.replace(self.return_python()+' /www/server/panel/class/ssh_security.py login',''))
|
||||
if os.path.exists('/etc/bashrc'):
|
||||
if re.search('python /www/server/panel/class/ssh_security.py', data):
|
||||
public.WriteFile(self.return_bashrc(),data.replace(self.return_python()+' /www/server/panel/class/ssh_security.py login',''))
|
||||
return public.returnMsg(True, 'Closed successfully')
|
||||
else:
|
||||
return public.returnMsg(True, 'Closed successfully')
|
||||
|
||||
#监控状态
|
||||
def get_jian(self,get):
|
||||
data = public.ReadFile('/etc/bashrc')
|
||||
data = public.ReadFile(self.return_bashrc())
|
||||
if re.search('{}\/www\/server\/panel\/class\/ssh_security.py\s+login'.format(".*python\s+"), data):
|
||||
return public.returnMsg(True, '1')
|
||||
else:
|
||||
@@ -362,8 +378,8 @@ class ssh_security:
|
||||
rec = '\n#?RSAAuthentication\s\w+'
|
||||
rec2 = '\n#?PubkeyAuthentication\s\w+'
|
||||
file = public.readFile(self.__SSH_CONFIG)
|
||||
file_ssh = re.sub(rec, '\n#RSAAuthentication no', file)
|
||||
file_result = re.sub(rec2, '\n#PubkeyAuthentication no', file_ssh)
|
||||
file_ssh = re.sub(rec, '\nRSAAuthentication no', file)
|
||||
file_result = re.sub(rec2, '\nPubkeyAuthentication no', file_ssh)
|
||||
self.wirte(self.__SSH_CONFIG, file_result)
|
||||
self.set_password(get)
|
||||
self.restart_ssh()
|
||||
@@ -379,6 +395,9 @@ class ssh_security:
|
||||
rec = '\n#?RSAAuthentication\s\w+'
|
||||
pubkey = '\n#?PubkeyAuthentication\s\w+'
|
||||
ssh_password = '\nPasswordAuthentication\s\w+'
|
||||
#是否运行root登录
|
||||
root_is_login='\n#?PermitRootLogin\s\w+'
|
||||
|
||||
ret = re.findall(ssh_password, file)
|
||||
if not ret:
|
||||
result['password'] = 'no'
|
||||
@@ -403,8 +422,48 @@ class ssh_security:
|
||||
result['rsa_auth'] = 'no'
|
||||
else:
|
||||
result['rsa_auth'] = 'yes'
|
||||
|
||||
is_root=re.findall(root_is_login, file)
|
||||
if not is_root:
|
||||
result['root_is_login'] = 'no'
|
||||
else:
|
||||
if is_root[-1].split()[-1] == 'no':
|
||||
result['root_is_login'] = 'no'
|
||||
else:
|
||||
result['root_is_login'] = 'yes'
|
||||
return result
|
||||
|
||||
|
||||
def set_root(self, get):
|
||||
'''
|
||||
开启密码登陆
|
||||
get: 无需传递参数
|
||||
'''
|
||||
ssh_password = '\n#?PermitRootLogin\s\w+'
|
||||
file = public.readFile(self.__SSH_CONFIG)
|
||||
if len(re.findall(ssh_password, file)) == 0:
|
||||
file_result = file + '\nPermitRootLogin yes'
|
||||
else:
|
||||
file_result = re.sub(ssh_password, '\nPermitRootLogin yes', file)
|
||||
self.wirte(self.__SSH_CONFIG, file_result)
|
||||
self.restart_ssh()
|
||||
return public.returnMsg(True, 'Successfully opened')
|
||||
|
||||
def stop_root(self, get):
|
||||
'''
|
||||
开启密码登陆
|
||||
get: 无需传递参数
|
||||
'''
|
||||
ssh_password = '\n#?PermitRootLogin\s\w+'
|
||||
file = public.readFile(self.__SSH_CONFIG)
|
||||
if len(re.findall(ssh_password, file)) == 0:
|
||||
file_result = file + '\nPermitRootLogin no'
|
||||
else:
|
||||
file_result = re.sub(ssh_password, '\nPermitRootLogin no', file)
|
||||
self.wirte(self.__SSH_CONFIG, file_result)
|
||||
self.restart_ssh()
|
||||
return public.returnMsg(True, 'Closed successfully')
|
||||
|
||||
def stop_password(self, get):
|
||||
'''
|
||||
关闭密码访问
|
||||
|
||||
@@ -94,7 +94,6 @@ class ssh_terminal:
|
||||
|
||||
try:
|
||||
self._tp.start_client()
|
||||
self.debug(self._pkey)
|
||||
if not self._pass and not self._pkey:
|
||||
self.set_sshd_config(True)
|
||||
return public.returnMsg(False,'SSH_LOGIN_INFO_ERR',(self._host,str(self._port)))
|
||||
@@ -449,7 +448,7 @@ class ssh_terminal:
|
||||
resp_line = self._ssh.recv(1024)
|
||||
if not resp_line:
|
||||
if not self._tp.is_active():
|
||||
self.debug('SSH_LOGIN_ERR14')
|
||||
self.debug(public.getMsg('SSH_LOGIN_ERR14'))
|
||||
self._ws.send(public.getMsg('RECONNECT_SSH'))
|
||||
self.close()
|
||||
return
|
||||
@@ -480,7 +479,7 @@ class ssh_terminal:
|
||||
self.debug(public.getMsg('SSH_LOGIN_ERR15',(str(e),)))
|
||||
|
||||
if self._ws.closed:
|
||||
self.debug('SSH_LOGIN_INFO1')
|
||||
self.debug(public.getMsg('SSH_LOGIN_INFO1'))
|
||||
self.close()
|
||||
|
||||
def send(self):
|
||||
@@ -513,7 +512,7 @@ class ssh_terminal:
|
||||
self.debug(public.getMsg('SSH_LOGIN_ERR17',(str(ex),)))
|
||||
|
||||
if self._ws.closed:
|
||||
self.debug('SSH_LOGIN_INFO1')
|
||||
self.debug(public.getMsg('SSH_LOGIN_INFO1'))
|
||||
self.close()
|
||||
|
||||
|
||||
|
||||
+114
-24
@@ -25,7 +25,6 @@ class system:
|
||||
session['config'] = public.M('config').where("id=?",('1',)).field('webserver,sites_path,backup_path,status,mysql_root').find()
|
||||
if not 'email' in session['config']:
|
||||
session['config']['email'] = public.M('users').where("id=?",('1',)).getField('email')
|
||||
data = {}
|
||||
data = session['config']
|
||||
data['webserver'] = public.get_webserver()
|
||||
#PHP版本
|
||||
@@ -100,7 +99,7 @@ class system:
|
||||
elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
|
||||
data['webserver'] = 'openlitespeed'
|
||||
serviceName = 'openlitespeed'
|
||||
tmp['setup'] = os.path.exists(self.setupPath +'/apache/bin/httpd')
|
||||
tmp['setup'] = os.path.exists('/usr/local/lsws/bin/lswsctrl')
|
||||
configFile = '/usr/local/lsws/bin/lswsctrl'
|
||||
try:
|
||||
if os.path.exists(configFile):
|
||||
@@ -234,18 +233,18 @@ class system:
|
||||
|
||||
def GetSystemTotal(self,get,interval = 1):
|
||||
#取系统统计信息
|
||||
data = self.GetMemInfo();
|
||||
cpu = self.GetCpuInfo(interval);
|
||||
data['cpuNum'] = cpu[1];
|
||||
data['cpuRealUsed'] = cpu[0];
|
||||
data['time'] = self.GetBootTime();
|
||||
data['system'] = self.GetSystemVersion();
|
||||
data['isuser'] = public.M('users').where('username=?',('admin',)).count();
|
||||
data = self.GetMemInfo()
|
||||
cpu = self.GetCpuInfo(interval)
|
||||
data['cpuNum'] = cpu[1]
|
||||
data['cpuRealUsed'] = cpu[0]
|
||||
data['time'] = self.GetBootTime()
|
||||
data['system'] = self.GetSystemVersion()
|
||||
data['isuser'] = public.M('users').where('username=?',('admin',)).count()
|
||||
try:
|
||||
data['isport'] = public.GetHost(True) == '8888'
|
||||
except:data['isport'] = False
|
||||
|
||||
data['version'] = session['version'];
|
||||
data['version'] = session['version']
|
||||
return data
|
||||
|
||||
def GetLoadAverage(self,get):
|
||||
@@ -253,14 +252,14 @@ class system:
|
||||
c = os.getloadavg()
|
||||
except:
|
||||
c = [0,0,0]
|
||||
data = {};
|
||||
data['one'] = float(c[0]);
|
||||
data['five'] = float(c[1]);
|
||||
data['fifteen'] = float(c[2]);
|
||||
data['max'] = psutil.cpu_count() * 2;
|
||||
data['limit'] = data['max'];
|
||||
data['safe'] = data['max'] * 0.75;
|
||||
return data;
|
||||
data = {}
|
||||
data['one'] = float(c[0])
|
||||
data['five'] = float(c[1])
|
||||
data['fifteen'] = float(c[2])
|
||||
data['max'] = psutil.cpu_count() * 2
|
||||
data['limit'] = data['max']
|
||||
data['safe'] = data['max'] * 0.75
|
||||
return data
|
||||
|
||||
def GetAllInfo(self,get):
|
||||
data = {}
|
||||
@@ -306,7 +305,7 @@ class system:
|
||||
days = math.floor(hours / 24)
|
||||
hours = math.floor(hours - (days * 24))
|
||||
min = math.floor(min - (days * 60 * 24) - (hours * 60))
|
||||
sys_time = "{} Days".format(int(days))
|
||||
sys_time = "{} Day(s)".format(int(days))
|
||||
cache.set(key,sys_time,1800)
|
||||
return sys_time
|
||||
#return public.getMsg('SYS_BOOT_TIME',(str(int(days)),str(int(hours)),str(int(min))))
|
||||
@@ -328,6 +327,7 @@ class system:
|
||||
|
||||
used_all = psutil.cpu_percent(percpu=True)
|
||||
cpu_name = public.getCpuType() + " * {}".format(cpuW)
|
||||
|
||||
return used,cpuCount,used_all,cpu_name,cpuNum,cpuW
|
||||
|
||||
def get_cpu_percent_thead(self,interval):
|
||||
@@ -408,7 +408,7 @@ class system:
|
||||
n += 1
|
||||
try:
|
||||
inodes = tempInodes1[n-1].split()
|
||||
disk = re.findall(r"^(.+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\d%]{2,4})\s+(/.{0,50})$",tmp.strip())
|
||||
disk = re.findall(r"^(.+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\d%]{2,4})\s+(/.{0,100})$",tmp.strip())
|
||||
if disk: disk = disk[0]
|
||||
if len(disk) < 6: continue
|
||||
if disk[2].find('M') != -1: continue
|
||||
@@ -420,7 +420,7 @@ class system:
|
||||
arr = {}
|
||||
arr['filesystem'] = disk[0].strip()
|
||||
arr['type'] = disk[1].strip()
|
||||
arr['path'] = disk[6]
|
||||
arr['path'] = disk[6].replace('/usr/local/lighthouse/softwares/btpanel','/www')
|
||||
tmp1 = [disk[2],disk[3],disk[4],disk[5]]
|
||||
arr['size'] = tmp1
|
||||
arr['inodes'] = [inodes[1],inodes[2],inodes[3],inodes[4]]
|
||||
@@ -431,6 +431,63 @@ class system:
|
||||
cache.set(key,diskInfo,360)
|
||||
return diskInfo
|
||||
|
||||
|
||||
# 获取磁盘IO开销数据
|
||||
def get_disk_iostat(self):
|
||||
iokey = 'iostat'
|
||||
diskio = cache.get(iokey)
|
||||
mtime = int(time.time())
|
||||
if not diskio:
|
||||
diskio = {}
|
||||
diskio['info'] = None
|
||||
diskio['time'] = mtime
|
||||
diskio_1 = diskio['info']
|
||||
stime = mtime - diskio['time']
|
||||
if not stime: stime = 1
|
||||
diskInfo = {}
|
||||
diskInfo['ALL'] = {}
|
||||
diskInfo['ALL']['read_count'] = 0
|
||||
diskInfo['ALL']['write_count'] = 0
|
||||
diskInfo['ALL']['read_bytes'] = 0
|
||||
diskInfo['ALL']['write_bytes'] = 0
|
||||
diskInfo['ALL']['read_time'] = 0
|
||||
diskInfo['ALL']['write_time'] = 0
|
||||
diskInfo['ALL']['read_merged_count'] = 0
|
||||
diskInfo['ALL']['write_merged_count'] = 0
|
||||
try:
|
||||
if os.path.exists('/proc/diskstats'):
|
||||
diskio_2 = psutil.disk_io_counters(perdisk=True)
|
||||
if not diskio_1:
|
||||
diskio_1 = diskio_2
|
||||
for disk_name in diskio_2.keys():
|
||||
diskInfo[disk_name] = {}
|
||||
diskInfo[disk_name]['read_count'] = int((diskio_2[disk_name].read_count - diskio_1[disk_name].read_count) / stime)
|
||||
diskInfo[disk_name]['write_count'] = int((diskio_2[disk_name].write_count - diskio_1[disk_name].write_count) / stime)
|
||||
diskInfo[disk_name]['read_bytes'] = int((diskio_2[disk_name].read_bytes - diskio_1[disk_name].read_bytes) / stime)
|
||||
diskInfo[disk_name]['write_bytes'] = int((diskio_2[disk_name].write_bytes - diskio_1[disk_name].write_bytes) / stime)
|
||||
diskInfo[disk_name]['read_time'] = int((diskio_2[disk_name].read_time - diskio_1[disk_name].read_time) / stime)
|
||||
diskInfo[disk_name]['write_time'] = int((diskio_2[disk_name].write_time - diskio_1[disk_name].write_time) / stime)
|
||||
diskInfo[disk_name]['read_merged_count'] = int((diskio_2[disk_name].read_merged_count - diskio_1[disk_name].read_merged_count) / stime)
|
||||
diskInfo[disk_name]['write_merged_count'] = int((diskio_2[disk_name].write_merged_count - diskio_1[disk_name].write_merged_count) / stime)
|
||||
|
||||
diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count']
|
||||
diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count']
|
||||
diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes']
|
||||
diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes']
|
||||
if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']:
|
||||
diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time']
|
||||
if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']:
|
||||
diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time']
|
||||
diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count']
|
||||
diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count']
|
||||
|
||||
cache.set(iokey,{'info':diskio_2,'time':mtime})
|
||||
except:
|
||||
public.writeFile('/tmp/2',str(public.get_error_info()))
|
||||
return diskInfo
|
||||
return diskInfo
|
||||
|
||||
|
||||
#清理系统垃圾
|
||||
def ClearSystem(self,get):
|
||||
count = total = 0
|
||||
@@ -545,6 +602,7 @@ class system:
|
||||
|
||||
if get != False:
|
||||
networkInfo['cpu'] = self.GetCpuInfo(1)
|
||||
networkInfo['cpu_times'] = self.get_cpu_times()
|
||||
networkInfo['load'] = self.GetLoadAverage(get)
|
||||
networkInfo['mem'] = self.GetMemInfo(get)
|
||||
networkInfo['version'] = session['version']
|
||||
@@ -561,9 +619,41 @@ class system:
|
||||
networkInfo['user_info'] = panelSSL.panelSSL().GetUserInfo(None)
|
||||
networkInfo['up'] = round(float(networkInfo['up']),2)
|
||||
networkInfo['down'] = round(float(networkInfo['down']),2)
|
||||
networkInfo['iostat'] = self.get_disk_iostat()
|
||||
|
||||
return networkInfo
|
||||
|
||||
|
||||
|
||||
def get_cpu_times(self):
|
||||
data = {}
|
||||
try:
|
||||
cpu_times_p = psutil.cpu_times_percent()
|
||||
data['user'] = cpu_times_p.user
|
||||
data['nice'] = cpu_times_p.nice
|
||||
data['system'] = cpu_times_p.system
|
||||
data['idle'] = cpu_times_p.idle
|
||||
data['iowait'] = cpu_times_p.iowait
|
||||
data['irq'] = cpu_times_p.irq
|
||||
data['softirq'] = cpu_times_p.softirq
|
||||
data['steal'] = cpu_times_p.steal
|
||||
data['guest'] = cpu_times_p.guest
|
||||
data['guest_nice'] = cpu_times_p.guest_nice
|
||||
data['total_processes'] = 0
|
||||
data['active_processes'] = 0
|
||||
for pid in psutil.pids():
|
||||
try:
|
||||
p = psutil.Process(pid)
|
||||
if p.status() == 'running':
|
||||
data['active_processes'] += 1
|
||||
except:
|
||||
continue
|
||||
data['total_processes'] += 1
|
||||
|
||||
except: pass
|
||||
return data
|
||||
|
||||
|
||||
|
||||
|
||||
def GetNetWorkApi(self,get=None):
|
||||
return self.GetNetWork()
|
||||
@@ -852,9 +942,9 @@ class system:
|
||||
self.ssh.connect('localhost', public.GetSSHPort())
|
||||
except:
|
||||
return False
|
||||
import firewalls,common
|
||||
import firewalls
|
||||
fw = firewalls.firewalls()
|
||||
get = common.dict_obj()
|
||||
get = public.dict_obj()
|
||||
get.status = '0'
|
||||
fw.SetSshStatus(get)
|
||||
self.ssh.connect('127.0.0.1', public.GetSSHPort())
|
||||
|
||||
@@ -45,6 +45,8 @@ class userlogin:
|
||||
num = self.limit_address('+')
|
||||
return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
|
||||
_key_file = "/www/server/panel/data/two_step_auth.txt"
|
||||
#登陆告警
|
||||
public.login_send_body("Userinfo",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))
|
||||
if hasattr(post,'vcode'):
|
||||
if self.limit_address('?',v="vcode") < 1: return public.returnJson(False,'You have failed verification many times, forbidden for 10 minutes'),json_header
|
||||
import pyotp
|
||||
@@ -119,6 +121,7 @@ class userlogin:
|
||||
|
||||
def request_temp(self,get):
|
||||
try:
|
||||
if len(get.__dict__.keys()) > 2: return public.getMsg('INIT_ARGS_ERR')
|
||||
if not hasattr(get,'tmp_token'): return public.getMsg('INIT_ARGS_ERR')
|
||||
if len(get.tmp_token) != 48: return public.getMsg('INIT_ARGS_ERR')
|
||||
if not re.match(r"^\w+$",get.tmp_token):return public.getMsg('INIT_ARGS_ERR')
|
||||
@@ -159,6 +162,7 @@ class userlogin:
|
||||
self.set_request_token()
|
||||
self.login_token()
|
||||
self.set_cdn_host(get)
|
||||
public.login_send_body("Temporary authorization",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))
|
||||
return redirect('/')
|
||||
except:
|
||||
return public.getMsg('LOGIN_FAIL')
|
||||
|
||||
+38
-20
@@ -42,6 +42,8 @@ class ScanLogin(object):
|
||||
def login_qrcode(self, get):
|
||||
tid = public.GetRandomString(12)
|
||||
qrcode_str = 'https://app.bt.cn/app.html?&panel_url='+public.getPanelAddr()+'&v=' + public.GetRandomString(3)+'?login&tid=' + tid
|
||||
data = public.get_session_id() + ':' + str(time.time())
|
||||
public.writeFile(self.app_path + "app_login_check.pl", data)
|
||||
cache.set(tid,public.get_session_id(),360)
|
||||
cache.set(public.get_session_id(),tid,360)
|
||||
return public.returnMsg(True, qrcode_str)
|
||||
@@ -75,31 +77,47 @@ class ScanLogin(object):
|
||||
import config
|
||||
config.config().reload_session()
|
||||
public.writeFile(login_type,'True')
|
||||
public.login_send_body("Wechat program",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))
|
||||
return public.returnMsg(True, 'login successful')
|
||||
return public.returnMsg(False, 'Login failed')
|
||||
|
||||
|
||||
#验证APP是否登录成功
|
||||
def check_app_login(self,get):
|
||||
session_id = public.get_session_id()
|
||||
if cache.get(session_id) != 'True':
|
||||
return public.returnMsg(False,'Wait for the app to scan the code and log in')
|
||||
cache.delete(session_id)
|
||||
userInfo = public.M('users').where("id=?",(1,)).field('id,username').find()
|
||||
session['login'] = True
|
||||
session['username'] = userInfo['username']
|
||||
session['tmp_login'] = True
|
||||
public.WriteLog('TYPE_LOGIN','APP scan code login, account: {}, login IP: {}'.format(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
|
||||
cache.delete('panelNum')
|
||||
cache.delete('dologin')
|
||||
sess_input_path = 'data/session_last.pl'
|
||||
public.writeFile(sess_input_path,str(int(time.time())))
|
||||
login_type = 'data/app_login.pl'
|
||||
self.set_request_token()
|
||||
import config
|
||||
config.config().reload_session()
|
||||
public.writeFile(login_type,'True')
|
||||
return public.returnMsg(True,'login successful!')
|
||||
#判断是否存在绑定
|
||||
btapp_info = json.loads(public.readFile('/www/server/panel/config/api.json'))
|
||||
if not btapp_info:return public.returnMsg(False,'Unbound')
|
||||
if not btapp_info['open']:return public.returnMsg(False,'API not open')
|
||||
if not 'apps' in btapp_info:return public.returnMsg(False,'Unbound phone')
|
||||
if not btapp_info['apps']:return public.returnMsg(False,'Unbound phone')
|
||||
try:
|
||||
session_id=public.get_session_id()
|
||||
if not os.path.exists(self.app_path+'app_login_check.pl'):return public.returnMsg(False,'Wait for the app to scan the code and log in 1')
|
||||
data = public.readFile(self.app_path+'app_login_check.pl')
|
||||
public.ExecShell('rm ' + self.app_path+"app_login_check.pl")
|
||||
secret_key, init_time = data.split(':')
|
||||
if len(session_id)!=64:return public.returnMsg(False,'Wait for the app to scan the code and log in 2')
|
||||
if time.time() - float(init_time) < 180 and session_id != secret_key:
|
||||
return public.returnMsg(False,'Wait for the app to scan the code and log in')
|
||||
cache.delete(session_id)
|
||||
userInfo = public.M('users').where("id=?",(1,)).field('id,username').find()
|
||||
session['login'] = True
|
||||
session['username'] = userInfo['username']
|
||||
session['tmp_login'] = True
|
||||
public.WriteLog('TYPE_LOGIN','APP scan code login, account: {}, login IP: {}'.format(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
|
||||
cache.delete('panelNum')
|
||||
cache.delete('dologin')
|
||||
sess_input_path = 'data/session_last.pl'
|
||||
public.writeFile(sess_input_path,str(int(time.time())))
|
||||
login_type = 'data/app_login.pl'
|
||||
self.set_request_token()
|
||||
import config
|
||||
config.config().reload_session()
|
||||
public.writeFile(login_type,'True')
|
||||
public.login_send_body("aaPanel Mobile",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))
|
||||
return public.returnMsg(True,'login successful!')
|
||||
except:
|
||||
return public.returnMsg(False, 'login fail')
|
||||
|
||||
class SelfModule():
|
||||
'''
|
||||
@@ -198,7 +216,7 @@ class wxapp(SelfModule, ScanLogin):
|
||||
if type(encryption_str) == str:
|
||||
encryption_str = encryption_str.encode()
|
||||
if get['sgin'] == public.md5(binascii.hexlify(base64.b64encode(encryption_str))):
|
||||
if public.GetClientIp() in ['118.24.150.167', '103.224.251.67', '125.88.182.170', '47.52.194.186', '39.104.53.226','119.147.144.162']:
|
||||
if public.GetClientIp() in ['47.52.194.186']:
|
||||
return True
|
||||
return public.returnMsg(False, 'UNAUTHORIZED')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user