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:
jose
2021-06-11 11:50:57 +08:00
parent aacc0df179
commit 3ac20e08ce
93 changed files with 11684 additions and 7756 deletions
+62 -5
View File
@@ -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
View File
@@ -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:
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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')
+5 -1
View File
@@ -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
View File
@@ -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 = {'&amp;':'&'}
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 -2
View File
@@ -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
+109
View File
@@ -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
+563
View File
@@ -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)
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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
View File
@@ -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):
'''
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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 reminderYour 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 reminderYour 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
+57
View File
@@ -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'
+13 -12
View File
@@ -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'
+98
View File
@@ -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))
+9 -3
View File
@@ -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'
+1 -1
View File
@@ -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 -4
View File
@@ -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])
+57
View File
@@ -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'
+41 -2
View File
@@ -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
+1 -1
View File
@@ -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 = [
File diff suppressed because it is too large Load Diff
+13 -4
View File
@@ -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
View File
@@ -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:
+179
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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):
'''
关闭密码访问
+3 -4
View File
@@ -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
View File
@@ -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())
+4
View File
@@ -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
View File
@@ -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')