mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-17 21:25:47 +02:00
3767 lines
119 KiB
Python
3767 lines
119 KiB
Python
# coding: utf-8
|
||
# +-------------------------------------------------------------------
|
||
# | 宝塔Linux面板
|
||
# +-------------------------------------------------------------------
|
||
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||
# +-------------------------------------------------------------------
|
||
# | Author: hwliang <hwl@bt.cn>
|
||
# +-------------------------------------------------------------------
|
||
|
||
# --------------------------------
|
||
# 宝塔公共库
|
||
# --------------------------------
|
||
|
||
import json,os,sys,time,re,socket,importlib,binascii,base64,io,string
|
||
from random import choice
|
||
_LAN_PUBLIC = None
|
||
_LAN_LOG = None
|
||
_LAN_TEMPLATE = None
|
||
|
||
if sys.version_info[0] == 2:
|
||
reload(sys)
|
||
sys.setdefaultencoding('utf8')
|
||
else:
|
||
from importlib import reload
|
||
|
||
def M(table):
|
||
"""
|
||
@name 访问面板数据库
|
||
@author hwliang<hwl@bt.cn>
|
||
@table 被访问的表名(必需)
|
||
@return db.Sql object
|
||
|
||
ps: 默认访问data/default.db
|
||
"""
|
||
import db
|
||
with db.Sql() as sql:
|
||
#sql = db.Sql()
|
||
return sql.table(table)
|
||
|
||
def HttpGet(url,timeout = 6,headers = {}):
|
||
"""
|
||
@name 发送GET请求
|
||
@author hwliang<hwl@bt.cn>
|
||
@url 被请求的URL地址(必需)
|
||
@timeout 超时时间默认60秒
|
||
@return string
|
||
"""
|
||
if is_local(): return False
|
||
import http_requests
|
||
res = http_requests.get(url,timeout=timeout,headers = headers,verify=False)
|
||
if res.status_code == 0:
|
||
if headers: return False
|
||
s_body = res.text
|
||
return s_body
|
||
s_body = res.text
|
||
del res
|
||
return s_body
|
||
|
||
def http_get_home(url,timeout,ex):
|
||
"""
|
||
@name Get方式使用优选节点访问官网
|
||
@author hwliang<hwl@bt.cn>
|
||
@param url 当前官网URL地址
|
||
@param timeout 用于测试超时时间
|
||
@param ex 上一次错误的响应内容
|
||
@return string 响应内容
|
||
|
||
如果已经是优选节点,将直接返回ex
|
||
"""
|
||
try:
|
||
home = 'www.bt.cn'
|
||
if url.find(home) == -1: return ex
|
||
hosts_file = "config/hosts.json"
|
||
if not os.path.exists(hosts_file): return ex
|
||
hosts = json.loads(readFile(hosts_file))
|
||
headers = {"host":home}
|
||
for host in hosts:
|
||
new_url = url.replace(home,host)
|
||
res = HttpGet(new_url,timeout,headers)
|
||
if res:
|
||
writeFile("data/home_host.pl",host)
|
||
# set_home_host(host)
|
||
return res
|
||
return ex
|
||
except: return ex
|
||
|
||
|
||
# def set_home_host(host):
|
||
# """
|
||
# @name 设置官网hosts
|
||
# @author hwliang<hwl@bt.cn>
|
||
# @param host IP地址
|
||
# @return void
|
||
# """
|
||
# ExecShell('sed -i "/www.bt.cn/d" /etc/hosts')
|
||
# ExecShell("echo '' >> /etc/hosts")
|
||
# ExecShell("echo '%s www.bt.cn' >> /etc/hosts" % host)
|
||
# ExecShell('sed -i "/^\s*$/d" /etc/hosts')
|
||
|
||
def httpGet(url,timeout=6):
|
||
return HttpGet(url,timeout)
|
||
|
||
def HttpPost(url,data,timeout = 6,headers = {}):
|
||
"""
|
||
发送POST请求
|
||
@url 被请求的URL地址(必需)
|
||
@data POST参数,可以是字符串或字典(必需)
|
||
@timeout 超时时间默认60秒
|
||
return string
|
||
"""
|
||
if is_local(): return False
|
||
import http_requests
|
||
res = http_requests.post(url,data=data,timeout=timeout,headers = headers)
|
||
if res.status_code == 0:
|
||
if headers: return False
|
||
s_body = res.text
|
||
return s_body
|
||
s_body = res.text
|
||
return s_body
|
||
|
||
def httpPost(url,data,timeout=6):
|
||
"""
|
||
@name 发送POST请求
|
||
@author hwliang<hwl@bt.cn>
|
||
@param url 被请求的URL地址(必需)
|
||
@param data POST参数,可以是字符串或字典(必需)
|
||
@param timeout 超时时间默认60秒
|
||
@return string
|
||
"""
|
||
return HttpPost(url,data,timeout)
|
||
|
||
def check_home():
|
||
return True
|
||
|
||
def Md5(strings):
|
||
"""
|
||
@name 生成MD5
|
||
@author hwliang<hwl@bt.cn>
|
||
@param strings 要被处理的字符串
|
||
@return string(32)
|
||
"""
|
||
if type(strings) != bytes:
|
||
strings = strings.encode()
|
||
import hashlib
|
||
m = hashlib.md5()
|
||
m.update(strings)
|
||
return m.hexdigest()
|
||
|
||
def md5(strings):
|
||
return Md5(strings)
|
||
|
||
def FileMd5(filename):
|
||
"""
|
||
@name 生成文件的MD5
|
||
@author hwliang<hwl@bt.cn>
|
||
@param filename 文件名
|
||
@return string(32) or False
|
||
"""
|
||
if not os.path.isfile(filename): return False
|
||
import hashlib
|
||
my_hash = hashlib.md5()
|
||
f = open(filename,'rb')
|
||
while True:
|
||
b = f.read(8096)
|
||
if not b :
|
||
break
|
||
my_hash.update(b)
|
||
f.close()
|
||
return my_hash.hexdigest()
|
||
|
||
def GetRandomString(length):
|
||
"""
|
||
@name 取随机字符串
|
||
@author hwliang<hwl@bt.cn>
|
||
@param length 要获取的长度
|
||
@return string(length)
|
||
"""
|
||
from random import Random
|
||
strings = ''
|
||
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
|
||
chrlen = len(chars) - 1
|
||
random = Random()
|
||
for i in range(length):
|
||
strings += chars[random.randint(0, chrlen)]
|
||
return strings
|
||
|
||
def ReturnJson(status,msg,args=()):
|
||
"""
|
||
@name 取通用Json返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return string(json)
|
||
"""
|
||
return GetJson(ReturnMsg(status, msg, args))
|
||
|
||
def returnJson(status,msg,args=()):
|
||
"""
|
||
@name 取通用Json返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return string(json)
|
||
"""
|
||
return ReturnJson(status,msg,args)
|
||
|
||
def ReturnMsg(status,msg,args = ()):
|
||
"""
|
||
@name 取通用dict返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return dict {"status":bool,"msg":string}
|
||
"""
|
||
try:
|
||
log_message = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/public.json'))
|
||
except:
|
||
log_message = {}
|
||
keys = log_message.keys()
|
||
if type(msg) == str:
|
||
if msg in keys:
|
||
msg = log_message[msg]
|
||
for i in range(len(args)):
|
||
rep = '{'+str(i+1)+'}'
|
||
msg = msg.replace(rep,args[i])
|
||
return {'status':status,'msg':msg}
|
||
|
||
def returnMsg(status,msg,args = ()):
|
||
"""
|
||
@name 取通用dict返回
|
||
@author hwliang<hwl@bt.cn>
|
||
@param status 返回状态
|
||
@param msg 返回消息
|
||
@return dict {"status":bool,"msg":string}
|
||
"""
|
||
return ReturnMsg(status,msg,args)
|
||
|
||
|
||
def GetFileMode(filename):
|
||
"""
|
||
@name 取文件权限字符串
|
||
@author hwliang<hwl@bt.cn>
|
||
@param filename 文件全路径
|
||
@return string 如:644/777/755
|
||
"""
|
||
stat = os.stat(filename)
|
||
accept = str(oct(stat.st_mode)[-3:])
|
||
return accept
|
||
|
||
def get_mode_and_user(path):
|
||
'''取文件或目录权限信息'''
|
||
import pwd
|
||
data = {}
|
||
if not os.path.exists(path): return None
|
||
stat = os.stat(path)
|
||
data['mode'] = str(oct(stat.st_mode)[-3:])
|
||
try:
|
||
data['user'] = pwd.getpwuid(stat.st_uid).pw_name
|
||
except:
|
||
data['user'] = str(stat.st_uid)
|
||
return data
|
||
|
||
|
||
def GetJson(data):
|
||
"""
|
||
将对象转换为JSON
|
||
@data 被转换的对象(dict/list/str/int...)
|
||
"""
|
||
from json import dumps
|
||
if data == bytes: data = data.decode('utf-8')
|
||
try:
|
||
return dumps(data,ensure_ascii=False)
|
||
except:
|
||
return dumps(returnMsg(False,"WRONG_RESPONSE", (str(data))))
|
||
|
||
def getJson(data):
|
||
return GetJson(data)
|
||
|
||
def ReadFile(filename,mode = 'r'):
|
||
"""
|
||
读取文件内容
|
||
@filename 文件名
|
||
return string(bin) 若文件不存在,则返回None
|
||
"""
|
||
import os
|
||
if not os.path.exists(filename): return False
|
||
try:
|
||
fp = open(filename, mode)
|
||
f_body = fp.read()
|
||
fp.close()
|
||
except Exception as ex:
|
||
if sys.version_info[0] != 2:
|
||
try:
|
||
fp = open(filename, mode,encoding="utf-8")
|
||
f_body = fp.read()
|
||
fp.close()
|
||
except:
|
||
fp = open(filename, mode,encoding="GBK")
|
||
f_body = fp.read()
|
||
fp.close()
|
||
else:
|
||
return False
|
||
return f_body
|
||
|
||
def readFile(filename,mode='r'):
|
||
'''
|
||
@name 读取指定文件数据
|
||
@author hwliang<2021-06-09>
|
||
@param filename<string> 文件名
|
||
@param mode<string> 文件打开模式,默认r
|
||
@return string or bytes or False 如果返回False则说明读取失败
|
||
'''
|
||
return ReadFile(filename,mode)
|
||
|
||
def WriteFile(filename,s_body,mode='w+'):
|
||
"""
|
||
写入文件内容
|
||
@filename 文件名
|
||
@s_body 欲写入的内容
|
||
return bool 若文件不存在则尝试自动创建
|
||
"""
|
||
try:
|
||
fp = open(filename, mode)
|
||
fp.write(s_body)
|
||
fp.close()
|
||
return True
|
||
except:
|
||
try:
|
||
fp = open(filename, mode,encoding="utf-8")
|
||
fp.write(s_body)
|
||
fp.close()
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
def writeFile(filename,s_body,mode='w+'):
|
||
'''
|
||
@name 写入到指定文件
|
||
@author hwliang<2021-06-09>
|
||
@param filename<string> 文件名
|
||
@param s_boey<string/bytes> 被写入的内容,字节或字符串
|
||
@param mode<string> 文件打开模式,默认w+
|
||
@return bool
|
||
'''
|
||
return WriteFile(filename,s_body,mode)
|
||
|
||
def WriteLog(type,logMsg,args=(),not_web = False):
|
||
#写日志
|
||
try:
|
||
import time,db,json
|
||
username = 'system'
|
||
uid = 1
|
||
tmp_msg = ''
|
||
if not not_web:
|
||
try:
|
||
from BTPanel import session
|
||
if 'username' in session:
|
||
username = session['username']
|
||
uid = session['uid']
|
||
if session.get('debug') == 1: return
|
||
except:
|
||
pass
|
||
global _LAN_LOG
|
||
if not _LAN_LOG:
|
||
_LAN_LOG = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/log.json'))
|
||
keys = _LAN_LOG.keys()
|
||
if logMsg in keys:
|
||
logMsg = _LAN_LOG[logMsg]
|
||
for i in range(len(args)):
|
||
rep = '{'+str(i+1)+'}'
|
||
logMsg = logMsg.replace(rep,args[i])
|
||
if type in keys: type = _LAN_LOG[type]
|
||
sql = db.Sql()
|
||
mDate = time.strftime('%Y-%m-%d %X',time.localtime())
|
||
data = (uid,username,type,xssencode2(logMsg + tmp_msg),mDate)
|
||
result = sql.table('logs').add('uid,username,type,log,addtime',data)
|
||
except:
|
||
pass
|
||
|
||
def GetLanguage():
|
||
'''
|
||
取语言
|
||
'''
|
||
return GetConfigValue("language")
|
||
|
||
def get_language():
|
||
return GetLanguage()
|
||
|
||
def GetConfigValue(key):
|
||
'''
|
||
取配置值
|
||
'''
|
||
config = GetConfig()
|
||
if not key in config.keys():
|
||
if key == 'download': return 'https://node.aapanel.com'
|
||
return None
|
||
return config[key]
|
||
|
||
def SetConfigValue(key,value):
|
||
config = GetConfig()
|
||
config[key] = value
|
||
WriteConfig(config)
|
||
|
||
def GetConfig():
|
||
'''
|
||
取所有配置项
|
||
'''
|
||
path = "config/config.json"
|
||
if not os.path.exists(path): return {}
|
||
f_body = ReadFile(path)
|
||
if not f_body: return {}
|
||
return json.loads(f_body)
|
||
|
||
def WriteConfig(config):
|
||
path = "config/config.json"
|
||
WriteFile(path, json.dumps(config))
|
||
|
||
|
||
def GetLan(key):
|
||
"""
|
||
取提示消息
|
||
"""
|
||
global _LAN_TEMPLATE
|
||
if not _LAN_TEMPLATE:
|
||
_LAN_TEMPLATE = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/template.json'))
|
||
keys = _LAN_TEMPLATE.keys()
|
||
msg = None
|
||
if key in keys:
|
||
msg = _LAN_TEMPLATE[key]
|
||
return msg
|
||
def getLan(key):
|
||
return GetLan(key)
|
||
|
||
def GetMsg(key,args = ()):
|
||
try:
|
||
global _LAN_PUBLIC
|
||
if not _LAN_PUBLIC:
|
||
_LAN_PUBLIC = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/public.json'))
|
||
keys = _LAN_PUBLIC.keys()
|
||
msg = None
|
||
if key in keys:
|
||
msg = _LAN_PUBLIC[key]
|
||
for i in range(len(args)):
|
||
rep = '{'+str(i+1)+'}'
|
||
msg = msg.replace(rep,args[i])
|
||
return msg
|
||
except:
|
||
return key
|
||
def getMsg(key,args = ()):
|
||
return GetMsg(key,args)
|
||
|
||
|
||
# 获取Web服务器
|
||
def GetWebServer():
|
||
if os.path.exists('{}/apache/bin/apachectl'.format(get_setup_path())):
|
||
webserver = 'apache'
|
||
elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
|
||
webserver = 'openlitespeed'
|
||
else:
|
||
webserver = 'nginx'
|
||
return webserver
|
||
|
||
def get_webserver():
|
||
return GetWebServer()
|
||
|
||
def ServiceReload():
|
||
#重载Web服务配置
|
||
if os.path.exists('{}/nginx/sbin/nginx'.format(get_setup_path())):
|
||
result = ExecShell('/etc/init.d/nginx reload')
|
||
if result[1].find('nginx.pid') != -1:
|
||
ExecShell('pkill -9 nginx && sleep 1')
|
||
ExecShell('/etc/init.d/nginx start')
|
||
elif os.path.exists('{}/apache/bin/apachectl'.format(get_setup_path())):
|
||
result = ExecShell('/etc/init.d/httpd reload')
|
||
else:
|
||
result = ExecShell('rm -f /tmp/lshttpd/*.sock* && /usr/local/lsws/bin/lswsctrl restart')
|
||
return result
|
||
|
||
def serviceReload():
|
||
return ServiceReload()
|
||
|
||
|
||
|
||
def get_preexec_fn(run_user):
|
||
'''
|
||
@name 获取指定执行用户预处理函数
|
||
@author hwliang<2021-08-19>
|
||
@param run_user<string> 运行用户
|
||
@return 预处理函数
|
||
'''
|
||
import pwd
|
||
pid = pwd.getpwnam(run_user)
|
||
uid = pid.pw_uid
|
||
gid = pid.pw_gid
|
||
|
||
def _exec_rn():
|
||
os.setgid(gid)
|
||
os.setuid(uid)
|
||
return _exec_rn
|
||
|
||
|
||
def ExecShell(cmdstring, timeout=None, shell=True,cwd=None,env=None,user = None):
|
||
'''
|
||
@name 执行命令
|
||
@author hwliang<2021-08-19>
|
||
@param cmdstring 命令 [必传]
|
||
@param timeout 超时时间
|
||
@param shell 是否通过shell运行
|
||
@param cwd 进入的目录
|
||
@param env 环境变量
|
||
@param user 执行用户名
|
||
@return 命令执行结果
|
||
'''
|
||
a = ''
|
||
e = ''
|
||
import subprocess,tempfile
|
||
preexec_fn = None
|
||
tmp_dir = '/dev/shm'
|
||
if user:
|
||
preexec_fn = get_preexec_fn(user)
|
||
tmp_dir = '/tmp'
|
||
try:
|
||
rx = md5(cmdstring)
|
||
succ_f = tempfile.SpooledTemporaryFile(max_size=4096,mode='wb+',suffix='_succ',prefix='btex_' + rx ,dir=tmp_dir)
|
||
err_f = tempfile.SpooledTemporaryFile(max_size=4096,mode='wb+',suffix='_err',prefix='btex_' + rx ,dir=tmp_dir)
|
||
sub = subprocess.Popen(cmdstring, close_fds=True, shell=shell,bufsize=128,stdout=succ_f,stderr=err_f,cwd=cwd,env=env,preexec_fn=preexec_fn)
|
||
if timeout:
|
||
s = 0
|
||
d = 0.01
|
||
while sub.poll() is None:
|
||
time.sleep(d)
|
||
s += d
|
||
if s >= timeout:
|
||
if not err_f.closed: err_f.close()
|
||
if not succ_f.closed: succ_f.close()
|
||
return 'Timed out'
|
||
else:
|
||
sub.wait()
|
||
|
||
err_f.seek(0)
|
||
succ_f.seek(0)
|
||
a = succ_f.read()
|
||
e = err_f.read()
|
||
if not err_f.closed: err_f.close()
|
||
if not succ_f.closed: succ_f.close()
|
||
except:
|
||
return '',get_error_info()
|
||
try:
|
||
#编码修正
|
||
if type(a) == bytes: a = a.decode('utf-8')
|
||
if type(e) == bytes: e = e.decode('utf-8')
|
||
except:pass
|
||
|
||
return a,e
|
||
|
||
def GetLocalIp():
|
||
# 取本地外网IP
|
||
try:
|
||
filename = 'data/iplist.txt'
|
||
ipaddress = readFile(filename)
|
||
if not ipaddress:
|
||
url = 'http://pv.sohu.com/cityjson?ie=utf-8'
|
||
m_str = HttpGet(url)
|
||
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()
|
||
return ipaddress
|
||
except:
|
||
try:
|
||
url = 'https://brandnew.aapanel.com/api/common/getClientIP'
|
||
return HttpGet(url)
|
||
except:
|
||
return GetHost()
|
||
|
||
def is_ipv4(ip):
|
||
try:
|
||
socket.inet_pton(socket.AF_INET, ip)
|
||
except AttributeError:
|
||
try:
|
||
socket.inet_aton(ip)
|
||
except socket.error:
|
||
return False
|
||
return ip.count('.') == 3
|
||
except socket.error:
|
||
return False
|
||
return True
|
||
|
||
|
||
def is_ipv6(ip):
|
||
try:
|
||
socket.inet_pton(socket.AF_INET6, ip)
|
||
except socket.error:
|
||
return False
|
||
return True
|
||
|
||
|
||
def check_ip(ip):
|
||
return is_ipv4(ip) or is_ipv6(ip)
|
||
|
||
def GetHost(port = False):
|
||
from flask import request
|
||
host_tmp = request.headers.get('host')
|
||
if not host_tmp:
|
||
if request.url_root:
|
||
tmp = re.findall(r"(https|http)://([\[\]\w:\.-]+)",request.url_root)
|
||
if tmp: host_tmp = tmp[0][1]
|
||
if not host_tmp:
|
||
host_tmp = GetLocalIp() + ':' + readFile('data/port.pl').strip()
|
||
try:
|
||
if host_tmp.find(':') == -1: host_tmp += ':80'
|
||
except:
|
||
host_tmp = "127.0.0.1:8888"
|
||
h = host_tmp.split(':')
|
||
if port: return h[-1]
|
||
if len(h) > 2:
|
||
h = ":".join(h[:-1])
|
||
return h
|
||
return h[0]
|
||
|
||
def GetClientIp():
|
||
from flask import request
|
||
return request.remote_addr.replace('::ffff:', '')
|
||
|
||
def get_client_ip():
|
||
return GetClientIp()
|
||
|
||
def phpReload(version):
|
||
#重载PHP配置
|
||
import os
|
||
if os.path.exists(get_setup_path()+'/php/' + version + '/libphp5.so'):
|
||
ExecShell('/etc/init.d/httpd reload')
|
||
else:
|
||
ExecShell('/etc/init.d/php-fpm-'+version+' reload')
|
||
ExecShell("/etc/init.d/php-fpm-{} start".format(version))
|
||
|
||
|
||
def get_timeout(url,timeout=3):
|
||
try:
|
||
start = time.time()
|
||
result = int(httpGet(url,timeout))
|
||
return result,int((time.time() - start) * 1000 - 500)
|
||
except: return 0,False
|
||
|
||
def get_url(timeout = 0.5):
|
||
return 'https://node.aapanel.com'
|
||
|
||
import json
|
||
try:
|
||
pkey = 'node_url'
|
||
node_url = cache_get(pkey)
|
||
if node_url: return node_url
|
||
nodeFile = 'data/node.json'
|
||
node_list = json.loads(readFile(nodeFile))
|
||
mnode1 = []
|
||
mnode2 = []
|
||
mnode3 = []
|
||
new_node_list = {}
|
||
for node in node_list:
|
||
node['net'],node['ping'] = get_timeout(node['protocol'] + node['address'] + ':' + node['port'] + '/net_test',1)
|
||
new_node_list[node['address']] = node['ping']
|
||
if not node['ping']: continue
|
||
if node['ping'] < 100: #当响应时间<100ms且可用带宽大于1500KB时
|
||
if node['net'] > 1500:
|
||
mnode1.append(node)
|
||
elif node['net'] > 1000:
|
||
mnode3.append(node)
|
||
else:
|
||
if node['net'] > 1000: #当响应时间>=100ms且可用带宽大于1000KB时
|
||
mnode2.append(node)
|
||
if node['ping'] < 100:
|
||
if node['net'] > 3000: break #有节点可用带宽大于3000时,不再检查其它节点
|
||
if mnode1: #优选低延迟高带宽
|
||
mnode = sorted(mnode1,key= lambda x:x['net'],reverse=True)
|
||
elif mnode3: #备选低延迟,中等带宽
|
||
mnode = sorted(mnode3,key= lambda x:x['net'],reverse=True)
|
||
else: #终选中等延迟,中等带宽
|
||
mnode = sorted(mnode2,key= lambda x:x['ping'],reverse=False)
|
||
|
||
if not mnode: return 'https://node.aapanel.com'
|
||
|
||
new_node_keys = new_node_list.keys()
|
||
for i in range(len(node_list)):
|
||
if node_list[i]['address'] in new_node_keys:
|
||
node_list[i]['ping'] = new_node_list[node_list[i]['address']]
|
||
else:
|
||
node_list[i]['ping'] = 500
|
||
|
||
new_node_list = sorted(node_list,key=lambda x: x['ping'],reverse=False)
|
||
writeFile(nodeFile,json.dumps(new_node_list))
|
||
node_url = mnode[0]['protocol'] + mnode[0]['address'] + ':' + mnode[0]['port']
|
||
cache_set(pkey,node_url,86400)
|
||
return node_url
|
||
except:
|
||
return 'https://node.aapanel.com'
|
||
|
||
|
||
#过滤输入
|
||
def checkInput(data):
|
||
if not data: return data
|
||
if type(data) != str: return data
|
||
checkList = [
|
||
{'d':'<','r':'<'},
|
||
{'d':'>','r':'>'},
|
||
{'d':'\'','r':'‘'},
|
||
{'d':'"','r':'“'},
|
||
{'d':'&','r':'&'},
|
||
{'d':'#','r':'#'},
|
||
{'d':'<','r':'<'}
|
||
]
|
||
for v in checkList:
|
||
data = data.replace(v['d'],v['r'])
|
||
return data
|
||
|
||
#取文件指定尾行数
|
||
def GetNumLines(path,num,p=1):
|
||
pyVersion = sys.version_info[0]
|
||
max_len = 1024*128
|
||
try:
|
||
import cgi
|
||
if not os.path.exists(path): return ""
|
||
start_line = (p - 1) * num
|
||
count = start_line + num
|
||
fp = open(path,'r')
|
||
buf = ""
|
||
fp.seek(-1, 2)
|
||
if fp.read(1) == "\n": fp.seek(-1, 2)
|
||
data = []
|
||
total_len = 0
|
||
b = True
|
||
n = 0
|
||
for i in range(count):
|
||
while True:
|
||
newline_pos = str.rfind(str(buf), "\n")
|
||
pos = fp.tell()
|
||
if newline_pos != -1:
|
||
if n >= start_line:
|
||
line = buf[newline_pos + 1:]
|
||
line_len = len(line)
|
||
total_len += line_len
|
||
sp_len = total_len - max_len
|
||
if sp_len > 0:
|
||
line = line[sp_len:]
|
||
try:
|
||
data.insert(0,cgi.escape(line))
|
||
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 pyVersion == 3:
|
||
try:
|
||
if type(t_buf) == bytes: t_buf = t_buf.decode('utf-8')
|
||
except:t_buf = str(t_buf)
|
||
buf = t_buf + buf
|
||
fp.seek(-to_read, 1)
|
||
if pos - to_read == 0:
|
||
buf = "\n" + buf
|
||
if total_len >= max_len: break
|
||
if not b: break
|
||
fp.close()
|
||
result = "\n".join(data)
|
||
if not result: raise Exception('null')
|
||
except:
|
||
result = ExecShell("tail -n {} {}".format(num,path))[0]
|
||
if len(result) > max_len:
|
||
result = result[-max_len:]
|
||
|
||
try:
|
||
try:
|
||
result = json.dumps(result)
|
||
return json.loads(result).strip()
|
||
except:
|
||
if pyVersion == 2:
|
||
result = result.decode('utf8',errors='ignore')
|
||
else:
|
||
result = result.encode('utf-8',errors='ignore').decode("utf-8",errors="ignore")
|
||
return result.strip()
|
||
except: return ""
|
||
|
||
#验证证书
|
||
def CheckCert(certPath = 'ssl/certificate.pem'):
|
||
openssl = '/usr/local/openssl/bin/openssl'
|
||
if not os.path.exists(openssl): openssl = 'openssl'
|
||
certPem = readFile(certPath)
|
||
s = "\n-----BEGIN CERTIFICATE-----"
|
||
tmp = certPem.strip().split(s)
|
||
for tmp1 in tmp:
|
||
if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: tmp1 = s + tmp1
|
||
writeFile(certPath, tmp1)
|
||
result = ExecShell(openssl + " x509 -in " + certPath + " -noout -subject")
|
||
if result[1].find('-bash:') != -1: return True
|
||
if len(result[1]) > 2: return False
|
||
if result[0].find('error:') != -1: return False
|
||
return True
|
||
|
||
|
||
# 获取面板地址
|
||
def getPanelAddr():
|
||
from flask import request
|
||
protocol = 'https://' if os.path.exists("data/ssl.pl") else 'http://'
|
||
return protocol + request.headers.get('host')
|
||
|
||
|
||
# 字节单位转换
|
||
def to_size(size):
|
||
if not size: return '0.00 b'
|
||
size = float(size)
|
||
d = ('b','KB','MB','GB','TB')
|
||
s = d[0]
|
||
for b in d:
|
||
if size < 1024: return ("%.2f" % size) + ' ' + b
|
||
size = size / 1024
|
||
s = b
|
||
return ("%.2f" % size) + ' ' + b
|
||
|
||
|
||
def checkCode(code, outime=120):
|
||
# 校验验证码
|
||
from BTPanel import session, cache
|
||
try:
|
||
codeStr = cache.get('codeStr')
|
||
cache.delete('codeStr')
|
||
if not codeStr:
|
||
session['login_error'] = GetMsg('CODE_TIMEOUT')
|
||
return False
|
||
|
||
if md5(code.lower()) != codeStr:
|
||
session['login_error'] = GetMsg('CODE_ERR')
|
||
return False
|
||
return True
|
||
except:
|
||
session['login_error'] = GetMsg('CODE_NOT_EXISTS')
|
||
return False
|
||
|
||
#写进度
|
||
def writeSpeed(title,used,total,speed = 0):
|
||
import json
|
||
if not title:
|
||
data = {'title': None, 'progress': 0, 'total': 0, 'used': 0, 'speed': 0}
|
||
else:
|
||
progress = int((100.0 * used / total))
|
||
data = {'title': title, 'progress': progress, 'total': total, 'used': used, 'speed': speed}
|
||
writeFile('/tmp/panelSpeed.pl', json.dumps(data))
|
||
return True
|
||
|
||
#取进度
|
||
def getSpeed():
|
||
import json
|
||
data = readFile('/tmp/panelSpeed.pl')
|
||
if not data:
|
||
data = json.dumps({'title': None, 'progress': 0, 'total': 0, 'used': 0, 'speed': 0})
|
||
writeFile('/tmp/panelSpeed.pl', data)
|
||
return json.loads(data)
|
||
|
||
def get_requests_headers():
|
||
return {"Content-type":"application/x-www-form-urlencoded","User-Agent":"BT-Panel"}
|
||
|
||
def downloadFile(url,filename):
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
import requests
|
||
headers = {'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'}
|
||
r = requests.get(url, headers=headers,verify=False)
|
||
with open(filename,"wb") as f:
|
||
f.write(r.content)
|
||
else:
|
||
import urllib.request
|
||
import ssl
|
||
ssl._create_default_https_context = ssl._create_unverified_context
|
||
opener = urllib.request.build_opener()
|
||
opener.addheaders = [('User-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36')]
|
||
urllib.request.install_opener(opener)
|
||
urllib.request.urlretrieve(url, filename=filename)
|
||
except:
|
||
ExecShell("wget -O --no-check-certificate {} {}".format(filename,url))
|
||
|
||
def exists_args(args,get):
|
||
'''
|
||
@name 检查参数是否存在
|
||
@author hwliang<2021-06-08>
|
||
@param args<list or str> 参数列表 允许是列表或字符串
|
||
@param get<dict_obj> 参数对像
|
||
@return bool 都存在返回True,否则抛出KeyError异常
|
||
'''
|
||
if type(args) == str:
|
||
args = args.split(',')
|
||
for arg in args:
|
||
if not arg in get:
|
||
raise KeyError('Required parameters are missing:{}'.format(arg))
|
||
return True
|
||
|
||
|
||
def get_error_info():
|
||
import traceback
|
||
errorMsg = traceback.format_exc()
|
||
return errorMsg
|
||
|
||
|
||
def get_plugin_replace_rules():
|
||
'''
|
||
@name 获取插件文件内容替换规则
|
||
@author hwliang<2021-06-28>
|
||
@return list
|
||
'''
|
||
return [
|
||
{
|
||
"find":"[PATH]",
|
||
"replace": "[PATH]"
|
||
}
|
||
]
|
||
|
||
|
||
def get_plugin_title(plugin_name):
|
||
'''
|
||
@name 获取插件标题
|
||
@author hwliang<2021-06-24>
|
||
@param plugin_name<string> 插件名称
|
||
@return string
|
||
'''
|
||
|
||
info_file = '{}/{}/info.json'.format(get_plugin_path(),plugin_name)
|
||
try:
|
||
return json.loads(readFile(info_file))['title']
|
||
except:
|
||
return plugin_name
|
||
|
||
|
||
def get_error_object(plugin_title = None,plugin_name = None):
|
||
'''
|
||
@name 获取格式化错误响应对像
|
||
@author hwliang<2021-06-21>
|
||
@return Resp
|
||
'''
|
||
if not plugin_title: plugin_title = get_plugin_title(plugin_name)
|
||
try:
|
||
from BTPanel import request,Resp
|
||
is_cli = False
|
||
except:
|
||
is_cli = True
|
||
|
||
if is_cli:
|
||
raise get_error_info()
|
||
ss = '''404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
|
||
|
||
During handling of the above exception, another exception occurred:'''
|
||
error_info = get_error_info().strip().split(ss)[-1].strip()
|
||
request_info = '''REQUEST_DATE: {request_date}
|
||
PAN_VERSION: {panel_version}
|
||
OS_VERSION: {os_version}
|
||
REMOTE_ADDR: {remote_addr}
|
||
REQUEST_URI: {method} {full_path}
|
||
REQUEST_FORM: {request_form}
|
||
USER_AGENT: {user_agent}'''.format(
|
||
request_date = getDate(),
|
||
remote_addr = GetClientIp(),
|
||
method = request.method,
|
||
full_path = request.full_path,
|
||
request_form = request.form.to_dict(),
|
||
user_agent = request.headers.get('User-Agent'),
|
||
panel_version = version(),
|
||
os_version = get_os_version()
|
||
)
|
||
|
||
result =readFile('{}/BTPanel/templates/default/plugin_error.html'.format(get_panel_path())).format(
|
||
plugin_name=plugin_title,
|
||
request_info=request_info,
|
||
error_title=error_info.split("\n")[-1],
|
||
error_msg=error_info
|
||
)
|
||
return Resp(result,500)
|
||
|
||
# 搜索数据中是否存在
|
||
def inArray(arrays, searchStr):
|
||
for key in arrays:
|
||
if key == searchStr: return True
|
||
|
||
return False
|
||
|
||
#格式化指定时间戳
|
||
def format_date(format="%Y-%m-%d %H:%M:%S",times = None):
|
||
if not times: times = int(time.time())
|
||
time_local = time.localtime(times)
|
||
return time.strftime(format, time_local)
|
||
|
||
|
||
# 检查Web服务器配置文件是否有错误
|
||
def checkWebConfig():
|
||
f1 = '{}/'.format(get_vhost_path())
|
||
f2 = '{}/'.format(get_plugin_path())
|
||
setup_path = get_setup_path()
|
||
if not os.path.exists(f2 + 'btwaf'):
|
||
f3 = f1 + 'nginx/btwaf.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
# if not os.path.exists(f2 + 'btwaf_httpd'):
|
||
# f3 = f1 + 'apache/btwaf.conf'
|
||
# if os.path.exists(f3): os.remove(f3)
|
||
|
||
if not os.path.exists(f2 + 'total'):
|
||
f3 = f1 + 'apache/total.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
f3 = f1 + 'nginx/total.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
else:
|
||
if os.path.exists(setup_path + '/apache/modules/mod_lua.so'):
|
||
writeFile(f1 + 'apache/btwaf.conf','LoadModule lua_module modules/mod_lua.so')
|
||
writeFile(f1 + 'apache/total.conf','LuaHookLog {}/total/httpd_log.lua run_logs'.format(setup_path))
|
||
else:
|
||
f3 = f1 + 'apache/total.conf'
|
||
if os.path.exists(f3): os.remove(f3)
|
||
|
||
if get_webserver() == 'nginx':
|
||
result = ExecShell("ulimit -n 8192 ; {setup_path}/nginx/sbin/nginx -t -c {setup_path}/nginx/conf/nginx.conf".format(setup_path = setup_path))
|
||
searchStr = 'successful'
|
||
elif get_webserver() == 'apache':
|
||
# else:
|
||
result = ExecShell("ulimit -n 8192 ; {setup_path}/apache/bin/apachectl -t".format(setup_path = setup_path))
|
||
searchStr = 'Syntax OK'
|
||
else:
|
||
result = ["1","1"]
|
||
searchStr = "1"
|
||
if result[1].find(searchStr) == -1:
|
||
WriteLog("TYPE_SOFT", 'CONF_CHECK_ERR', (result[1],))
|
||
return result[1]
|
||
return True
|
||
|
||
# 检查是否为IPv4地址
|
||
def checkIp(ip):
|
||
p = re.compile(r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
|
||
if p.match(ip):
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
#检查端口是否合法
|
||
def checkPort(port):
|
||
if not re.match("^\d+$",port): return False
|
||
ports = ['21','25','443','8080','888','8888','8443']
|
||
if port in ports: return False
|
||
intport = int(port)
|
||
if intport < 1 or intport > 65535: return False
|
||
return True
|
||
|
||
#字符串取中间
|
||
def getStrBetween(startStr,endStr,srcStr):
|
||
start = srcStr.find(startStr)
|
||
if start == -1: return None
|
||
end = srcStr.find(endStr)
|
||
if end == -1: return None
|
||
return srcStr[start + 1:end]
|
||
|
||
#取CPU类型
|
||
def getCpuType():
|
||
cpuinfo = open('/proc/cpuinfo', 'r').read()
|
||
rep = "model\s+name\s+:\s+(.+)"
|
||
tmp = re.search(rep,cpuinfo,re.I)
|
||
cpuType = ''
|
||
if tmp:
|
||
cpuType = tmp.groups()[0]
|
||
else:
|
||
cpuinfo = ExecShell('LANG="en_US.UTF-8" && lscpu')[0]
|
||
rep = "Model\s+name:\s+(.+)"
|
||
tmp = re.search(rep, cpuinfo, re.I)
|
||
if tmp: cpuType = tmp.groups()[0]
|
||
return cpuType
|
||
|
||
|
||
# 检查是否允许重启
|
||
def IsRestart():
|
||
num = M('tasks').where('status!=?',('1',)).count()
|
||
if num > 0: return False
|
||
return True
|
||
|
||
#加密密码字符
|
||
def hasPwd(password):
|
||
import crypt
|
||
return crypt.crypt(password,password)
|
||
|
||
def getDate(format='%Y-%m-%d %X'):
|
||
# 取格式时间
|
||
return time.strftime(format, time.localtime())
|
||
|
||
|
||
# 处理MySQL配置文件
|
||
def CheckMyCnf():
|
||
import os
|
||
confFile = '/etc/my.cnf'
|
||
if os.path.exists(confFile):
|
||
conf = readFile(confFile)
|
||
if conf.find('[mysqld]') != -1: return True
|
||
versionFile = get_setup_path() + '/mysql/version.pl'
|
||
if not os.path.exists(versionFile): return False
|
||
|
||
versions = ['5.1','5.5','5.6','5.7','8.0','AliSQL']
|
||
version = readFile(versionFile)
|
||
for key in versions:
|
||
if key in version:
|
||
version = key
|
||
break
|
||
|
||
shellStr = '''
|
||
#!/bin/bash
|
||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||
export PATH
|
||
|
||
CF='node.aapanel.com'
|
||
HK='download.bt.cn'
|
||
HK2='103.224.251.67'
|
||
US='128.1.164.196'
|
||
sleep 0.5;
|
||
CN_PING=`ping -c 1 -w 1 $CF|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||
|
||
echo "$HK_PING $HK" > ping.pl
|
||
echo "$HK2_PING $HK2" >> ping.pl
|
||
echo "$US_PING $US" >> ping.pl
|
||
echo "$CF_PING $CF" >> ping.pl
|
||
nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'`
|
||
if [ "$nodeAddr" == "" ];then
|
||
nodeAddr=$CF
|
||
fi
|
||
|
||
Download_Url=http://$nodeAddr:5880
|
||
|
||
|
||
MySQL_Opt()
|
||
{
|
||
MemTotal=`free -m | grep Mem | awk '{print $2}'`
|
||
if [[ ${MemTotal} -gt 1024 && ${MemTotal} -lt 2048 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 128#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 768K#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 768K#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 8M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 16#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 32M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 2048 && ${MemTotal} -lt 4096 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 256#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 1M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 1M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 32#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 64M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 4096 && ${MemTotal} -lt 8192 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 512#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 2M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 2M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 32M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 64#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 128M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 8192 && ${MemTotal} -lt 16384 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 1024#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 4M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 4M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 64M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 128#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 1024M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 256M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 16384 && ${MemTotal} -lt 32768 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 2048#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 8M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 8M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 128M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 256#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 2048M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 512M#" /etc/my.cnf
|
||
elif [[ ${MemTotal} -ge 32768 ]]; then
|
||
sed -i "s#^key_buffer_size.*#key_buffer_size = 1024M#" /etc/my.cnf
|
||
sed -i "s#^table_open_cache.*#table_open_cache = 4096#" /etc/my.cnf
|
||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^read_buffer_size.*#read_buffer_size = 16M#" /etc/my.cnf
|
||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 256M#" /etc/my.cnf
|
||
sed -i "s#^thread_cache_size.*#thread_cache_size = 512#" /etc/my.cnf
|
||
sed -i "s#^query_cache_size.*#query_cache_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^tmp_table_size.*#tmp_table_size = 512M#" /etc/my.cnf
|
||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 4096M#" /etc/my.cnf
|
||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 1024M#" /etc/my.cnf
|
||
fi
|
||
}
|
||
|
||
wget -O /etc/my.cnf $Download_Url/install/conf/mysql-%s.conf -T 5
|
||
chmod 644 /etc/my.cnf
|
||
MySQL_Opt
|
||
''' % (version,)
|
||
ExecShell(shellStr)
|
||
#判断是否迁移目录
|
||
if os.path.exists('data/datadir.pl'):
|
||
newPath = readFile('data/datadir.pl')
|
||
if os.path.exists(newPath):
|
||
mycnf = readFile('/etc/my.cnf')
|
||
mycnf = mycnf.replace('/www/server/data',newPath)
|
||
writeFile('/etc/my.cnf',mycnf)
|
||
WriteLog('TYPE_SOFE', 'MYSQL_CHECK_ERR')
|
||
return True
|
||
|
||
|
||
def GetSSHPort():
|
||
try:
|
||
file = '/etc/ssh/sshd_config'
|
||
conf = ReadFile(file)
|
||
rep = "#*Port\s+([0-9]+)\s*\n"
|
||
port = re.search(rep, conf).groups(0)[0]
|
||
return int(port)
|
||
except:
|
||
return 22
|
||
|
||
def GetSSHStatus():
|
||
if os.path.exists('/usr/bin/apt-get'):
|
||
status = ExecShell("service ssh status | grep -P '(dead|stop)'")
|
||
else:
|
||
import system
|
||
panelsys = system.system()
|
||
version = panelsys.GetSystemVersion()
|
||
if version.find(' 7.') != -1:
|
||
status = ExecShell("systemctl status sshd.service | grep 'dead'")
|
||
else:
|
||
status = ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'")
|
||
if len(status[0]) > 3:
|
||
status = False
|
||
else:
|
||
status = True
|
||
return status
|
||
|
||
#检查端口是否合法
|
||
def CheckPort(port,other=None):
|
||
if type(port) == str: port = int(port)
|
||
if port < 1 or port > 65535: return False
|
||
if other:
|
||
checks = [22, 20, 21, 8888, 3306, 11211, 888, 25]
|
||
if port in checks: return False
|
||
return True
|
||
|
||
#获取Token
|
||
def GetToken():
|
||
try:
|
||
from json import loads
|
||
tokenFile = 'data/token.json'
|
||
if not os.path.exists(tokenFile): return False
|
||
token = loads(readFile(tokenFile))
|
||
return token
|
||
except:
|
||
return False
|
||
|
||
def to_btint(string):
|
||
m_list = []
|
||
for s in string:
|
||
m_list.append(ord(s))
|
||
return m_list
|
||
|
||
def load_module(pluginCode):
|
||
from imp import new_module
|
||
from BTPanel import cache
|
||
p_tk = 'data/%s' % md5(pluginCode + get_uuid())
|
||
pluginInfo = None
|
||
if cache: pluginInfo = cache.get(pluginCode + 'code')
|
||
if not pluginInfo:
|
||
import panelAuth
|
||
pdata = panelAuth.panelAuth().create_serverid(None)
|
||
pdata['pid'] = pluginCode
|
||
url = GetConfigValue('home') + '/api/panel/get_py_module'
|
||
pluginTmp = httpPost(url, pdata)
|
||
try:
|
||
pluginInfo = json.loads(pluginTmp)
|
||
except:
|
||
if not os.path.exists(p_tk): return False
|
||
pluginInfo = json.loads(ReadFile(p_tk))
|
||
if pluginInfo['status'] == False: return False
|
||
WriteFile(p_tk, json.dumps(pluginInfo))
|
||
os.chmod(p_tk, 384)
|
||
if cache: cache.set(pluginCode + 'code', pluginInfo, 1800)
|
||
|
||
mod = sys.modules.setdefault(pluginCode, new_module(pluginCode))
|
||
code = compile(pluginInfo['msg'].encode('utf-8'), pluginCode, 'exec')
|
||
mod.__file__ = pluginCode
|
||
mod.__package__ = ''
|
||
exec (code, mod.__dict__)
|
||
return mod
|
||
|
||
#解密数据
|
||
def auth_decode(data):
|
||
token = GetToken()
|
||
#是否有生成Token
|
||
if not token: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#校验access_key是否正确
|
||
if token['access_key'] != data['btauth_key']: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#解码数据
|
||
import binascii,hashlib,urllib,hmac,json
|
||
tdata = binascii.unhexlify(data['data'])
|
||
|
||
#校验signature是否正确
|
||
signature = binascii.hexlify(hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest())
|
||
if signature != data['signature']: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#返回
|
||
return json.loads(urllib.unquote(tdata))
|
||
|
||
|
||
#数据加密
|
||
def auth_encode(data):
|
||
token = GetToken()
|
||
pdata = {}
|
||
|
||
#是否有生成Token
|
||
if not token: return returnMsg(False,'REQUEST_ERR')
|
||
|
||
#生成signature
|
||
import binascii,hashlib,urllib,hmac,json
|
||
tdata = urllib.quote(json.dumps(data))
|
||
#公式 hex(hmac_sha256(data))
|
||
pdata['signature'] = binascii.hexlify(hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest())
|
||
|
||
#加密数据
|
||
pdata['btauth_key'] = token['access_key']
|
||
pdata['data'] = binascii.hexlify(tdata)
|
||
pdata['timestamp'] = time.time()
|
||
|
||
#返回
|
||
return pdata
|
||
|
||
#检查Token
|
||
def checkToken(get):
|
||
tempFile = 'data/tempToken.json'
|
||
if not os.path.exists(tempFile): return False
|
||
import json, time
|
||
tempToken = json.loads(readFile(tempFile))
|
||
if time.time() > tempToken['timeout']: return False
|
||
if get.token != tempToken['token']: return False
|
||
return True
|
||
|
||
#获取识别码
|
||
def get_uuid():
|
||
import uuid
|
||
return uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||
|
||
#取计算机名
|
||
def get_hostname():
|
||
import socket
|
||
return socket.gethostname()
|
||
|
||
|
||
#取mysql datadir
|
||
def get_datadir():
|
||
mycnf_file = '/etc/my.cnf'
|
||
if not os.path.exists(mycnf_file): return ''
|
||
mycnf = readFile(mycnf_file)
|
||
import re
|
||
tmp = re.findall(r"datadir\s*=\s*(.+)",mycnf)
|
||
if not tmp: return ''
|
||
return tmp[0]
|
||
|
||
|
||
#进程是否存在
|
||
def process_exists(pname,exe = None,cmdline = None):
|
||
try:
|
||
import psutil
|
||
pids = psutil.pids()
|
||
for pid in pids:
|
||
try:
|
||
p = psutil.Process(pid)
|
||
if p.name() == pname:
|
||
if not exe and not cmdline:
|
||
return True
|
||
else:
|
||
if exe:
|
||
if p.exe() == exe: return True
|
||
if cmdline:
|
||
if cmdline in p.cmdline(): return True
|
||
except:
|
||
pass
|
||
return False
|
||
except: return True
|
||
|
||
#pid是否存在
|
||
def pid_exists(pid):
|
||
if os.path.exists('/proc/{}/exe'.format(pid)):
|
||
return True
|
||
return False
|
||
|
||
|
||
# 重启面板
|
||
def restart_panel():
|
||
import system
|
||
return system.system().ReWeb(None)
|
||
|
||
#获取mac
|
||
def get_mac_address():
|
||
import uuid
|
||
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
|
||
|
||
|
||
# 转码
|
||
def to_string(lites):
|
||
if type(lites) != list: lites = [lites]
|
||
m_str = ''
|
||
for mu in lites:
|
||
if sys.version_info[0] == 2:
|
||
m_str += unichr(mu).encode('utf-8')
|
||
else:
|
||
m_str += chr(mu)
|
||
return m_str
|
||
|
||
#解码
|
||
def to_ord(string):
|
||
o = []
|
||
for s in string:
|
||
o.append(ord(s))
|
||
return o
|
||
|
||
#xss 防御
|
||
def xssencode(text):
|
||
import cgi
|
||
list = ['`', '~', '&', '#', '/', '*', '$', '@', '<', '>', '\"', '\'', ';', '%', ',', '.', '\\u']
|
||
ret = []
|
||
for i in text:
|
||
if i in list:
|
||
i = ''
|
||
ret.append(i)
|
||
str_convert = ''.join(ret)
|
||
text2 = cgi.escape(str_convert, quote=True)
|
||
return text2
|
||
|
||
#xss 防御
|
||
def xssencode2(text):
|
||
import cgi
|
||
text2 = cgi.escape(text, quote=True)
|
||
return text2
|
||
|
||
# 取缓存
|
||
def cache_get(key):
|
||
from BTPanel import cache
|
||
return cache.get(key)
|
||
|
||
# 设置缓存
|
||
def cache_set(key, value, timeout=None):
|
||
from BTPanel import cache
|
||
return cache.set(key, value, timeout)
|
||
|
||
# 删除缓存
|
||
def cache_remove(key):
|
||
from BTPanel import cache
|
||
return cache.delete(key)
|
||
|
||
# 取session值
|
||
def sess_get(key):
|
||
from BTPanel import session
|
||
if key in session: return session[key]
|
||
return None
|
||
|
||
# 设置或修改session值
|
||
def sess_set(key, value):
|
||
from BTPanel import session
|
||
session[key] = value
|
||
return True
|
||
|
||
# 删除指定session值
|
||
def sess_remove(key):
|
||
from BTPanel import session
|
||
if key in session: del (session[key])
|
||
return True
|
||
|
||
# 构造分页
|
||
def get_page(count, p=1, rows=12, callback='', result='1,2,3,4,5,8'):
|
||
import page
|
||
from BTPanel import request
|
||
page = page.Page();
|
||
info = {'count': count, 'row': rows, 'p': p, 'return_js': callback, 'uri': request.full_path}
|
||
data = {'page': page.GetPage(info, result), 'shift': str(page.SHIFT), 'row': str(page.ROW)}
|
||
return data
|
||
|
||
# 取面板版本
|
||
def version():
|
||
try:
|
||
comm = ReadFile('{}/common.py'.format(get_class_path()))
|
||
return re.search("g\.version\s*=\s*'(\d+\.\d+\.\d+)'",comm).groups()[0]
|
||
except:
|
||
return get_panel_version()
|
||
|
||
def get_panel_version():
|
||
comm = ReadFile('{}/common.py'.format(get_class_path()))
|
||
s_key = 'g.version = '
|
||
s_len = len(s_key)
|
||
s_leff = comm.find(s_key) + s_len
|
||
version = comm[s_leff:s_leff+10].strip().strip("'")
|
||
return version
|
||
|
||
|
||
def get_os_version():
|
||
'''
|
||
@name 取操作系统版本
|
||
@author hwliang<2021-08-07>
|
||
@return string
|
||
'''
|
||
version = readFile('/etc/redhat-release')
|
||
if not version:
|
||
version = readFile('/etc/issue').strip().split("\n")[0].replace('\\n','').replace('\l','').strip()
|
||
else:
|
||
version = version.replace('release ','').replace('Linux','').replace('(Core)','').strip()
|
||
v_info = sys.version_info
|
||
try:
|
||
version = "{} {}(Py{}.{}.{})".format(version,os.uname().machine,v_info.major,v_info.minor,v_info.micro)
|
||
except:
|
||
version = "{} (Py{}.{}.{})".format(version,v_info.major,v_info.minor,v_info.micro)
|
||
return version
|
||
|
||
#取文件或目录大小
|
||
def get_path_size(path, exclude=[]):
|
||
"""根据排除目录获取路径的总大小
|
||
|
||
:path 目标路径
|
||
:exclude 排除路径单个字符串或者多个列表。匹配路径是基于path的相对路径,规则是
|
||
tar命令的--exclude规则的子集。
|
||
"""
|
||
import fnmatch
|
||
if not os.path.exists(path): return 0
|
||
if os.path.isfile(path): return os.path.getsize(path)
|
||
if type(exclude) != type([]):
|
||
exclude = [exclude]
|
||
|
||
path = path[0:-1] if path[-1] == "/" else path
|
||
path = os.path.normcase(path)
|
||
# print("path:"+ path)
|
||
# print("exclude:"+ str(exclude))
|
||
_exclude = exclude[0:]
|
||
for i, e in enumerate(_exclude):
|
||
if not e.startswith(path):
|
||
basename = os.path.basename(path)
|
||
if not e.startswith(basename):
|
||
exclude.append(os.path.join(path, e))
|
||
else:
|
||
new_exc = e.replace(basename+"/", "")
|
||
new_exc = os.path.join(path, new_exc)
|
||
exclude.append(new_exc)
|
||
|
||
# print(exclude)
|
||
total_size = 0
|
||
count = 0
|
||
for root, dirs, files in os.walk(path, topdown=True):
|
||
# filter path
|
||
for exc in exclude:
|
||
for d in dirs:
|
||
sub_dir = os.path.normcase(root+os.path.sep+d)
|
||
if fnmatch.fnmatch(sub_dir, exc) or d==exc:
|
||
# print("排除目录:"+sub_dir)
|
||
dirs.remove(d)
|
||
count += 1
|
||
for f in files:
|
||
to_exclude = False
|
||
count += 1
|
||
filename = os.path.normcase(root+os.path.sep+f)
|
||
if not os.path.exists(filename): continue
|
||
if os.path.islink(filename): continue
|
||
# filter file
|
||
norm_filename = os.path.normcase(filename)
|
||
for fexc in exclude:
|
||
if fnmatch.fnmatch(norm_filename, fexc) or fexc==f:
|
||
to_exclude = True
|
||
# print("排除文件:"+norm_filename)
|
||
break
|
||
if to_exclude:
|
||
continue
|
||
total_size += os.path.getsize(filename)
|
||
return total_size
|
||
|
||
#写关键请求日志
|
||
def write_request_log(reques = None):
|
||
try:
|
||
from BTPanel import request,g,session
|
||
if session.get('debug') == 1: return
|
||
if request.path in ['/service_status','/favicon.ico','/task','/system','/ajax','/control','/data','/ssl']:
|
||
return False
|
||
|
||
log_path = '{}/logs/request'.format(get_panel_path())
|
||
log_file = getDate(format='%Y-%m-%d') + '.json'
|
||
if not os.path.exists(log_path): os.makedirs(log_path)
|
||
|
||
log_data = []
|
||
log_data.append(getDate())
|
||
log_data.append(GetClientIp() + ':' + str(request.environ.get('REMOTE_PORT')))
|
||
log_data.append(request.method)
|
||
log_data.append(request.full_path)
|
||
log_data.append(request.headers.get('User-Agent'))
|
||
if request.method == 'POST':
|
||
args = str(request.form.to_dict())
|
||
if len(args) < 2048 and args.find('pass') == -1 and args.find('user') == -1:
|
||
log_data.append(args)
|
||
else:
|
||
log_data.append('{}')
|
||
else:
|
||
log_data.append('{}')
|
||
log_data.append(int((time.time() - g.request_time) * 1000))
|
||
WriteFile(log_path + '/' + log_file,json.dumps(log_data) + "\n",'a+')
|
||
rep_sys_path()
|
||
except: pass
|
||
|
||
# 重载模块
|
||
def mod_reload(mode):
|
||
if not mode: return False
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
reload(mode)
|
||
else:
|
||
import imp
|
||
imp.reload(mode)
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
|
||
# 设置权限
|
||
def set_mode(filename, mode):
|
||
if not os.path.exists(filename): return False
|
||
mode = int(str(mode), 8)
|
||
os.chmod(filename, mode)
|
||
return True
|
||
|
||
|
||
# 设置用户组
|
||
def set_own(filename, user, group=None):
|
||
if not os.path.exists(filename): return False
|
||
from pwd import getpwnam
|
||
try:
|
||
user_info = getpwnam(user)
|
||
user = user_info.pw_uid
|
||
if group:
|
||
user_info = getpwnam(group)
|
||
group = user_info.pw_gid
|
||
except:
|
||
# 如果指定用户或组不存在,则使用www
|
||
user_info = getpwnam('www')
|
||
user = user_info.pw_uid
|
||
group = user_info.pw_gid
|
||
os.chown(filename, user, group)
|
||
return True
|
||
|
||
#校验路径安全
|
||
def path_safe_check(path,force=True):
|
||
if len(path) > 256: return False
|
||
checks = ['..','./','\\','%','$','^','&','*','~','"',"'",';','|','{','}','`']
|
||
for c in checks:
|
||
if path.find(c) != -1: return False
|
||
if force:
|
||
rep = r"^[\w\s\.\/-]+$"
|
||
if not re.match(rep,path): return False
|
||
return True
|
||
|
||
#取数据库字符集
|
||
def get_database_character(db_name):
|
||
try:
|
||
import panelMysql
|
||
tmp = panelMysql.panelMysql().query("show create database `%s`" % db_name.strip())
|
||
c_type = str(re.findall(r"SET\s+([\w\d-]+)\s",tmp[0][1])[0])
|
||
c_types = ['utf8','utf-8','gbk','big5','utf8mb4']
|
||
if not c_type.lower() in c_types: return 'utf8'
|
||
return c_type
|
||
except:
|
||
return 'utf8'
|
||
|
||
def get_database_codestr(codeing):
|
||
wheres = {
|
||
'utf8' : 'utf8_general_ci',
|
||
'utf8mb4' : 'utf8mb4_general_ci',
|
||
'gbk' : 'gbk_chinese_ci',
|
||
'big5' : 'big5_chinese_ci'
|
||
}
|
||
return wheres[codeing]
|
||
|
||
|
||
def get_database_size():
|
||
"""
|
||
@获取数据库大小
|
||
"""
|
||
data = {}
|
||
try:
|
||
import panelMysql
|
||
tables = panelMysql.panelMysql().query("select table_schema, (sum(DATA_LENGTH)+sum(INDEX_LENGTH)) as data from information_schema.TABLES group by table_schema")
|
||
if type(tables) == list:
|
||
for x in tables:
|
||
if len(x) < 2:continue
|
||
if x[1] == None:continue
|
||
data[x[0]] = int(x[1])
|
||
except: return data
|
||
return data
|
||
|
||
def en_punycode(domain):
|
||
if sys.version_info[0] == 2:
|
||
domain = domain.encode('utf8')
|
||
tmp = domain.split('.')
|
||
newdomain = ''
|
||
for dkey in tmp:
|
||
if dkey == '*': continue
|
||
#匹配非ascii字符
|
||
match = re.search(u"[\x80-\xff]+",dkey)
|
||
if not match: match = re.search(u"[\u4e00-\u9fa5]+",dkey)
|
||
if not match:
|
||
newdomain += dkey + '.'
|
||
else:
|
||
if sys.version_info[0] == 2:
|
||
newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.'
|
||
else:
|
||
newdomain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.'
|
||
if tmp[0] == '*': newdomain = "*." + newdomain
|
||
return newdomain[0:-1]
|
||
|
||
|
||
|
||
#punycode 转中文
|
||
def de_punycode(domain):
|
||
tmp = domain.split('.')
|
||
newdomain = ''
|
||
for dkey in tmp:
|
||
if dkey.find('xn--') >=0:
|
||
newdomain += dkey.replace('xn--','').encode('utf-8').decode('punycode') + '.'
|
||
else:
|
||
newdomain += dkey + '.'
|
||
return newdomain[0:-1]
|
||
|
||
#取计划任务文件路径
|
||
def get_cron_path():
|
||
u_file = '/var/spool/cron/crontabs/root'
|
||
if not os.path.exists(u_file):
|
||
file = '/var/spool/cron/root'
|
||
else:
|
||
file = u_file
|
||
return file
|
||
|
||
#加密字符串
|
||
def en_crypt(key,strings):
|
||
try:
|
||
if type(strings) != bytes: strings = strings.encode('utf-8')
|
||
from cryptography.fernet import Fernet
|
||
f = Fernet(key)
|
||
result = f.encrypt(strings)
|
||
return result.decode('utf-8')
|
||
except:
|
||
#print(get_error_info())
|
||
return strings
|
||
|
||
#解密字符串
|
||
def de_crypt(key,strings):
|
||
try:
|
||
if type(strings) != bytes: strings = strings.decode('utf-8')
|
||
from cryptography.fernet import Fernet
|
||
f = Fernet(key)
|
||
result = f.decrypt(strings).decode('utf-8')
|
||
return result
|
||
except:
|
||
#print(get_error_info())
|
||
return strings
|
||
|
||
|
||
#获取IP限制列表
|
||
def get_limit_ip():
|
||
iplong_list = []
|
||
try:
|
||
ip_file = 'data/limitip.conf'
|
||
if not os.path.exists(ip_file): return iplong_list
|
||
|
||
from BTPanel import cache
|
||
ikey = 'limit_ip'
|
||
iplong_list = cache.get(ikey)
|
||
if iplong_list: return iplong_list
|
||
|
||
iplong_list = []
|
||
iplist = ReadFile(ip_file)
|
||
if not iplist:return iplong_list
|
||
iplist = iplist.strip()
|
||
for limit_ip in iplist.split(','):
|
||
if not limit_ip: continue
|
||
limit_ip = limit_ip.split('-')
|
||
iplong = {}
|
||
iplong['min'] = ip2long(limit_ip[0])
|
||
if len(limit_ip) > 1:
|
||
iplong['max'] = ip2long(limit_ip[1])
|
||
else:
|
||
iplong['max'] = iplong['min']
|
||
iplong_list.append(iplong)
|
||
|
||
cache.set(ikey,iplong_list,3600)
|
||
except:pass
|
||
return iplong_list
|
||
|
||
|
||
|
||
|
||
#检查IP白名单
|
||
def check_ip_panel():
|
||
iplong_list = get_limit_ip()
|
||
if not iplong_list: return False
|
||
client_ip = GetClientIp()
|
||
if client_ip in ['127.0.0.1','localhost','::1']: return False
|
||
client_ip_long = ip2long(client_ip)
|
||
for limit_ip in iplong_list:
|
||
if client_ip_long >= limit_ip['min'] and client_ip_long <= limit_ip['max']:
|
||
return False
|
||
|
||
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'))
|
||
except IndexError:pass
|
||
return errorStr
|
||
|
||
#检查面板域名
|
||
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:
|
||
errorStr = errorStr.format(getMsg('PAGE_ERR_TITLE'),getMsg('PAGE_ERR_DOMAIN_H1'),getMsg('PAGE_ERR_DOMAIN_P1'),getMsg('PAGE_ERR_DOMAIN_P2'),getMsg('PAGE_ERR_DOMAIN_P3'),getMsg('NAME'),getMsg('PAGE_ERR_HELP'))
|
||
except:pass
|
||
return errorStr
|
||
return False
|
||
|
||
#是否离线模式
|
||
def is_local():
|
||
s_file = '{}/data/not_network.pl'.format(get_panel_path())
|
||
return os.path.exists(s_file)
|
||
|
||
|
||
#自动备份面板数据
|
||
def auto_backup_panel():
|
||
try:
|
||
panel_paeh = get_panel_path()
|
||
paths = panel_paeh + '/data/not_auto_backup.pl'
|
||
if os.path.exists(paths): return False
|
||
b_path = '{}/panel'.format(get_backup_path())
|
||
backup_path = b_path + '/' + format_date('%Y-%m-%d')
|
||
if os.path.exists(backup_path): return True
|
||
if os.path.getsize(panel_paeh + '/data/default.db') > 104857600 * 2: return False
|
||
os.makedirs(backup_path,384)
|
||
import shutil
|
||
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(path=b_path))
|
||
time_now = time.time() - (86400 * 15)
|
||
for f in os.listdir(b_path):
|
||
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
|
||
|
||
|
||
|
||
|
||
#检查端口状态
|
||
def check_port_stat(port,localIP = '127.0.0.1'):
|
||
import socket
|
||
temp = {}
|
||
temp['port'] = port
|
||
temp['local'] = True
|
||
try:
|
||
s = socket.socket()
|
||
s.settimeout(0.15)
|
||
s.connect((localIP,port))
|
||
s.close()
|
||
except:
|
||
temp['local'] = False
|
||
|
||
result = 0
|
||
if temp['local']: result +=2
|
||
return result
|
||
|
||
|
||
#同步时间
|
||
def sync_date():
|
||
tip_file = "/dev/shm/last_sync_time.pl"
|
||
s_time = int(time.time())
|
||
try:
|
||
if os.path.exists(tip_file):
|
||
if s_time - int(readFile(tip_file)) < 60: return False
|
||
os.remove(tip_file)
|
||
time_str = HttpGet(GetConfigValue('home') + '/api/index/get_time')
|
||
new_time = int(time_str)
|
||
time_arr = time.localtime(new_time)
|
||
date_str = time.strftime("%Y-%m-%d %H:%M:%S", time_arr)
|
||
ExecShell('date -s "%s"' % date_str)
|
||
writeFile(tip_file,str(s_time))
|
||
return True
|
||
except:
|
||
if os.path.exists(tip_file): os.remove(tip_file)
|
||
return False
|
||
|
||
|
||
#重载模块
|
||
def reload_mod(mod_name = None):
|
||
#是否重载指定模块
|
||
modules = []
|
||
if mod_name:
|
||
if type(mod_name) == str:
|
||
mod_names = mod_name.split(',')
|
||
|
||
for mod_name in mod_names:
|
||
if mod_name in sys.modules:
|
||
print(mod_name)
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
reload(sys.modules[mod_name])
|
||
else:
|
||
importlib.reload(sys.modules[mod_name])
|
||
modules.append([mod_name,True])
|
||
except:
|
||
modules.append([mod_name,False])
|
||
else:
|
||
modules.append([mod_name,False])
|
||
return modules
|
||
|
||
#重载所有模块
|
||
for mod_name in sys.modules.keys():
|
||
if mod_name in ['BTPanel']: continue
|
||
f = getattr(sys.modules[mod_name],'__file__',None)
|
||
if f:
|
||
try:
|
||
if f.find('panel/') == -1: continue
|
||
if sys.version_info[0] == 2:
|
||
reload(sys.modules[mod_name])
|
||
else:
|
||
importlib.reload(sys.modules[mod_name])
|
||
modules.append([mod_name,True])
|
||
except:
|
||
modules.append([mod_name,False])
|
||
return modules
|
||
|
||
|
||
def de_hexb(data):
|
||
if sys.version_info[0] != 2:
|
||
if type(data) == str: data = data.encode('utf-8')
|
||
pdata = base64.b64encode(data)
|
||
if sys.version_info[0] != 2:
|
||
if type(pdata) == str: pdata = pdata.encode('utf-8')
|
||
return binascii.hexlify(pdata)
|
||
|
||
def en_hexb(data):
|
||
if sys.version_info[0] != 2:
|
||
if type(data) == str: data = data.encode('utf-8')
|
||
result = base64.b64decode(binascii.unhexlify(data))
|
||
if type(result) != str: result = result.decode('utf-8')
|
||
return result
|
||
|
||
# def upload_file_url(filename):
|
||
# try:
|
||
# if os.path.exists(filename):
|
||
# data = ExecShell('/usr/bin/curl https://scanner.baidu.com/enqueue -F archive=@%s' % filename)
|
||
# data = json.loads(data[0])
|
||
# time.sleep(1)
|
||
# import requests
|
||
# default_headers = {
|
||
# 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
|
||
# }
|
||
# data_list = requests.get(url=data['url'], headers=default_headers, verify=False)
|
||
# return (data_list.json())
|
||
# else:
|
||
# return False
|
||
# except:
|
||
# return False
|
||
|
||
#直接请求到PHP-FPM
|
||
#version php版本
|
||
#uri 请求uri
|
||
#filename 要执行的php文件
|
||
#args 请求参数
|
||
#method 请求方式
|
||
def request_php(version,uri,document_root,method='GET',pdata=b''):
|
||
import panelPHP
|
||
if type(pdata) == dict: pdata = url_encode(pdata)
|
||
fpm_address = get_fpm_address(version)
|
||
p = panelPHP.FPM(fpm_address,document_root)
|
||
result = p.load_url_public(uri,pdata,method)
|
||
return result
|
||
|
||
|
||
def get_fpm_address(php_version,bind=False):
|
||
'''
|
||
@name 获取FPM请求地址
|
||
@author hwliang<2020-10-23>
|
||
@param php_version string PHP版本
|
||
@return tuple or string
|
||
'''
|
||
fpm_address = '/tmp/php-cgi-{}.sock'.format(php_version)
|
||
php_fpm_file = '{}/php/{}/etc/php-fpm.conf'.format(get_setup_path(),php_version)
|
||
try:
|
||
fpm_conf = readFile(php_fpm_file)
|
||
tmp = re.findall(r"listen\s*=\s*(.+)",fpm_conf)
|
||
if not tmp: return fpm_address
|
||
if tmp[0].find('sock') != -1: return fpm_address
|
||
if tmp[0].find(':') != -1:
|
||
listen_tmp = tmp[0].split(':')
|
||
if bind:
|
||
fpm_address = (listen_tmp[0],int(listen_tmp[1]))
|
||
else:
|
||
fpm_address = ('127.0.0.1',int(listen_tmp[1]))
|
||
else:
|
||
fpm_address = ('127.0.0.1',int(tmp[0]))
|
||
return fpm_address
|
||
except:
|
||
return fpm_address
|
||
|
||
|
||
def get_php_proxy(php_version,webserver = 'nginx'):
|
||
'''
|
||
@name 获取PHP代理地址
|
||
@author hwliang<2020-10-24>
|
||
@param php_version string php版本 (52|53|54|55|56|70|71|72|73|74)
|
||
@param webserver string web服务器类型 (nginx|apache|ols)
|
||
return string
|
||
'''
|
||
php_address = get_fpm_address(php_version)
|
||
if isinstance(php_address,str):
|
||
if webserver == 'nginx':
|
||
return 'unix:{}'.format(php_address)
|
||
elif webserver == 'apache':
|
||
return 'unix:{}|fcgi://localhost'.format(php_address)
|
||
else:
|
||
if webserver == 'nginx':
|
||
return '{}:{}'.format(php_address[0],php_address[1])
|
||
elif webserver == 'apache':
|
||
return 'fcgi://{}:{}'.format(php_address[0],php_address[1])
|
||
|
||
def get_php_version_conf(conf):
|
||
'''
|
||
@name 从指定配置文件获取PHP版本
|
||
@author hwliang<2020-10-24>
|
||
@param conf string 配置文件内容
|
||
@return string
|
||
'''
|
||
if not conf: return '00'
|
||
if conf.find('enable-php-') != -1:
|
||
rep = r"enable-php-(\w{2,5})\.conf"
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp: return '00'
|
||
elif conf.find('/usr/local/lsws/lsphp') != -1:
|
||
rep = r"path\s*/usr/local/lsws/lsphp(\d+)/bin/lsphp"
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp: return '00'
|
||
else:
|
||
rep = r"php-cgi-([0-9]{2,3})\.sock"
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp:
|
||
rep = r'127.0.0.1:10(\d{2,2})1'
|
||
tmp = re.findall(rep,conf)
|
||
if not tmp:
|
||
return '00'
|
||
return tmp[0]
|
||
|
||
|
||
def get_site_php_version(siteName):
|
||
'''
|
||
@name 获取指定网站当前使用的PHP版本
|
||
@author hwliang<2020-10-24>
|
||
@param siteName string 网站名称
|
||
@return string
|
||
'''
|
||
web_server = get_webserver()
|
||
vhost_path = get_vhost_path()
|
||
conf = readFile(vhost_path + '/'+web_server+'/'+siteName+'.conf')
|
||
if web_server == 'openlitespeed':
|
||
conf = readFile(vhost_path + '/' + web_server + '/detail/' + siteName + '.conf')
|
||
return get_php_version_conf(conf)
|
||
|
||
|
||
def check_tcp(ip,port):
|
||
'''
|
||
@name 使用TCP的方式检测指定IP:端口是否能连接
|
||
@author hwliang<2021-06-01>
|
||
@param ip<string> IP地址
|
||
@param port<int> 端口
|
||
@return bool
|
||
'''
|
||
import socket
|
||
try:
|
||
s = socket.socket()
|
||
s.settimeout(5)
|
||
s.connect((ip.strip(),int(port)))
|
||
s.close()
|
||
except:
|
||
return False
|
||
return True
|
||
|
||
|
||
def sub_php_address(conf_file,rep,tsub,php_version):
|
||
'''
|
||
@name 替换新的PHP配置到配置文件
|
||
@author hwliang<2020-10-24>
|
||
@param conf_file string 配置文件全路径
|
||
@param rep string 用于查找目标替换内容的正则表达式
|
||
@param tsub string 新的内容
|
||
@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
|
||
#if conf.find('#PHP') == -1 and conf.find('pathinfo.conf') == -1: return False
|
||
phpv = get_php_version_conf(conf)
|
||
if phpv != php_version: return False
|
||
tmp = re.search(rep,conf)
|
||
if not tmp: return False
|
||
if tmp.group() == tsub: return False
|
||
conf = conf.replace(tmp.group(),tsub) #re.sub(rep,php_proxy,conf)
|
||
writeFile(conf_file,conf)
|
||
return True
|
||
|
||
|
||
def sync_all_address():
|
||
'''
|
||
@name 同步所有PHP版本配置到配置文件
|
||
@author hwliang<2020-10-24>
|
||
@return void
|
||
'''
|
||
php_versions = ['52','53','54','55','56','70','71','72','73','74','75','80','81']
|
||
for phpv in php_versions:
|
||
sync_php_address(phpv)
|
||
|
||
def sync_php_address(php_version):
|
||
'''
|
||
@name 同步PHP版本配置到所有配置文件
|
||
@author hwliang<2020-10-24>
|
||
@param php_version string PHP版本
|
||
@return void
|
||
'''
|
||
if not os.path.exists('{}/php/{}/bin/php'.format(get_setup_path(),php_version)): # 指定PHP版本是否安装
|
||
return False
|
||
ngx_rep = r"(unix:/tmp/php-cgi.*\.sock|127.0.0.1:\d+)"
|
||
apa_rep = r"(unix:/tmp/php-cgi.*\.sock\|fcgi://localhost|fcgi://127.0.0.1:\d+)"
|
||
ngx_proxy = get_php_proxy(php_version,'nginx')
|
||
apa_proxy = get_php_proxy(php_version,'apache')
|
||
is_write = False
|
||
|
||
#nginx的PHP配置文件
|
||
nginx_conf_path = '{}/nginx/conf'.format(get_setup_path())
|
||
|
||
if os.path.exists(nginx_conf_path):
|
||
for f_name in os.listdir(nginx_conf_path):
|
||
if f_name.find('enable-php') != -1:
|
||
conf_file = '/'.join((nginx_conf_path,f_name))
|
||
if sub_php_address(conf_file,ngx_rep,ngx_proxy,php_version):
|
||
is_write = True
|
||
#nginx的phpmyadmin
|
||
# conf_file = '/www/server/nginx/conf/nginx.conf'
|
||
# if os.path.exists(conf_file):
|
||
# if sub_php_address(conf_file,ngx_rep,ngx_proxy,php_version):
|
||
# is_write = True
|
||
|
||
#apache的网站配置文件
|
||
apache_conf_path = '{}/apache'.format(get_vhost_path())
|
||
if os.path.exists(apache_conf_path):
|
||
for f_name in os.listdir(apache_conf_path):
|
||
conf_file = '/'.join((apache_conf_path,f_name))
|
||
if sub_php_address(conf_file,apa_rep,apa_proxy,php_version):
|
||
is_write = True
|
||
#apache的phpmyadmin
|
||
conf_file = '{}/apache/conf/extra/httpd-vhosts.conf'.format(get_setup_path())
|
||
if os.path.exists(conf_file):
|
||
if sub_php_address(conf_file,apa_rep,apa_proxy,php_version):
|
||
is_write = True
|
||
|
||
if is_write: serviceReload()
|
||
return True
|
||
|
||
|
||
|
||
|
||
def url_encode(data):
|
||
if type(data) == str: return data
|
||
import urllib
|
||
if sys.version_info[0] != 2:
|
||
pdata = urllib.parse.urlencode(data).encode('utf-8')
|
||
else:
|
||
pdata = urllib.urlencode(data)
|
||
return pdata
|
||
|
||
def url_decode(data):
|
||
if type(data) == str: return data
|
||
import urllib
|
||
if sys.version_info[0] != 2:
|
||
pdata = urllib.parse.urldecode(data).encode('utf-8')
|
||
else:
|
||
pdata = urllib.urldecode(data)
|
||
return pdata
|
||
|
||
|
||
def unicode_encode(data):
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
result = unicode(data,errors='ignore')
|
||
else:
|
||
result = data.encode('utf8',errors='ignore')
|
||
return result
|
||
except: return data
|
||
|
||
def unicode_decode(data,charset = 'utf8'):
|
||
try:
|
||
if sys.version_info[0] == 2:
|
||
result = unicode(data,errors='ignore')
|
||
else:
|
||
result = data.decode('utf8',errors='ignore')
|
||
return result
|
||
except: return data
|
||
|
||
def import_cdn_plugin():
|
||
plugin_path = 'plugin/static_cdn'
|
||
if not os.path.exists(plugin_path): return True
|
||
try:
|
||
import static_cdn_main
|
||
except:
|
||
package_path_append(plugin_path)
|
||
import static_cdn_main
|
||
|
||
|
||
def get_cdn_hosts():
|
||
try:
|
||
if import_cdn_plugin(): return []
|
||
import static_cdn_main
|
||
return static_cdn_main.static_cdn_main().get_hosts(None)
|
||
except:
|
||
return []
|
||
|
||
def get_cdn_url():
|
||
try:
|
||
if os.path.exists('plugin/static_cdn/not_open.pl'):
|
||
return False
|
||
from BTPanel import cache
|
||
cdn_url = cache.get('cdn_url')
|
||
if cdn_url: return cdn_url
|
||
if import_cdn_plugin(): return False
|
||
import static_cdn_main
|
||
cdn_url = static_cdn_main.static_cdn_main().get_url(None)
|
||
cache.set('cdn_url',cdn_url,3)
|
||
return cdn_url
|
||
except:
|
||
return False
|
||
|
||
def set_cdn_url(cdn_url):
|
||
if not cdn_url: return False
|
||
import_cdn_plugin()
|
||
get = dict_obj()
|
||
get.cdn_url = cdn_url
|
||
import static_cdn_main
|
||
static_cdn_main.static_cdn_main().set_url(get)
|
||
return True
|
||
|
||
def get_python_bin():
|
||
bin_file = '{}/pyenv/bin/python'.format(get_panel_path())
|
||
if os.path.exists(bin_file):
|
||
return bin_file
|
||
return '/usr/bin/python'
|
||
|
||
def get_pip_bin():
|
||
bin_file = '{}/pyenv/bin/pip'.format(get_panel_path())
|
||
if os.path.exists(bin_file):
|
||
return bin_file
|
||
return '/usr/bin/pip'
|
||
|
||
def aes_encrypt(data,key):
|
||
import panelAes
|
||
if sys.version_info[0] == 2:
|
||
aes_obj = panelAes.aescrypt_py2(key)
|
||
return aes_obj.aesencrypt(data)
|
||
else:
|
||
aes_obj = panelAes.aescrypt_py3(key)
|
||
return aes_obj.aesencrypt(data)
|
||
|
||
def aes_decrypt(data,key):
|
||
import panelAes
|
||
if sys.version_info[0] == 2:
|
||
aes_obj = panelAes.aescrypt_py2(key)
|
||
return aes_obj.aesdecrypt(data)
|
||
else:
|
||
aes_obj = panelAes.aescrypt_py3(key)
|
||
return aes_obj.aesdecrypt(data)
|
||
|
||
#清理大日志文件
|
||
def clean_max_log(log_file,max_size = 100,old_line = 100):
|
||
if not os.path.exists(log_file): return False
|
||
max_size = 1024 * 1024 * max_size
|
||
if os.path.getsize(log_file) > max_size:
|
||
try:
|
||
old_body = GetNumLines(log_file,old_line)
|
||
writeFile(log_file,old_body)
|
||
except:
|
||
print(get_error_info())
|
||
|
||
#获取证书哈希
|
||
def get_cert_data(path):
|
||
import panelSSL
|
||
get = dict_obj()
|
||
get.certPath = path
|
||
data = panelSSL.panelSSL().GetCertName(get)
|
||
return data
|
||
|
||
# 获取系统发行版
|
||
def get_linux_distribution():
|
||
distribution = 'ubuntu'
|
||
redhat_file = '/etc/redhat-release'
|
||
if os.path.exists(redhat_file):
|
||
try:
|
||
tmp = readFile(redhat_file).split()[3][0]
|
||
distribution = 'centos{}'.format(tmp)
|
||
except:
|
||
distribution = 'centos7'
|
||
return distribution
|
||
|
||
def long2ip(ips):
|
||
'''
|
||
@name 将整数转换为IP地址
|
||
@author hwliang<2020-06-11>
|
||
@param ips string(ip地址整数)
|
||
@return ipv4
|
||
'''
|
||
i1 = int(ips / (2 ** 24))
|
||
i2 = int((ips - i1 * ( 2 ** 24 )) / ( 2 ** 16 ))
|
||
i3 = int(((ips - i1 * ( 2 ** 24 )) - i2 * ( 2 ** 16 )) / ( 2 ** 8))
|
||
i4 = int(((ips - i1 * ( 2 ** 24 )) - i2 * ( 2 ** 16 )) - i3 * ( 2 ** 8))
|
||
return "{}.{}.{}.{}".format(i1,i2,i3,i4)
|
||
|
||
def ip2long(ip):
|
||
'''
|
||
@name 将IP地址转换为整数
|
||
@author hwliang<2020-06-11>
|
||
@param ip string(ipv4)
|
||
@return long
|
||
'''
|
||
ips = ip.split('.')
|
||
if len(ips) != 4: return 0
|
||
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
|
||
return GetClientIp() +':'+ str(request.environ.get('REMOTE_PORT')) + '|' + str(int(time.time())) + '|' + get_error_info()
|
||
|
||
#获取sessionid
|
||
def get_session_id():
|
||
from BTPanel import request,app
|
||
session_id = request.cookies.get(app.config['SESSION_COOKIE_NAME'],'')
|
||
if not re.findall(r"^([\w\.-]{64,64})$",session_id):
|
||
return GetRandomString(64)
|
||
return session_id
|
||
|
||
#尝试自动恢复面板数据库
|
||
def rep_default_db():
|
||
db_path = '{}/data/'.format(get_panel_path())
|
||
db_file = db_path + 'default.db'
|
||
db_tmp_backup = db_path + 'default_' + format_date("%Y%m%d_%H%M%S") + ".db"
|
||
|
||
panel_backup = '{}/panel'.format(get_backup_path())
|
||
bak_list = os.listdir(panel_backup)
|
||
if not bak_list: return False
|
||
bak_list = sorted(bak_list,reverse=True)
|
||
db_bak_file = ''
|
||
for d_name in bak_list:
|
||
db_bak_file = panel_backup + '/' + d_name + '/data/default.db'
|
||
if not os.path.exists(db_bak_file): continue
|
||
if os.path.getsize(db_bak_file) < 17408: continue
|
||
break
|
||
|
||
if not db_bak_file: return False
|
||
ExecShell("\cp -arf {} {}".format(db_file,db_tmp_backup))
|
||
ExecShell("\cp -arf {} {}".format(db_bak_file,db_file))
|
||
return True
|
||
|
||
|
||
|
||
def chdck_salt():
|
||
'''
|
||
@name 检查所有用户密码是否加盐,若没有则自动加上
|
||
@author hwliang<2020-07-08>
|
||
@return void
|
||
'''
|
||
|
||
if not M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'users','%salt%')).count():
|
||
M('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
|
||
u_list = M('users').where('salt is NULL',()).field('id,username,password,salt').select()
|
||
if isinstance(u_list,str):
|
||
if u_list.find('no such table: users') != -1:
|
||
rep_default_db()
|
||
if not M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'users','%salt%')).count():
|
||
M('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
|
||
u_list = M('users').where('salt is NULL',()).field('id,username,password,salt').select()
|
||
|
||
for u_info in u_list:
|
||
salt = GetRandomString(12) #12位随机
|
||
pdata = {}
|
||
pdata['password'] = md5(md5(u_info['password']+'_bt.cn') + salt)
|
||
pdata['salt'] = salt
|
||
M('users').where('id=?',(u_info['id'],)).update(pdata)
|
||
|
||
|
||
def get_login_token():
|
||
token_s = readFile('{}/data/login_token.pl'.format(get_panel_path()))
|
||
if not token_s: return GetRandomString(32)
|
||
return token_s
|
||
|
||
def get_sess_key():
|
||
from BTPanel import session
|
||
return md5(get_login_token() + session.get('request_token_head',''))
|
||
|
||
|
||
def password_salt(password,username=None,uid=None):
|
||
'''
|
||
@name 为指定密码加盐
|
||
@author hwliang<2020-07-08>
|
||
@param password string(被md5加密一次的密码)
|
||
@param username string(用户名) 可选
|
||
@param uid int(uid) 可选
|
||
@return string
|
||
'''
|
||
chdck_salt()
|
||
if not uid:
|
||
if not username:
|
||
raise Exception('username或uid必需传一项')
|
||
uid = M('users').where('username=?',(username,)).getField('id')
|
||
salt = M('users').where('id=?',(uid,)).getField('salt')
|
||
return md5(md5(password+'_bt.cn')+salt)
|
||
|
||
# 备份配置文件
|
||
def back_file(file, act=None):
|
||
"""
|
||
@name 备份配置文件
|
||
@author zhwen<zhw@bt.cn>
|
||
@param file 需要备份的文件
|
||
@param act 如果存在,则备份一份作为默认配置
|
||
"""
|
||
file_type = "_bak"
|
||
if act:
|
||
file_type = "_def"
|
||
ExecShell("/usr/bin/cp -p {0} {1}".format(file, file + file_type))
|
||
|
||
# 还原配置文件
|
||
def restore_file(file, act=None):
|
||
"""
|
||
@name 还原配置文件
|
||
@author zhwen<zhw@bt.cn>
|
||
@param file 需要还原的文件
|
||
@param act 如果存在,则还原默认配置
|
||
"""
|
||
file_type = "_bak"
|
||
if act:
|
||
file_type = "_def"
|
||
ExecShell("/usr/bin/cp -p {1} {0}".format(file, file + file_type))
|
||
|
||
|
||
def package_path_append(path):
|
||
if not path in sys.path:
|
||
sys.path.insert(0, path)
|
||
|
||
|
||
def rep_sys_path():
|
||
sys_path = []
|
||
for p in sys.path:
|
||
if p in sys_path: continue
|
||
sys_path.append(p)
|
||
sys.path = sys_path
|
||
|
||
|
||
def get_ssh_port():
|
||
'''
|
||
@name 获取本机SSH端口
|
||
@author hwliang<2020-08-07>
|
||
@return int
|
||
'''
|
||
s_file = '/etc/ssh/sshd_config'
|
||
conf = readFile(s_file)
|
||
if not conf: conf = ''
|
||
port_all = re.findall(r".*Port\s+[0-9]+",conf)
|
||
ssh_port = 22
|
||
for p in port_all:
|
||
rep = r"^\s*Port\s+([0-9]+)\s*"
|
||
tmp1 = re.findall(rep,p)
|
||
if tmp1:
|
||
ssh_port = int(tmp1[0])
|
||
|
||
return ssh_port
|
||
|
||
def set_error_num(key,empty = False,expire=3600):
|
||
'''
|
||
@name 设置失败次数(每调用一次+1)
|
||
@author hwliang<2020-08-21>
|
||
@param key<string> 索引
|
||
@param empty<bool> 是否清空计数
|
||
@param expire<int> 计数器生命周期(秒)
|
||
@return bool
|
||
'''
|
||
from BTPanel import cache
|
||
key = md5(key)
|
||
num = cache.get(key)
|
||
if not num:
|
||
num = 0
|
||
else:
|
||
if empty:
|
||
cache.delete(key)
|
||
return True
|
||
cache.set(key,num + 1,expire)
|
||
return True
|
||
|
||
def get_error_num(key,limit=False):
|
||
'''
|
||
@name 获取失败次数
|
||
@author hwliang<2020-08-21>
|
||
@param key<string> 索引
|
||
@param limit<False or int> 如果为False,则直接返回失败次数,否则与失败次数比较,若大于失败次数返回True,否则返回False
|
||
@return int or bool
|
||
'''
|
||
from BTPanel import cache
|
||
key = md5(key)
|
||
num = cache.get(key)
|
||
if not num: num = 0
|
||
if not limit:
|
||
return num
|
||
if limit > num:
|
||
return True
|
||
return False
|
||
|
||
|
||
def get_menus():
|
||
'''
|
||
@name 获取菜单列表
|
||
@author hwliang<2020-08-31>
|
||
@return list
|
||
'''
|
||
from BTPanel import session
|
||
data = json.loads(ReadFile('config/menu.json'))
|
||
hide_menu = ReadFile('config/hide_menu.json')
|
||
debug = session.get('debug')
|
||
if hide_menu:
|
||
hide_menu = json.loads(hide_menu)
|
||
show_menu = []
|
||
for i in range(len(data)):
|
||
if data[i]['id'] in hide_menu: continue
|
||
if data[i]['id'] == "memuAxterm":
|
||
if debug: continue
|
||
show_menu.append(data[i])
|
||
data = show_menu
|
||
del(hide_menu)
|
||
del(show_menu)
|
||
menus = sorted(data, key=lambda x: x['sort'])
|
||
return menus
|
||
|
||
|
||
#取CURL路径
|
||
def get_curl_bin():
|
||
'''
|
||
@name 取CURL执行路径
|
||
@author hwliang<2020-09-01>
|
||
@return string
|
||
'''
|
||
c_bin = ['/usr/local/curl2/bin/curl','/usr/local/curl/bin/curl','/usr/bin/curl']
|
||
for cb in c_bin:
|
||
if os.path.exists(cb): return cb
|
||
return 'curl'
|
||
|
||
|
||
#设置防跨站配置
|
||
def set_open_basedir():
|
||
try:
|
||
fastcgi_file = '{}/nginx/conf/fastcgi.conf'.format(get_setup_path())
|
||
|
||
if os.path.exists(fastcgi_file):
|
||
fastcgi_body = readFile(fastcgi_file)
|
||
if fastcgi_body.find('bt_safe_dir') == -1:
|
||
fastcgi_body = fastcgi_body + "\n"+'fastcgi_param PHP_ADMIN_VALUE "$bt_safe_dir=$bt_safe_open";'
|
||
writeFile(fastcgi_file,fastcgi_body)
|
||
|
||
proxy_file = '{}/nginx/conf/proxy.conf'.format(get_setup_path())
|
||
if os.path.exists(proxy_file):
|
||
proxy_body = readFile(proxy_file)
|
||
if proxy_body.find('bt_safe_dir') == -1:
|
||
proxy_body = proxy_body + "\n"+'''map "baota_dir" $bt_safe_dir {
|
||
default "baota_dir";
|
||
}
|
||
map "baota_open" $bt_safe_open {
|
||
default "baota_open";
|
||
} '''
|
||
writeFile(proxy_file,proxy_body)
|
||
|
||
open_basedir_path = '{}/open_basedir/nginx'.format(get_vhost_path())
|
||
if not os.path.exists(open_basedir_path):
|
||
os.makedirs(open_basedir_path,384)
|
||
|
||
site_list = M('sites').field('id,name,path').select()
|
||
for site_info in site_list:
|
||
set_site_open_basedir_nginx(site_info['name'])
|
||
except: return
|
||
|
||
|
||
#处理指定站点的防跨站配置 for Nginx
|
||
def set_site_open_basedir_nginx(siteName):
|
||
try:
|
||
return
|
||
open_basedir_path = '/www/server/panel/vhost/open_basedir/nginx'
|
||
if not os.path.exists(open_basedir_path):
|
||
os.makedirs(open_basedir_path,384)
|
||
config_file = '/www/server/panel/vhost/nginx/{}.conf'.format(siteName)
|
||
open_basedir_file = "/".join(
|
||
(open_basedir_path,'{}.conf'.format(siteName))
|
||
)
|
||
if not os.path.exists(config_file): return
|
||
if not os.path.exists(open_basedir_file):
|
||
writeFile(open_basedir_file,'')
|
||
config_body = readFile(config_file)
|
||
if config_body.find(open_basedir_path) == -1:
|
||
config_body = config_body.replace("include enable-php","include {};\n\t\tinclude enable-php".format(open_basedir_file))
|
||
writeFile(config_file,config_body)
|
||
|
||
root_path = re.findall(r"root\s+(.+);",config_body)[0]
|
||
if not root_path: return
|
||
userini_file = root_path + '/.user.ini'
|
||
if not os.path.exists(userini_file):
|
||
writeFile(open_basedir_file,'')
|
||
return
|
||
userini_body = readFile(userini_file)
|
||
if not userini_body: return
|
||
if userini_body.find('open_basedir') == -1:
|
||
writeFile(open_basedir_file,'')
|
||
return
|
||
|
||
open_basedir_conf = re.findall("open_basedir=(.+)",userini_body)
|
||
if not open_basedir_conf: return
|
||
open_basedir_conf = open_basedir_conf[0]
|
||
open_basedir_body = '''set $bt_safe_dir "open_basedir";
|
||
set $bt_safe_open "{}";'''.format(open_basedir_conf)
|
||
writeFile(open_basedir_file,open_basedir_body)
|
||
except: return
|
||
|
||
|
||
def run_thread(fun,args = (),daemon=False):
|
||
'''
|
||
@name 使用线程执行指定方法
|
||
@author hwliang<2020-10-27>
|
||
@param fun {def} 函数对像
|
||
@param args {tuple} 参数元组
|
||
@param daemon {bool} 是否守护线程
|
||
@return bool
|
||
'''
|
||
import threading
|
||
p = threading.Thread(target=fun,args=args)
|
||
p.setDaemon(daemon)
|
||
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 = '{}/data/check_domain/'.format(get_panel_path())
|
||
if not os.path.exists(check_domain_path):
|
||
os.makedirs(check_domain_path,384)
|
||
pdata = get_user_info()
|
||
pdata['domain'] = domain
|
||
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 get_mac_address():
|
||
import uuid
|
||
mac=uuid.UUID(int = uuid.getnode()).hex[-12:]
|
||
return ":".join([mac[e:e+2] for e in range(0,11,2)])
|
||
|
||
def get_user_info():
|
||
user_file = '{}/data/userInfo.json'.format(get_panel_path())
|
||
if not os.path.exists(user_file): return {}
|
||
userInfo = {}
|
||
try:
|
||
userTmp = json.loads(readFile(user_file))
|
||
userInfo['uid'] = userTmp['id']
|
||
userInfo['username'] = userTmp['username']
|
||
userInfo['serverid'] = userTmp['serverid']
|
||
userInfo['oem'] = get_oem_name()
|
||
userInfo['o'] = userInfo['oem']
|
||
except: pass
|
||
return userInfo
|
||
|
||
|
||
|
||
|
||
def send_file(data,fname='',mimetype = ''):
|
||
'''
|
||
@name 以文件流的形式返回
|
||
@author heliang<2020-10-27>
|
||
@param data {bytes|string} 文件数据或路径
|
||
@param mimetype {string} 文件类型
|
||
@param fname {string} 文件名
|
||
@return Response
|
||
'''
|
||
d_type = type(data)
|
||
from io import BytesIO,StringIO
|
||
from flask import send_file as send_to
|
||
if d_type == bytes:
|
||
fp = BytesIO(data)
|
||
else:
|
||
if len(data) < 128:
|
||
if os.path.exists(data):
|
||
fp = data
|
||
if not fname:
|
||
fname = os.path.basename(fname)
|
||
else:
|
||
fp = StringIO(data)
|
||
else:
|
||
fp = StringIO(data)
|
||
|
||
if not mimetype: mimetype = "application/octet-stream"
|
||
if not fname: fname = 'doan.txt'
|
||
|
||
return send_to(fp,
|
||
mimetype=mimetype,
|
||
as_attachment=True,
|
||
add_etags=True,
|
||
conditional=True,
|
||
attachment_filename=fname,
|
||
cache_timeout=0)
|
||
|
||
def gen_password(length=8,chars=string.ascii_letters+string.digits):
|
||
return ''.join([choice(chars) for i in range(length)])
|
||
|
||
def get_ipaddress():
|
||
'''
|
||
@name 获取本机IP地址
|
||
@author hwliang<2020-11-24>
|
||
@return list
|
||
'''
|
||
ipa_tmp = ExecShell("ip a |grep inet|grep -v inet6|grep -v 127.0.0.1|grep -v 'inet 192.168.'|grep -v 'inet 10.'|awk '{print $2}'|sed 's#/[0-9]*##g'")[0].strip()
|
||
iplist = ipa_tmp.split('\n')
|
||
return iplist
|
||
|
||
def get_oem_name():
|
||
'''
|
||
@name 获取OEM名称
|
||
@author hwliang<2021-03-24>
|
||
@return string
|
||
'''
|
||
oem = ''
|
||
oem_file = '{}/data/o.pl'.format(get_panel_path())
|
||
if os.path.exists(oem_file):
|
||
oem = readFile(oem_file)
|
||
if oem: oem = oem.strip()
|
||
return oem
|
||
|
||
def get_pdata():
|
||
'''
|
||
@name 构造POST基础参数
|
||
@author hwliang<2021-03-24>
|
||
@return dict
|
||
'''
|
||
import panelAuth
|
||
pdata = panelAuth.panelAuth().create_serverid(None)
|
||
pdata['oem'] = get_oem_name()
|
||
return pdata
|
||
|
||
|
||
# 名称输入系列化
|
||
def xssdecode(text):
|
||
try:
|
||
cs = {""":'"',"'":"'"}
|
||
for c in cs.keys():
|
||
text = text.replace(c,cs[c])
|
||
|
||
str_convert = text
|
||
if sys.version_info[0] == 3:
|
||
import html
|
||
text2 = html.unescape(str_convert)
|
||
else:
|
||
text2 = cgi.unescape(str_convert)
|
||
return text2
|
||
except:
|
||
return text
|
||
|
||
def fetch_disk_SN():
|
||
r,e = ExecShell("fdisk -l |grep 'Disk identifier' |awk {'print $3'}")
|
||
if r:
|
||
return r.split('\n')[0]
|
||
|
||
def fetch_cpu_ID():
|
||
r, e = ExecShell("cat /proc/cpuinfo|grep microcode|awk -F':' '{print $2}'")
|
||
if r:
|
||
return r.split('\n')[0]
|
||
|
||
def get_hostname():
|
||
import socket
|
||
return socket.getfqdn(socket.gethostname())
|
||
|
||
def get_platform():
|
||
import platform
|
||
return platform.platform()
|
||
|
||
def get_memory():
|
||
import psutil
|
||
return psutil.virtual_memory().total
|
||
|
||
def fetch_env_info():
|
||
import panelAuth
|
||
userInfo = panelAuth.panelAuth().create_serverid(None)
|
||
try:
|
||
return {'ip': GetLocalIp(),
|
||
'is_ipv6': 0,
|
||
'os': get_platform(),
|
||
'mac': get_mac_address(),
|
||
'hdid': fetch_disk_SN(),
|
||
'ramid': get_memory(),
|
||
'cpuid': fetch_cpu_ID(),
|
||
'server_name': get_hostname(),
|
||
'install_code': userInfo['server_id']
|
||
}
|
||
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:
|
||
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
|
||
def exists(self,keys):
|
||
return exists_args(keys,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 format in ['port']:
|
||
result_port = int(result)
|
||
if result_port > 65535 or result_port < 0:
|
||
raise ValueError("Parameters: {}, the required port number is 0-65535".format(key))
|
||
result = result_port
|
||
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
|
||
|
||
#实例化定目录下的所有模块
|
||
class get_modules:
|
||
|
||
def __contains__(self, key):
|
||
return self.get_attr(key)
|
||
|
||
def __setitem__(self, key, value):
|
||
setattr(self,key,value)
|
||
|
||
def get_attr(self,key):
|
||
'''
|
||
尝试获取模块,若为字符串,则尝试实例化模块,否则直接返回模块对像
|
||
'''
|
||
res = getattr(self,key)
|
||
if isinstance(res,str):
|
||
try:
|
||
tmp_obj = __import__(key)
|
||
reload(tmp_obj)
|
||
setattr(self,key,tmp_obj)
|
||
return tmp_obj
|
||
except:
|
||
raise Exception(get_error_info())
|
||
return res
|
||
|
||
def __getitem__(self, key):
|
||
return self.get_attr(key)
|
||
|
||
def __delitem__(self,key):
|
||
delattr(self,key)
|
||
|
||
def __delattr__(self, key):
|
||
delattr(self,key)
|
||
|
||
def get_items(self):
|
||
return self
|
||
|
||
def __init__(self,path = "class",limit = None):
|
||
'''
|
||
@name 加载指定目录下的模块
|
||
@author hwliang<2020-08-03>
|
||
@param path<string> 指定目录,可指定绝对目录,也可指定相对于/www/server/panel的相对目录 默认加载class目录
|
||
@param limit<string/list/tuple> 指定限定加载的模块名称,默认加载path目录下的所有模块
|
||
@param object
|
||
|
||
@example
|
||
p = get_modules('class')
|
||
if 'public' in p:
|
||
md5_str = p.public.md5('test')
|
||
md5_str = p['public'].md5('test')
|
||
md5_str = getattr(p['public'],'md5')('test')
|
||
else:
|
||
print(p.__dict__)
|
||
'''
|
||
os.chdir(get_panel_path())
|
||
exp_files = ['__init__.py','__pycache__']
|
||
if not path in sys.path:
|
||
sys.path.insert(0,path)
|
||
for fname in os.listdir(path):
|
||
if fname in exp_files: continue
|
||
filename = '/'.join([path,fname])
|
||
if os.path.isfile(filename):
|
||
if not fname[-3:] in ['.py','.so']: continue
|
||
mod_name = fname[:-3]
|
||
else:
|
||
c_file = '/'.join((filename,'__init__.py'))
|
||
if not os.path.exists(c_file):
|
||
continue
|
||
mod_name = fname
|
||
|
||
if limit:
|
||
if not isinstance(limit,list) and not isinstance(limit,tuple):
|
||
limit = (limit,)
|
||
if not mod_name in limit:
|
||
continue
|
||
|
||
setattr(self,mod_name,mod_name)
|
||
|
||
#检查App和小程序的绑定
|
||
def check_app(check='app'):
|
||
path=get_panel_path() + '/'
|
||
if check=='app':
|
||
try:
|
||
if not os.path.exists(path+'data/user.json') and os.path.exists(path+'config/api.json') and not os.path.exists(path+'plugin/app/user.json'):return False
|
||
if os.path.exists(path+'plugin/app/user.json'):
|
||
wxapp = json.loads(readFile(path+'plugin/app/user.json'))
|
||
if wxapp:return True
|
||
if os.path.exists(path+'data/user.json'):
|
||
app_info = json.loads(readFile(path+'data/user.json'))
|
||
if app_info:return True
|
||
if os.path.exists(path+'config/api.json'):
|
||
btapp_info = json.loads(readFile(path+'config/api.json'))
|
||
if not btapp_info['open']:return False
|
||
if not 'apps' in btapp_info:return False
|
||
if not btapp_info['apps']:return False
|
||
return True
|
||
return False
|
||
except:
|
||
return False
|
||
elif check=='app_bind':
|
||
if not os.path.exists(path + 'config/api.json'):return False
|
||
btapp_info = json.loads(readFile(path +'config/api.json'))
|
||
if not btapp_info: return False
|
||
if not btapp_info['open']: return False
|
||
return True
|
||
elif check=='wxapp':
|
||
if not os.path.exists(path+'plugin/app/user.json'):return False
|
||
app_info = json.loads(readFile(path+'plugin/app/user.json'))
|
||
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():
|
||
iplist_file = '{}/data/iplist.txt'.format(get_panel_path())
|
||
if os.path.exists(iplist_file):
|
||
data=ReadFile(iplist_file)
|
||
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):
|
||
login_send_mail = "{}/data/login_send_mail.pl".format(get_panel_path())
|
||
send_login_white = '{}/data/send_login_white.json'.format(get_panel_path())
|
||
login_send_dingding = "{}/data/login_send_dingding.pl".format(get_panel_path())
|
||
if os.path.exists(login_send_mail):
|
||
if check_ip_white(send_login_white,login_ip):return False
|
||
send_mail("aapanel login reminder","aapanel login reminder:Your server "+get_ip()+" successfully logged in via "+is_type+", account number: "+username+", login IP: "+login_ip+":"+port+", login time: "+time.strftime('%Y -%m-%d %X',time.localtime()), True)
|
||
if os.path.exists(login_send_dingding):
|
||
if check_ip_white(send_login_white,login_ip):return False
|
||
send_dingding("aapanel login reminder:Your server "+get_ip()+" successfully logged in via "+is_type+", account number: "+username+", login IP: "+login_ip+":"+port+", login time: "+time.strftime('%Y -%m-%d %X',time.localtime())+'\n\n > Login status: <font color=#20a53a>Succeed</font>', True)
|
||
|
||
#普通模式下调用发送消息【设置登陆告警后的设置】
|
||
#title= 发送的title
|
||
#body= 发送的body
|
||
#is_logs= 是否记录日志
|
||
#is_type=发送告警的类型
|
||
def send_to_body(title,body,is_logs=False,is_type="aaPanel email alert"):
|
||
login_send_mail = "{}/data/login_send_mail.pl".format(get_panel_path())
|
||
login_send_dingding = "{}/data/login_send_dingding.pl".format(get_panel_path())
|
||
if os.path.exists(login_send_mail):
|
||
if is_logs:
|
||
send_mail(title, body,True,is_type)
|
||
send_mail(title,body)
|
||
|
||
if os.path.exists(login_send_dingding):
|
||
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
|
||
|
||
|
||
def get_sys_path():
|
||
'''
|
||
@name 关键目录
|
||
@author hwliang<2021-06-11>
|
||
@return tuple
|
||
'''
|
||
a = ['/www','/usr','/','/dev','/home','/media','/mnt','/opt','/tmp','/var']
|
||
c = ['/www/Recycle_bin/','/www/backup/','/www/php_session/','/www/wwwlogs/','/www/server/','/etc/','/usr/','/var/','/boot/','/proc/','/sys/','/tmp/','/root/','/lib/','/bin/','/sbin/','/run/','/lib64/','/lib32/','/srv/']
|
||
return a,c
|
||
|
||
|
||
def check_site_path(site_path):
|
||
'''
|
||
@name 检查网站根目录是否为系统关键目录
|
||
@author hwliang<2021-05-31>
|
||
@param site_path<string> 网站根目录全路径
|
||
@return bool
|
||
'''
|
||
whites = ['/www/server/tomcat','/www/server/stop','/www/server/phpmyadmin']
|
||
for w in whites:
|
||
if site_path.find(w) == 0: return True
|
||
a,error_paths = get_sys_path()
|
||
site_path = site_path.strip()
|
||
if site_path[-1] == '/': site_path = site_path[:-1]
|
||
if site_path in a:
|
||
return False
|
||
site_path += '/'
|
||
for ep in error_paths:
|
||
if site_path.find(ep) == 0: return False
|
||
return True
|
||
|
||
def is_debug():
|
||
debug_file = "{}/data/debug.pl".format(get_panel_path())
|
||
return os.path.exists(debug_file)
|
||
|
||
|
||
class PanelError(Exception):
|
||
'''
|
||
@name 宝塔通用异常对像
|
||
@author hwliang<2021-06-25>
|
||
'''
|
||
def __init__(self, value):
|
||
self.value = value
|
||
|
||
def __str__(self):
|
||
return ("面板运行时发生错误: {}".format(repr(self.value)))
|
||
|
||
def get_sysbit():
|
||
'''
|
||
@name 获取操作系统位数
|
||
@author hwliang<2021-07-07>
|
||
@return int 32 or 64
|
||
'''
|
||
import struct
|
||
return struct.calcsize('P') * 8
|
||
|
||
def get_setup_path():
|
||
'''
|
||
@name 获取安装路径
|
||
@author hwliang<2021-07-22>
|
||
@return string
|
||
'''
|
||
return '/www/server'
|
||
|
||
def get_panel_path():
|
||
'''
|
||
@name 取面板根目录
|
||
@author hwliang<2021-07-14>
|
||
@return string
|
||
'''
|
||
return '{}/panel'.format(get_setup_path())
|
||
|
||
def get_plugin_path(plugin_name = None):
|
||
'''
|
||
@name 取指定插件目录
|
||
@author hwliang<2021-07-14>
|
||
@param plugin_name<string> 插件名称 不传则返回插件根目录
|
||
@return string
|
||
'''
|
||
|
||
root_path = "{}/plugin".format(get_panel_path())
|
||
if not plugin_name: return root_path
|
||
return "{}/{}".format(root_path,plugin_name)
|
||
|
||
def get_class_path():
|
||
'''
|
||
@name 取类库所在路径
|
||
@author hwliang<2021-07-14>
|
||
@return string
|
||
'''
|
||
return "{}/class".format(get_panel_path())
|
||
|
||
def get_logs_path():
|
||
'''
|
||
@name 取日志目录
|
||
@author hwliang<2021-07-14>
|
||
@return string
|
||
'''
|
||
return '/www/wwwlogs'
|
||
|
||
def get_vhost_path():
|
||
'''
|
||
@name 取虚拟主机目录
|
||
@author hwliang<2021-08-14>
|
||
@return string
|
||
'''
|
||
return '{}/vhost'.format(get_panel_path())
|
||
|
||
|
||
def get_backup_path():
|
||
'''
|
||
@name 取备份目录
|
||
@author hwliang<2021-07-14>
|
||
@return string
|
||
'''
|
||
default_backup_path = '/www/backup'
|
||
backup_path = M('config').where("id=?",(1,)).getField('backup_path')
|
||
if not backup_path: return default_backup_path
|
||
if os.path.exists(backup_path): return backup_path
|
||
return default_backup_path
|
||
|
||
def get_site_path():
|
||
'''
|
||
@name 取站点默认存储目录
|
||
@author hwliang<2021-07-14>
|
||
@return string
|
||
'''
|
||
default_site_path = '/www/wwwroot'
|
||
site_path = M('config').where("id=?",(1,)).getField('sites_path')
|
||
if not site_path: return default_site_path
|
||
if os.path.exists(site_path): return site_path
|
||
return default_site_path
|
||
|
||
|
||
def read_config(config_name,ext_name = 'json'):
|
||
'''
|
||
@name 读取指定配置文件
|
||
@author hwliang<2021-07-14>
|
||
@param config_name<string> 配置文件名称(不含扩展名)
|
||
@param ext_name<string> 配置文件扩展名,默认为json
|
||
@return string 如果发生错误,将抛出PanelError异常
|
||
'''
|
||
config_file = "{}/config/{}.{}".format(get_panel_path(),config_name,ext_name)
|
||
if not os.path.exists(config_file):
|
||
raise PanelError('指定配置文件{} 不存在'.format(config_name))
|
||
|
||
config_str = readFile(config_file)
|
||
if ext_name == 'json':
|
||
try:
|
||
config_body = json.loads(config_str)
|
||
except Exception as ex:
|
||
raise PanelError('配置文件不是标准的可解析JSON内容!\n{}'.format(ex))
|
||
return config_body
|
||
return config_str
|
||
|
||
def save_config(config_name,config_body,ext_name = 'json'):
|
||
'''
|
||
@name 保存配置文件
|
||
@author hwliang<2021-07-14>
|
||
@param config_name<string> 配置文件名称(不含扩展名)
|
||
@param config_body<mixed> 被保存的内容, ext_name为json,请传入可解析为json的参数类型,如list,dict,int,str等
|
||
@param ext_name<string> 配置文件扩展名,默认为json
|
||
@return string 如果发生错误,将抛出PanelError异常
|
||
'''
|
||
|
||
config_file = "{}/config/{}.{}".format(get_panel_path(),config_name,ext_name)
|
||
if ext_name == 'json':
|
||
try:
|
||
config_body = json.dumps(config_body)
|
||
except Exception as ex:
|
||
raise PanelError('配置内容无法被转换为json格式!\n{}'.format(ex))
|
||
|
||
return writeFile(config_file,config_body)
|
||
|
||
def get_config_value(config_name,key,default='',ext_name='json'):
|
||
'''
|
||
@name 获取指定配置文件的指定配置项
|
||
@author hwliang<2021-07-14>
|
||
@param config_name<string> 配置文件名称(不含扩展名)
|
||
@param key<string> 配置项
|
||
@param default<mixed> 获不存在则返回的默认值,默认为空字符串
|
||
@param ext_name<string> 配置文件扩展名,默认为json
|
||
@return mixed 如果发生错误,将抛出PanelError异常
|
||
'''
|
||
config_data = read_config(config_name,ext_name)
|
||
return config_data.get(key,default)
|
||
|
||
def set_config_value(config_name,key,value,ext_name='json'):
|
||
'''
|
||
@name 设置指定配置文件的指定配置项
|
||
@author hwliang<2021-07-14>
|
||
@param config_name<string> 配置文件名称(不含扩展名)
|
||
@param key<string> 配置项
|
||
@param value<mixed> 配置值
|
||
@param ext_name<string> 配置文件扩展名,默认为json
|
||
@return mixed 如果发生错误,将抛出PanelError异常
|
||
'''
|
||
config_data = read_config(config_name,ext_name)
|
||
config_data[key] = value
|
||
return save_config(config_name,config_data,ext_name)
|
||
|
||
|
||
def return_data(status,data = {},status_code=None,error_msg = None):
|
||
'''
|
||
@name 格式化响应内容
|
||
@author hwliang<2021-07-14>
|
||
@param status<bool> 状态
|
||
@param data<mixed> 响应数据
|
||
@param status_code<int> 状态码
|
||
@param error_msg<string> 错误消息内容
|
||
@return dict
|
||
|
||
'''
|
||
if status_code is None:
|
||
status_code = 1 if status else 0
|
||
if error_msg is None:
|
||
error_msg = '' if status else '未知错误'
|
||
|
||
result = {
|
||
'status':status,
|
||
"status_code":status_code,
|
||
'error_msg':str(error_msg),
|
||
'data':data
|
||
}
|
||
return result
|
||
|
||
|
||
def return_error(error_msg,status_code = -1,data = []):
|
||
'''
|
||
@name 格式化错误响应内容
|
||
@author hwliang<2021-07-15>
|
||
@param error_msg<string> 错误消息
|
||
@param status_code<int> 状态码,默认为-1
|
||
@param data<mixed> 响应数据
|
||
@return dict
|
||
'''
|
||
return return_data(False,data,status_code,str(error_msg))
|
||
|
||
|
||
def error(error_msg,status_code = -1,data = []):
|
||
'''
|
||
@name 格式化错误响应内容
|
||
@author hwliang<2021-07-15>
|
||
@param error_msg<string> 错误消息
|
||
@param status_code<int> 状态码,默认为-1
|
||
@param data<mixed> 响应数据
|
||
@return dict
|
||
'''
|
||
return return_error(error_msg,status_code,data)
|
||
|
||
def success(data = [],status_code = 1,error_msg = ''):
|
||
'''
|
||
@name 格式化成功响应内容
|
||
@author hwliang<2021-07-15>
|
||
@param data<mixed> 响应数据
|
||
@param status_code<int> 状态码,默认为0
|
||
@return dict
|
||
'''
|
||
return return_data(True,data,status_code,error_msg)
|
||
|
||
|
||
def return_status_code(status_code,format_body,data = []):
|
||
'''
|
||
@name 按状态码返回
|
||
@author hwliang<2021-07-15>
|
||
@param status_code<int> 状态码
|
||
@param format_body<string> 错误内容
|
||
@param data<mixed> 响应数据
|
||
@return dict
|
||
'''
|
||
error_msg = get_config_value('status_code',str(status_code))
|
||
if not error_msg: raise PanelError('指定状态码不存在!')
|
||
return return_data(error_msg[0],data,status_code,error_msg[1].format(format_body))
|
||
|
||
|
||
def to_dict_obj(data):
|
||
'''
|
||
@name 将dict转换为dict_obj
|
||
@author hwliang<2021-07-15>
|
||
@param data<dict> 要被转换的数据
|
||
@return dict_obj
|
||
'''
|
||
if not isinstance(data,dict):
|
||
raise PanelError('错误的数据类型,只支持将dict转换为dict_obj')
|
||
pdata = dict_obj()
|
||
for key in data.keys():
|
||
pdata[key] = data[key]
|
||
return pdata
|
||
|
||
def get_script_object(filename):
|
||
'''
|
||
@name 从脚本文件获取对像
|
||
@author hwliang<2021-07-19>
|
||
@param filename<string> 文件名
|
||
@return object
|
||
'''
|
||
_obj = sys.modules.get(filename,None)
|
||
if _obj: return _obj
|
||
from types import ModuleType
|
||
_obj = sys.modules.setdefault(filename, ModuleType(filename))
|
||
_code = readFile(filename)
|
||
_code_object = compile(_code,filename, 'exec')
|
||
_obj.__file__ = filename
|
||
_obj.__package__ = ''
|
||
exec(_code_object, _obj.__dict__)
|
||
return _obj
|
||
|
||
def check_hooks():
|
||
'''
|
||
@name 自动注册HOOK
|
||
@author hwliang<2021-07-19>
|
||
@return void
|
||
'''
|
||
hooks_path = '{}/hooks'.format(get_panel_path())
|
||
if not os.path.exists(hooks_path):
|
||
return
|
||
for hook_name in os.listdir(hooks_path):
|
||
if hook_name[-3:] != '.py': continue
|
||
filename = os.path.join(hooks_path,hook_name)
|
||
_obj = get_script_object(filename)
|
||
_main = getattr(_obj,'main',None)
|
||
if not _main: continue
|
||
_main()
|
||
|
||
def register_hook(hook_index, hook_def):
|
||
'''
|
||
@name 注册HOOK
|
||
@author hwliang<2021-07-15>
|
||
@param hook_index<string> HOOK位置
|
||
@param hook_def<def> HOOK函数对像
|
||
@return void
|
||
'''
|
||
from BTPanel import hooks
|
||
hook_keys = hooks.keys()
|
||
if not hook_index in hook_keys:
|
||
hooks[hook_index] = []
|
||
if not hook_def in hooks[hook_index]:
|
||
hooks[hook_index].append(hook_def)
|
||
|
||
def exec_hook(hook_index, data):
|
||
'''
|
||
@name 执行HOOk
|
||
@author hwliang<2021-07-15>
|
||
@param hook_index<string> HOOK索引位置,格式限制:^\w+$
|
||
@param data<mixed> 运行数据
|
||
@return mixed
|
||
'''
|
||
|
||
from BTPanel import hooks
|
||
hook_keys = hooks.keys()
|
||
if not hook_index in hook_keys:
|
||
return data
|
||
|
||
for hook_def in hooks[hook_index]:
|
||
data = hook_def(data)
|
||
return data
|
||
|
||
def get_hook_index(mod_name, def_name):
|
||
'''
|
||
@name 获取HOOK位置
|
||
@author hwliang<2021-07-19>
|
||
@param mod_name<string> 模块名称
|
||
@param def_name<string> 方法名称
|
||
@return tuple
|
||
'''
|
||
mod_name = mod_name.upper()
|
||
def_name = def_name.upper()
|
||
last_index = '{}_{}_LAST'.format(mod_name, def_name)
|
||
end_index = '{}_{}_END'.format(mod_name, def_name)
|
||
return last_index, end_index
|
||
|
||
|
||
def flush_plugin_list():
|
||
'''
|
||
@name 刷新插件列表
|
||
@author hwliang<2021-07-22>
|
||
@return bool
|
||
'''
|
||
skey = 'TNaMJdG3mDHKRS6Y'
|
||
from BTPanel import cache
|
||
from pluginAuth import Plugin
|
||
if cache.get(skey): cache.delete(skey)
|
||
Plugin(False).get_plugin_list(True)
|
||
return True
|
||
|
||
|
||
def get_session_timeout():
|
||
'''
|
||
@name 获取session过期时间
|
||
@author hwliang<2021-07-28>
|
||
@return int
|
||
'''
|
||
from BTPanel import cache
|
||
skey = 'session_timeout'
|
||
session_timeout = cache.get(skey)
|
||
if not session_timeout is None: return session_timeout
|
||
|
||
sess_out_path = '{}/data/session_timeout.pl'.format(get_panel_path())
|
||
session_timeout = 86400
|
||
if not os.path.exists(sess_out_path):
|
||
return session_timeout
|
||
try:
|
||
session_timeout = int(readFile(sess_out_path))
|
||
except:
|
||
session_timeout = 86400
|
||
cache.set(skey,session_timeout,3600)
|
||
return session_timeout
|
||
|
||
|
||
def get_login_token_auth():
|
||
'''
|
||
@name 获取登录token
|
||
@author hwliang<2021-07-28>
|
||
@return string
|
||
'''
|
||
from BTPanel import cache
|
||
skey = 'login_token'
|
||
login_token = cache.get(skey)
|
||
if not login_token is None: return login_token
|
||
|
||
login_token_file = '{}/data/login_token.pl'.format(get_panel_path())
|
||
login_token = '1234567890'
|
||
if not os.path.exists(login_token_file):
|
||
return login_token
|
||
login_token = readFile(login_token_file)
|
||
cache.set(skey,login_token,3600)
|
||
return login_token
|
||
|
||
|
||
def listen_ipv6():
|
||
'''
|
||
@name 是否监听ipv6
|
||
@author hwliang<2021-08-12>
|
||
@return bool
|
||
'''
|
||
ipv6_file = '{}/data/ipv6.pl'.format(get_panel_path())
|
||
return os.path.exists(ipv6_file)
|
||
|
||
def get_panel_log_file():
|
||
'''
|
||
@name 获取panel日志文件
|
||
@author hwliang<2021-08-12>
|
||
@return string
|
||
'''
|
||
return "{}/logs/error.log".format(get_panel_path())
|
||
|
||
|
||
def print_log(_info,_level = 'DEBUG'):
|
||
'''
|
||
@name 写入日志
|
||
@author hwliang<2021-08-12>
|
||
@param _info<string> 要写入到日志文件的信息
|
||
@param _level<string> 日志级别
|
||
@return void
|
||
'''
|
||
log_body = "[{}][{}] - {}\n".format(format_date(),_level.upper(),_info)
|
||
return WriteFile(get_panel_log_file(),log_body,'a+')
|
||
|
||
|
||
|
||
def to_date(format = "%Y-%m-%d %H:%M:%S",times = None):
|
||
'''
|
||
@name 格式时间转时间戳
|
||
@author hwliang<2021-08-17>
|
||
@param format<string> 时间格式
|
||
@param times<date> 时间
|
||
@return int
|
||
'''
|
||
ts = time.strptime(times, "%Y-%m-%d %H:%M:%S")
|
||
return time.mktime(ts)
|
||
|
||
|
||
def get_glibc_version():
|
||
'''
|
||
@name 获取glibc版本
|
||
@author hwliang<2021-08-17>
|
||
@return string
|
||
'''
|
||
try:
|
||
cmd_result = ExecShell("ldd --version")[0]
|
||
if not cmd_result: return ''
|
||
glibc_version = cmd_result.split("\n")[0].split()[-1]
|
||
except:
|
||
return ''
|
||
return glibc_version
|
||
|
||
|
||
def is_apache_nginx():
|
||
'''
|
||
@name 是否是apache或nginx
|
||
@author hwliang<2021-08-17>
|
||
@return bool
|
||
'''
|
||
setup_path = get_setup_path()
|
||
return os.path.exists(setup_path + '/apache') or os.path.exists(setup_path + '/nginx')
|
||
|
||
def error_403(e):
|
||
from BTPanel import Response
|
||
errorStr = '''<html>
|
||
<head><title>403 Forbidden</title></head>
|
||
<body>
|
||
<center><h1>403 Forbidden</h1></center>
|
||
<hr><center>nginx</center>
|
||
</body>
|
||
</html>'''
|
||
headers = {
|
||
"Content-Type": "text/html"
|
||
}
|
||
return Response(errorStr, status=403, headers=headers)
|
||
|
||
def error_404(e):
|
||
from BTPanel import Response
|
||
errorStr = '''<html>
|
||
<head><title>404 Not Found</title></head>
|
||
<body>
|
||
<center><h1>404 Not Found</h1></center>
|
||
<hr><center>nginx</center>
|
||
</body>
|
||
</html>'''
|
||
headers = {
|
||
"Content-Type": "text/html"
|
||
}
|
||
return Response(errorStr, status=404, headers=headers)
|
||
|