# coding: utf-8 # +------------------------------------------------------------------- # | 宝塔Linux面板 # +------------------------------------------------------------------- # | Copyright (c) 2015-2016 宝塔软件(http://bt.cn) All rights reserved. # +------------------------------------------------------------------- # | Author: hwliang # +------------------------------------------------------------------- from datetime import datetime, timezone from flask import session, request, Response import public, os, json, time, apache, psutil from mod.base.json_util import SrcFieldJSONEncoder, SrcField class ajax: def GetApacheStatus(self, get): a = apache.apache() return a.GetApacheStatus() def GetProcessCpuPercent(self, i, process_cpu): try: pp = psutil.Process(i) if pp.name() not in process_cpu.keys(): process_cpu[pp.name()] = float(pp.cpu_percent(interval=0.01)) process_cpu[pp.name()] += float(pp.cpu_percent(interval=0.01)) except: pass def GetNginxStatus(self, get): try: if not os.path.exists('/www/server/nginx/sbin/nginx'): return public.returnMsg(False, '未安装nginx') process_cpu = {"nginx": 0} nginx_pid = public.readFile('/www/server/nginx/logs/nginx.pid') pro_list = [] try: pro = psutil.Process(int(nginx_pid)) pro_list = pro.children() pro_list = [i for i in pro_list if "worker" in ''.join(i.cmdline())] except: pass worker = len(pro_list) workermen = int(sum([int(i.memory_info().rss) for i in pro_list])) / 1024 / 1024 from BTPanel import cache if pro_list: old_ngixn_cpu = cache.get('nginx_cpu') _cpu = 0 for pp in pro_list: c = pp.cpu_times() for s in c: _cpu += s s = psutil.cpu_times() now_all_cpu = 0 for i in s: now_all_cpu += i if old_ngixn_cpu: process_cpu['nginx'] = 100.00 * (_cpu - old_ngixn_cpu['nginx_cpu']) / (now_all_cpu - old_ngixn_cpu['all_cpu']) cache.set('nginx_cpu', {'nginx_cpu': _cpu, 'all_cpu':now_all_cpu}, 600) # 取Nginx负载状态 self.CheckStatusConf() result = public.httpGet('http://127.0.0.1/nginx_status') is_curl = False tmp = [] if result: tmp = result.split() if len(tmp) < 15: is_curl = True if is_curl: result = public.ExecShell( 'curl http://127.0.0.1/nginx_status')[0] tmp = result.split() data = {} if "request_time" in tmp: data['accepts'] = tmp[8] data['handled'] = tmp[9] data['requests'] = tmp[10] data['Reading'] = tmp[13] data['Writing'] = tmp[15] data['Waiting'] = tmp[17] else: data['accepts'] = tmp[8] data['handled'] = tmp[7] data['requests'] = tmp[9] data['Reading'] = tmp[11] data['Writing'] = tmp[13] data['Waiting'] = tmp[15] data['active'] = tmp[2] data['worker'] = worker data['workercpu'] = round(float(process_cpu["nginx"]), 2) data['workermen'] = "%s%s" % (int(workermen), "MB") return data except Exception as ex: public.print_log(public.get_error_info()) public.WriteLog('信息获取', "Nginx负载状态获取失败: %s" % ex) return public.returnMsg(False, '数据获取失败,检查nginx状态是否正常!') def GetPHPStatus(self, get): # 取指定PHP版本的负载状态 try: version = get.version uri = "/phpfpm_" + version + "_status?json" result = public.request_php(version, uri, '') tmp = json.loads(result) fTime = time.localtime(int(tmp['start time'])) tmp['start time'] = time.strftime('%Y-%m-%d %H:%M:%S', fTime) return tmp except Exception as ex: public.WriteLog('信息获取', "PHP负载状态获取失败: {}".format(public.get_error_info())) return public.returnMsg(False, '负载状态获取失败!') def CheckStatusConf(self): if public.get_webserver() != 'nginx': return filename = session[ 'setupPath'] + '/panel/vhost/nginx/phpfpm_status.conf' if os.path.exists(filename): if public.ReadFile(filename).find('nginx_status') != -1: return conf = '''server { listen 80; server_name 127.0.0.1; allow 127.0.0.1; location /nginx_status { stub_status on; access_log off; } }''' public.writeFile(filename, conf) public.serviceReload() def GetTaskCount(self, get): # 取任务数量 return public.M('tasks').where("status!=?", ('1',)).count() """ @name 下载json文件 """ def download_json_files(self, url, path): try: res = json.loads(public.httpGet(url)) if type(res) == dict: public.writeFile(path, json.dumps(res)) except: pass return True def _get_jdk_status(self): # 获取本机系统架构 import platform arce = platform.machine() if arce == 'x86_64': arce = 'x64' elif arce == 'aarch64' or 'arm' in arce: arce = 'arm' if arce not in ['x64', 'arm']: return { "name": "jdk", "check": "/usr/local/java/VERSION/bin/java", "msg": "Java 语言是一种通用的、面向对象的编程语言", "shell": "java.sh", "task": "1", "type": "语言解释器", "versions": [] } default_versions = { "x64": [ "jdk1.7.0_80", "jdk1.8.0_371", "jdk-9.0.4", "jdk-10.0.2", "jdk-11.0.19", "jdk-12.0.2", "jdk-13.0.2", "jdk-14.0.2", "jdk-15.0.2", "jdk-16.0.2", "jdk-17.0.8", "jdk-18.0.2.1", "jdk-19.0.2", "jdk-20.0.2" ], "arm": [ "jdk1.8.0_371", "jdk-11.0.19", "jdk-15.0.2", "jdk-16.0.2", "jdk-17.0.8", "jdk-18.0.2.1", "jdk-19.0.2", "jdk-20.0.2" ] } skey = 'is_down_jdk' tmp_file = '/www/server/panel/data/jdk.json' download_url = '{}/src/jdk/jdk.json'.format(public.get_url()) if not skey in session: public.run_thread(self.download_json_files, (download_url, tmp_file)) session[skey] = True try: versions = json.loads(public.readFile(tmp_file))[arce] except: public.run_thread(self.download_json_files, (download_url, tmp_file)) versions = default_versions[arce] data = { "name": "jdk", "check": "/usr/local/java/VERSION/bin/java", "msg": "Java 语言是一种通用的、面向对象的编程语言", "shell": "java.sh", "task": "1", "type": "语言解释器", "versions": [ {"status": True if os.path.exists('/www/server/java/' + i) else False, "version": i} for i in versions] } return data def GetSoftList(self, get): # 取软件列表 import json, os tmp = public.readFile('data/softList.conf') try: data = json.loads(tmp) except: data = [] tasks = public.M('tasks').where("status!=?", ('1',)).field('status,name').select() for i in range(len(data)): data[i]['check'] = public.GetConfigValue( 'root_path') + '/' + data[i]['check'] for n in range(len(data[i]['versions'])): # 处理任务标记 isTask = '1' for task in tasks: tmp = public.getStrBetween('[', ']', task['name']) if not tmp: continue tmp1 = tmp.split('-') if data[i]['name'] == 'PHP': if tmp1[0].lower() == data[i]['name'].lower( ) and tmp1[1] == data[i]['versions'][n]['version']: isTask = task['status'] else: if tmp1[0].lower() == data[i]['name'].lower(): isTask = task['status'] # 检查安装状态 if data[i]['name'] == 'PHP': data[i]['versions'][n]['task'] = isTask checkFile = data[i]['check'].replace( 'VERSION', data[i]['versions'][n]['version'].replace('.', '')) else: data[i]['task'] = isTask version = public.readFile( public.GetConfigValue('root_path') + '/server/' + data[i]['name'].lower() + '/version.pl') if not version: continue if version.find(data[i]['versions'][n]['version']) == -1: continue checkFile = data[i]['check'] data[i]['versions'][n]['status'] = os.path.exists(checkFile) data.append(self._get_jdk_status()) return data def GetLibList(self, get): # 取插件列表 import json, os tmp = public.readFile('data/libList.conf') data = json.loads(tmp) for i in range(len(data)): data[i]['status'] = self.CheckLibInstall(data[i]['check']) data[i]['optstr'] = self.GetLibOpt(data[i]['status'], data[i]['opt']) return data def CheckLibInstall(self, checks): for cFile in checks: if os.path.exists(cFile): return '已安装' return '未安装' # 取插件操作选项 def GetLibOpt(self, status, libName): optStr = '' if status == '未安装': optStr = '安装' else: libConfig = '配置' if (libName == 'beta'): libConfig = '内测资料' optStr = '' + libConfig + ' | 卸载' return optStr # 取插件AS def GetQiniuAS(self, get): filename = public.GetConfigValue( 'setup_path') + '/panel/data/' + get.name + 'As.conf' if not os.path.exists(filename): public.writeFile(filename, '') data = {} data['AS'] = public.readFile(filename).split('|') data['info'] = self.GetLibInfo(get.name) if len(data['AS']) < 3: data['AS'] = ['', '', '', ''] return data # 设置插件AS def SetQiniuAS(self, get): info = self.GetLibInfo(get.name) filename = public.GetConfigValue( 'setup_path') + '/panel/data/' + get.name + 'As.conf' conf = get.access_key.strip() + '|' + get.secret_key.strip( ) + '|' + get.bucket_name.strip() + '|' + get.bucket_domain.strip() public.writeFile(filename, conf) public.ExecShell("chmod 600 " + filename) result = public.ExecShell(public.get_python_bin() + " " + public.GetConfigValue('setup_path') + "/panel/script/backup_" + get.name + ".py list") if result[0].find("ERROR:") == -1: public.WriteLog("插件管理", "设置插件[" + info['name'] + "]AS!") return public.returnMsg(True, '设置成功!') return public.returnMsg( False, 'ERROR: 无法连接到' + info['name'] + '服务器,请检查[AK/SK/存储空间]设置是否正确!') # 设置内测 def SetBeta(self, get): data = {} data['username'] = get.bbs_name data['qq'] = get.qq data['email'] = get.email result = public.httpPost( public.GetConfigValue('home') + '/Api/LinuxBeta', data) import json data = json.loads(result) if data['status']: public.writeFile('data/beta.pl', get.bbs_name + '|' + get.qq + '|' + get.email) return data # 取内测资格状态 def GetBetaStatus(self, get): try: return public.readFile('data/beta.pl').strip() except: return 'False' # 获取指定插件信息 def GetLibInfo(self, name): import json tmp = public.readFile('data/libList.conf') data = json.loads(tmp) for lib in data: if name == lib['opt']: return lib return False # 获取文件列表 def GetQiniuFileList(self, get): try: import json result = public.ExecShell(public.get_python_bin() + " " + public.GetConfigValue('setup_path') + "/panel/script/backup_" + get.name + ".py list") return json.loads(result[0]) except: return public.returnMsg(False, '获取列表失败,请检查[AK/SK/存储空间]设是否正确!') # 取网络连接列表 def GetNetWorkList(self, get): import psutil netstats = psutil.net_connections() networkList = [] for netstat in netstats: tmp = {} if netstat.type == 1: tmp['type'] = 'tcp' else: tmp['type'] = 'udp' tmp['family'] = netstat.family tmp['laddr'] = netstat.laddr tmp['raddr'] = netstat.raddr tmp['status'] = netstat.status p = psutil.Process(netstat.pid) tmp['process'] = p.name() tmp['pid'] = netstat.pid networkList.append(tmp) del (p) del (tmp) networkList = sorted(networkList, key=lambda x: x['status'], reverse=True) return networkList # 取进程列表 def GetProcessList(self, get): import psutil, pwd Pids = psutil.pids() processList = [] for pid in Pids: try: tmp = {} p = psutil.Process(pid) if p.exe() == "": continue tmp['name'] = p.name() # 进程名称 if self.GoToProcess(tmp['name']): continue tmp['pid'] = pid # 进程标识 tmp['status'] = p.status() # 进程状态 tmp['user'] = p.username() # 执行用户 cputimes = p.cpu_times() tmp['cpu_percent'] = p.cpu_percent(0.1) tmp['cpu_times'] = cputimes.user # 进程占用的CPU时间 tmp['memory_percent'] = round(p.memory_percent(), 3) # 进程占用的内存比例 pio = p.io_counters() tmp['io_write_bytes'] = pio.write_bytes # 进程总共写入字节数 tmp['io_read_bytes'] = pio.read_bytes # 进程总共读取字节数 tmp['threads'] = p.num_threads() # 进程总线程数 processList.append(tmp) del (p) del (tmp) except: continue import operator processList = sorted(processList, key=lambda x: x['memory_percent'], reverse=True) processList = sorted(processList, key=lambda x: x['cpu_times'], reverse=True) return processList # 结束指定进程 def KillProcess(self, get): # return public.returnMsg(False,'演示服务器,禁止此操作!'); import psutil p = psutil.Process(int(get.pid)) name = p.name() if name == 'python': return public.returnMsg(False, 'KILL_PROCESS_ERR') p.kill() public.WriteLog('TYPE_PROCESS', 'KILL_PROCESS', (get.pid, name)) return public.returnMsg(True, 'KILL_PROCESS', (get.pid, name)) def GoToProcess(self, name): ps = [ 'sftp-server', 'login', 'nm-dispatcher', 'irqbalance', 'qmgr', 'wpa_supplicant', 'lvmetad', 'auditd', 'master', 'dbus-daemon', 'tapdisk', 'sshd', 'init', 'ksoftirqd', 'kworker', 'kmpathd', 'kmpath_handlerd', 'python', 'kdmflush', 'bioset', 'crond', 'kthreadd', 'migration', 'rcu_sched', 'kjournald', 'iptables', 'systemd', 'network', 'dhclient', 'systemd-journald', 'NetworkManager', 'systemd-logind', 'systemd-udevd', 'polkitd', 'tuned', 'rsyslogd' ] for key in ps: if key == name: return True return False def GetNetWorkIo(self, get): # 取指定时间段的网络Io try: len_data = (int(get.end) - int(get.start)) / 60 filter_str = "" if len_data > 15000: filter_str = "AND id % 15 = 0" elif len_data > 1500: filter_str = "AND id % 3 = 0" data = public.M('network').dbfile('system').where( "addtime>=? AND addtime<=? {}".format(filter_str), (get.start, get.end) ).field( 'id,up,down,total_up,total_down,down_packets,up_packets,addtime' ).order('id desc').select() for value in data: value['addtime'] = time.strftime('%m/%d %H:%M', time.localtime(float(value['addtime']))) value['down_packets'] = SrcField(value['down_packets']) value['up_packets'] = SrcField(value['up_packets']) resp = Response(json.dumps(data, cls=SrcFieldJSONEncoder), content_type='application/json') return resp except Exception as e: if "no such table" in str(e): import db _sql = db.Sql().dbfile('system') csql = ''' CREATE TABLE IF NOT EXISTS `network` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `up` INTEGER, `down` INTEGER, `total_up` INTEGER, `total_down` INTEGER, `down_packets` INTEGER, `up_packets` INTEGER, `addtime` INTEGER );''' _sql.execute(csql, ()) _sql.close() return [] def GetDiskIo(self, get): # 取指定时间段的磁盘Io tmp_cols = public.M('diskio').dbfile('system').query('PRAGMA table_info(diskio)', ()) fields = [] for col in tmp_cols: if len(col) > 2: fields.append(col[1]) fields.append("disk_top") filter_str = "" len_data = (int(get.end) - int(get.start)) / 60 if len_data > 15000: filter_str = "AND diskio.id % 15 = 0" elif len_data > 1500: filter_str = "AND diskio.id % 3 = 0" data = public.M('diskio').dbfile('system').query( "SELECT diskio.*,process_top_list.disk_top from diskio inner join process_top_list on diskio.addtime=process_top_list.addtime where diskio.addtime>={} AND diskio.addtime<={} {} ORDER BY diskio.addtime desc;" .format(get.start, get.end, filter_str), ()) if isinstance(data, str) and data.find('error: no such table: process_top_list') != -1: return public.M('diskio').dbfile('system').where( "addtime>=? AND addtime<=?", (get.start, get.end) ).order('id asc').select() try: def data_zip(x): x[-1] = SrcField(x[-1]) x[7] = time.strftime('%m/%d %H:%M', time.localtime(float(x[7]))) return dict(zip(fields, x)) data = list(map(data_zip, data)) resp = Response(json.dumps(data, cls=SrcFieldJSONEncoder), content_type='application/json') return resp except: return [] # t3 = time.time() # public.print_log("iter_time:", (t3 - t2) *1000, "ms") # data = self.ToAddtime(data, True, 'disk') # t4 = time.time() # public.print_log("format_time:", (t4 - t3) *1000, "ms") # public.print_log("total_time:", (t4 - t) *1000, "ms") # return data def __format_field(self, field): import re fields = [] for key in field: s_as = re.search(r'\s+as\s+', key, flags=re.IGNORECASE) if s_as: as_tip = s_as.group() key = key.split(as_tip)[1] fields.append(key) return fields def GetCpuIo(self, get): # 取指定时间段的CpuIo panel_path = public.get_panel_path() data_path = "{}/data/sql_index.pl".format(panel_path) if not os.path.exists(data_path): self.create_sql_index() db_obj = public.M('cpuio').dbfile('system') tmp_cols = db_obj.query('PRAGMA table_info(cpuio)', ()) if db_obj.ERR_INFO: return [] fields = [] for col in tmp_cols: if len(col) > 2: fields.append(col[1]) fields.append("cpu_top") fields.append("memory_top") # 抽样减少返回的数据量 filter_str = "" len_data = (int(get.end) - int(get.start)) / 60 if len_data > 15000: filter_str = "AND cpuio.id % 15 = 0" elif len_data > 1500: filter_str = "AND cpuio.id % 3 = 0" data = public.M('cpuio').dbfile('system').query( "SELECT cpuio.*,process_top_list.cpu_top,process_top_list.memory_top from cpuio inner join process_top_list on cpuio.addtime=process_top_list.addtime where cpuio.addtime>={} AND cpuio.addtime<={} {} ORDER BY cpuio.addtime desc;" .format(get.start, get.end, filter_str), ()) if isinstance(data, str) and data.find( 'error: no such table: process_top_list') != -1: return public.M('cpuio').dbfile('system').where( "addtime>=? AND addtime<=?", (get.start, get.end )).field('id,pro,mem,addtime').order('id asc').select() try: m_pre = (psutil.virtual_memory().total / 1024 / 1024) / 100 def data_zip(x): x[-1] = SrcField(x[-1]) x[-2] = SrcField(x[-2]) x[3] = time.strftime('%m/%d %H:%M', time.localtime(float(x[3]))) if x[2] > 100: x[2] = int(x[2]) / m_pre return dict(zip(fields, x)) data = list(map(data_zip, data)) resp = Response(json.dumps(data, cls=SrcFieldJSONEncoder), content_type='application/json') return resp except: return [] def get_load_average(self, get): db_obj = public.M('load_average').dbfile('system') tmp_cols = db_obj.query('PRAGMA table_info(load_average)', ()) if db_obj.ERR_INFO: return [] fields = [] for col in tmp_cols: if len(col) > 2: fields.append(col[1]) fields.append("cpu_top") filter_str = "" len_data = (int(get.end) - int(get.start)) / 60 if len_data > 15000: filter_str = "AND load_average.id % 15 = 0" elif len_data > 1500: filter_str = "AND load_average.id % 3 = 0" public.print_log("filter_str:", filter_str) data = public.M('load_average').dbfile('system').query( "SELECT load_average.*,process_top_list.cpu_top from load_average inner join process_top_list on load_average.addtime=process_top_list.addtime where load_average.addtime>={} AND load_average.addtime<={} {} ORDER BY load_average.addtime desc;" .format(get.start, get.end, filter_str), ()) if isinstance(data, str) and data.find( 'error: no such table: process_top_list') != -1: return public.M('load_average').dbfile('system').where( "addtime>=? AND addtime<=?", (get.start, get.end) ).field('id,pro,one,five,fifteen,addtime').order('id asc').select() try: def data_zip(x): x[-1] = SrcField(x[-1]) x[5] = time.strftime('%m/%d %H:%M', time.localtime(float(x[5]))) return dict(zip(fields, x)) data = list(map(data_zip, data)) resp = Response(json.dumps(data, cls=SrcFieldJSONEncoder), content_type='application/json') return resp except: public.print_error() return [] def get_process_tops(self, get): ''' @name 获取进程开销排行 @author hwliang<2021-09-07> @param get{ start: int<开始时间> end: int<结束时间> } @return list ''' data = public.M('process_tops').dbfile('system').where( "addtime>=? AND addtime<=?", (get.start, get.end )).field('id,process_list,addtime').order('id asc').select() return self.ToAddtime(data) def get_process_cpu_high(self, get): ''' @name 获取CPU占用高的进程列表 @author hwliang<2021-09-07> @param get{ start: int<开始时间> end: int<结束时间> } @return list ''' data = public.M('process_high_percent').dbfile('system').where( "addtime>=? AND addtime<=?", (get.start, get.end)).field( 'id,name,pid,cmdline,cpu_percent,memory,cpu_time_total,addtime' ).order('id asc').select() return self.ToAddtime(data) def ToAddtime(self, data, tomem=False, types=None): import time # 格式化addtime列 if tomem: import psutil mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100 length = len(data) he = 1 if length > 100: he = 1 if length > 1000: he = 3 if length > 10000: he = 15 if he == 1: for i in range(length): try: if types: key = '{}_top'.format(types) if key in data[i]: data[i][key] = json.loads(data[i][key]) if 'memory_top' in data[i]: data[i]['memory_top'] = json.loads( data[i]['memory_top']) data[i]['addtime'] = time.strftime( '%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) if 'process_list' in data[i]: data[i]['process_list'] = json.loads( data[i]['process_list']) if tomem and data[i]['mem'] > 100: data[i]['mem'] = data[i]['mem'] / mPre if tomem in [None]: if type(data[i]['down_packets']) == str: data[i]['down_packets'] = json.loads( data[i]['down_packets']) data[i]['up_packets'] = json.loads( data[i]['up_packets']) except: continue return data else: count = 0 tmp = [] couns = 0 for value in data: if count < he: # 0 1 2 count += 1 # cpu大于60的时候,随机取 if types == "cpu" and 'pro' in value and value['pro'] > 60: couns += 1 # he等于3 的时候 百分之50的概率取 当he等于15的时候 百分之33的概率取 if (he == 3 and couns % 2 == 0) or (he == 15 and couns % 3 == 0): if types: key = '{}_top'.format(types) if key in value: value[key] = json.loads(value[key]) if 'memory_top' in value: value['memory_top'] = json.loads( value['memory_top']) value['addtime'] = time.strftime( '%m/%d %H:%M', time.localtime(float(value['addtime']))) if tomem and 'mem' in value and value['mem'] > 100: value['mem'] = value['mem'] / mPre if tomem in [None]: if type(value['down_packets']) == str: value['down_packets'] = json.loads( value['down_packets']) value['up_packets'] = json.loads( value['up_packets']) tmp.append(value) if types == "cpu" and value['pro'] > 50: pass else: continue try: if types: key = '{}_top'.format(types) if key in value: value[key] = json.loads(value[key]) if 'memory_top' in value: value['memory_top'] = json.loads( value['memory_top']) value['addtime'] = time.strftime( '%m/%d %H:%M', time.localtime(float(value['addtime']))) if tomem and 'mem' in value and value['mem'] > 100: value['mem'] = value['mem'] / mPre if tomem in [None]: if type(value['down_packets']) == str: value['down_packets'] = json.loads( value['down_packets']) value['up_packets'] = json.loads( value['up_packets']) tmp.append(value) count = 0 except: continue return tmp def GetInstalleds(self, softlist): softs = '' for soft in softlist['data']: try: for v in soft['versions']: if v['status']: softs += soft['name'] + '-' + v['version'] + '|' except: pass return softs # 获取SSH爆破次数 def get_ssh_intrusion(self): fp = open('/var/log/secure', 'rb') l = fp.readline() intrusion_total = 0 while l: if l.find('Failed password for root') != -1: intrusion_total += 1 l = fp.readline() fp.close() return intrusion_total # 申请内测版 def apple_beta(self, get): try: userInfo = json.loads(public.ReadFile('data/userInfo.json')) p_data = {} p_data['uid'] = userInfo['uid'] p_data['access_key'] = userInfo['access_key'] p_data['username'] = userInfo['username'] result = public.HttpPost(public.GetConfigValue('home') + '/api/panel/apple_beta', p_data, 5) try: return json.loads(result) except: return public.returnMsg(False, 'AJAX_CONN_ERR') except: return public.returnMsg(False, 'AJAX_USER_BINDING_ERR') def to_not_beta(self, get): try: userInfo = json.loads(public.ReadFile('data/userInfo.json')) p_data = {} p_data['uid'] = userInfo['uid'] p_data['access_key'] = userInfo['access_key'] p_data['username'] = userInfo['username'] result = public.HttpPost( public.GetConfigValue('home') + '/api/panel/to_not_beta', p_data, 5) try: return json.loads(result) except: return public.returnMsg(False, 'AJAX_CONN_ERR') except: return public.returnMsg(False, 'AJAX_USER_BINDING_ERR') def to_beta(self): try: userInfo = json.loads(public.ReadFile('data/userInfo.json')) p_data = {} p_data['uid'] = userInfo['uid'] p_data['access_key'] = userInfo['access_key'] p_data['username'] = userInfo['username'] public.HttpPost( public.GetConfigValue('home') + '/api/panel/to_beta', p_data, 5) except: pass def get_uid(self): try: userInfo = json.loads(public.ReadFile('data/userInfo.json')) return userInfo['uid'] except: return 0 # 获取最新的5条测试版更新日志 def get_beta_logs(self, get): try: data = json.loads( public.HttpGet( public.GetConfigValue('home') + '/api/panel/get_beta_logs')) return data except: return public.returnMsg(False, 'AJAX_CONN_ERR') def get_other_info(self): other = {} other['ds'] = [] ds = public.M('domain').field('name').select() for d in ds: other['ds'].append(d['name']) return ','.join(other['ds']) def get_docker_model_info(self): """ 获取docker模块下所创建的容器,模板和项目数量 :return: str """ try: if not os.path.exists("/www/server/panel/data/docker.db"): return "" # 获取docker模块创建的容器数量 dk_c_num = str( public.M('container').dbfile('docker').count()) + ' dk_c_num' # 获取docker模块创建模板数量 dk_t_num = str( public.M('templates').dbfile('docker').count()) + ' dk_t_num' # 获取docker模块创建项目数量 dk_s_num = str( public.M('stacks').dbfile('docker').count()) + ' dk_s_num' return "|{}|{}|{}".format(dk_c_num, dk_t_num, dk_s_num) except: return "" def UpdatePanel(self, get): try: import json if int(session['config']['status']) == 0: public.HttpGet( public.GetConfigValue('home') + '/Api/SetupCount?type=Linux') public.M('config').where("id=?", ('1',)).setField('status', 1) # 取回远程版本信 息 if 'updateInfo' in session and hasattr(get, 'check') == False: updateInfo = session['updateInfo'] else: logs = public.get_debug_log() import psutil, system, sys mem = psutil.virtual_memory() import panelPlugin mplugin = panelPlugin.panelPlugin() mplugin.ROWS = 10000 panelsys = system.system() data = public.get_user_info() data['ds'] = '' # self.get_other_info() data['sites'] = str(public.M('sites').count()) data['ftps'] = str(public.M('ftps').count()) data['databases'] = str(public.M('databases').count()) data['system'] = panelsys.GetSystemVersion() + '|' + str( mem.total / 1024 / 1024) + 'MB|' + str(public.getCpuType()) + '*' + str( psutil.cpu_count()) + '|' + str( public.get_webserver()) + '|' + session['version'] data['system'] += '||' + self.GetInstalleds( mplugin.getPluginList( None)) + self.get_docker_model_info() data['logs'] = logs data['client'] = request.headers.get('User-Agent') data['oem'] = '' data['intrusion'] = 0 data['uid'] = self.get_uid() # msg = public.getMsg('PANEL_UPDATE_MSG'); data['o'] = public.get_oem_name() sUrl = public.GetConfigValue('home') + '/api/panel/updateLinux' try: updateInfo = json.loads(public.httpPost(sUrl, data)) except: return public.returnMsg(False, "CONNECT_ERR") if not updateInfo: return public.returnMsg(False, "CONNECT_ERR") # updateInfo['msg'] = msg; session['updateInfo'] = updateInfo # 输出忽略的版本 updateInfo['ignore'] = [] no_path = '{}/data/no_update.pl'.format(public.get_panel_path()) if os.path.exists(no_path): try: updateInfo['ignore'] = json.loads(public.readFile(no_path)) except: pass updateInfo['local_beta'] = 0 local_beta = '/www/server/panel/data/local_beta.pl' try: # 2024/1/4 下午 3:32 g.version获取版本号,版本号格式为:8.0.48,判断最后一个数字是否大于10,如果大于10,则为测试版,小于或等于10,则为正式版 from BTPanel import g if int(g.version.split('.')[-1]) > 10: updateInfo['local_beta'] = 1 except: if os.path.exists(local_beta): updateInfo['local_beta'] = 1 # 检查是否需要升级 if not hasattr(get, 'toUpdate'): if updateInfo['is_beta'] == 1: if updateInfo['beta']['version'] == session['version']: return public.returnMsg(False, updateInfo) else: if updateInfo['version'] == session['version']: return public.returnMsg(False, updateInfo) # 是否执行升级程序 if (updateInfo['force'] == True or hasattr(get, 'toUpdate') == True or os.path.exists('data/autoUpdate.pl') == True): if not public.IsRestart(): return public.returnMsg(False, 'EXEC_ERR_TASK') if updateInfo['is_beta'] == 1: updateInfo['version'] = updateInfo['beta']['version'] setupPath = public.GetConfigValue('setup_path') uptype = 'update' httpUrl = public.get_url() if httpUrl: updateInfo[ 'downUrl'] = httpUrl + '/install/' + uptype + '/LinuxPanel-' + updateInfo[ 'version'] + '.zip' public.downloadFile(updateInfo['downUrl'], 'panel.zip') if os.path.getsize('panel.zip') < 1048576: return public.returnMsg(False, "PANEL_UPDATE_ERR_DOWN") public.ExecShell('unzip -o panel.zip -d ' + setupPath + '/') if os.path.exists('/www/server/panel/runserver.py'): public.ExecShell('rm -f /www/server/panel/*.pyc') if os.path.exists('/www/server/panel/class/common.py'): public.ExecShell('rm -f /www/server/panel/class/*.pyc') if os.path.exists('panel.zip'): os.remove("panel.zip") session['version'] = updateInfo['version'] if 'getCloudPlugin' in session: del (session['getCloudPlugin']) if os.path.exists(local_beta): os.remove(local_beta) if updateInfo['is_beta'] == 1: self.to_beta() public.writeFile(local_beta, 'True') try: # 标记数据库结构检查次数,防止更新后不检查 update_num_file = '{}/data/db/update'.format(public.get_panel_path()) if os.path.exists(update_num_file): num = public.readFile(update_num_file) if num and int(num) > 3: public.writeFile(update_num_file, '3') except: pass public.ExecShell("/etc/init.d/bt start") public.writeFile('data/restart.pl', 'True') return public.returnMsg(True, 'PANEL_UPDATE', (updateInfo['version'],)) public.ExecShell('rm -rf /www/server/phpinfo/*') return public.returnMsg(True, updateInfo) except Exception as ex: return public.get_error_info() # 检查是否安装任何 def CheckInstalled(self, get): checks = ['nginx', 'apache', 'php', 'pure-ftpd', 'mysql'] import os for name in checks: filename = public.GetConfigValue('root_path') + "/server/" + name if os.path.exists(filename): return True return False # 取已安装软件列表 def GetInstalled(self, get): import system data = system.system().GetConcifInfo() return data # 取PHP配置 def GetPHPConfig(self, get): if not hasattr(get, 'version'): return public.returnMsg(False, "缺少参数! version") import re, json filename = public.GetConfigValue( 'setup_path') + '/php/' + get.version + '/etc/php.ini' if public.get_webserver() == 'openlitespeed': filename = '/usr/local/lsws/lsphp{}/etc/php/{}.{}/litespeed/php.ini'.format( get.version, get.version[0], get.version[1]) if os.path.exists('/etc/redhat-release'): filename = '/usr/local/lsws/lsphp' + get.version + '/etc/php.ini' if not os.path.exists(filename): return public.returnMsg(False, 'PHP_NOT_EXISTS') phpini = public.readFile(filename) data = {} rep = "disable_functions\s*=\s{0,1}(.*)\n" tmp = re.search(rep, phpini) if tmp: data['disable_functions'] = tmp.groups()[0] rep = "upload_max_filesize\s*=\s*([0-9]+)(M|m|K|k)" tmp = re.search(rep, phpini) if tmp: data['max'] = tmp.groups()[0] rep = u"\n;*\s*cgi\.fix_pathinfo\s*=\s*([0-9]+)\s*\n" tmp = re.search(rep, phpini) if tmp: if tmp.groups()[0] == '0': data['pathinfo'] = False else: data['pathinfo'] = True self.getCloudPHPExt(get) try: phplib = json.loads(public.readFile('data/phplib.conf')) except: phplib = self.get_php_ext_by_cloud() libs = [] tasks = public.M('tasks').where("status!=?", ('1',)).field('status,name').select() phpini_ols = None for lib in phplib: lib['task'] = '1' for task in tasks: tmp = public.getStrBetween('[', ']', task['name']) if not tmp: continue tmp1 = tmp.split('-') if tmp1[0].lower() == lib['name'].lower(): lib['task'] = task['status'] lib['phpversions'] = [] lib['phpversions'].append(tmp1[1]) if public.get_webserver() == 'openlitespeed': lib['status'] = False get.php_version = "{}.{}".format(get.version[0], get.version[1]) if not phpini_ols: phpini_ols = self.php_info( get)['phpinfo']['modules'].lower() phpini_ols = phpini_ols.split() for i in phpini_ols: if lib['check'][:-3].lower() == i: lib['status'] = True break if "ioncube" in lib['check'][:-3].lower( ) and "ioncube" == i: lib['status'] = True break else: if phpini.find(lib['check']) == -1: lib['status'] = False else: lib['status'] = True libs.append(lib) data['libs'] = libs return data # 获取PHP扩展 def getCloudPHPExt(self, get): import json try: if 'php_ext' in session: return True if not session.get('download_url'): session['download_url'] = public.GetConfigValue('download') download_url = session['download_url'] + '/install/lib/phplib.json' tstr = public.httpGet(download_url) data = json.loads(tstr) if not data: return False public.writeFile('data/phplib.conf', json.dumps(data)) session['php_ext'] = True return True except: return False @staticmethod def get_php_ext_by_cloud(): try: if not session.get('download_url'): session['download_url'] = public.GetConfigValue('download') download_url = session['download_url'] + '/install/lib/phplib.json' resp_str = public.httpGet(download_url) data = json.loads(resp_str) if isinstance(data, list): public.writeFile('{}/data/phplib.conf'.format(public.get_panel_path()), resp_str) return data except: pass return [] def reGetCloudPHPExt(self, get): data = self.get_php_ext_by_cloud() if len(data) > 0: return public.returnMsg(True, '拉取云端数据成功') else: return public.returnMsg(False, '手动拉取云端数据失败,建议去 [面板设置] 中更换 [面板云端通讯节点] 后再手动拉取云端数据') # MySQL相关信息获取(告警信息获取) def GetMysqlAlarmInfo(self, get): from panelPush import panelPush p = panelPush() res = p.get_push_list(get) data = {"service": "mysql", "alarm": False, "id": "", "status": False} if not 'site_push' in res: return public.returnMsg(True, data) res = res['site_push'] for key, value in res.items(): if value["title"] == "mysql服务停止告警": data["id"] = key data["alarm"] = True data['data'] = value data["status"] = value["status"] return public.returnMsg(True, data) return public.returnMsg(True, data) # 取PHPINFO信息 def GetPHPInfo(self, get): if public.get_webserver() == "openlitespeed": shell_str = "/usr/local/lsws/lsphp{}/bin/php -i".format( get.version) return public.ExecShell(shell_str)[0] sPath = '/www/server/phpinfo' if os.path.exists(sPath): public.ExecShell("rm -rf " + sPath) p_file = '/dev/shm/phpinfo.php' public.writeFile(p_file, '') try: phpinfo = public.request_php(get.version, '/phpinfo.php', '/dev/shm') except: return public.returnMsg(False, '获取PHPINFO失败!') if os.path.exists(p_file): os.remove(p_file) return phpinfo.decode() # 清理日志 def delClose(self, get): if not 'uid' in session: session['uid'] = 1 if session['uid'] != 1: return public.returnMsg(False, '没有权限!') if 'tmp_login_id' in session: return public.returnMsg(False, '没有权限!') # 备份近100条日志 new_bak = public.M('logs').limit('100').select() if len(new_bak) > 3: bak_file = '{}/data/logs.bak'.format(public.get_panel_path()) public.writeFile(bak_file, json.dumps(new_bak)) public.add_security_logs( "清空日志", '清空所有日志条数为:{}'.format(public.M('logs').count())) # 清空日志 public.M('logs').where('id>?', (0,)).delete() public.WriteLog('TYPE_CONFIG', 'LOG_CLOSE') return public.returnMsg(True, 'LOG_CLOSE') def __get_webserver_conffile(self): webserver = public.get_webserver() if webserver == 'nginx': filename = public.GetConfigValue( 'setup_path') + '/nginx/conf/nginx.conf' elif webserver == 'openlitespeed': filename = public.GetConfigValue( 'setup_path' ) + "/panel/vhost/openlitespeed/detail/phpmyadmin.conf" else: filename = public.GetConfigValue( 'setup_path') + '/apache/conf/extra/httpd-vhosts.conf' return filename def phpmyadmin_client_check(self,get): from BTPanel import cache pmd = cache.get("pmd_port_path") if not pmd: from BTPanel import get_phpmyadmin_dir pmd = get_phpmyadmin_dir() if not pmd: return public.ReturnMsg(False,'未安装phpMyAdmin,请到【软件商店】页面安装!') pmd_path=pmd[0] pmd_port=pmd[1] phpmyadmin_url="http://127.0.0.1:{}/{}/index.php".format(pmd_port,pmd_path) import requests from requests.exceptions import ConnectionError, Timeout, HTTPError webserver=public.get_webserver() try: response = requests.get(phpmyadmin_url,timeout=5) status_code = response.status_code if 200 <= status_code < 400: return public.ReturnMsg(True,"可以正常访问!") elif status_code == 502 or status_code == 503: return public.ReturnMsg(False,"phpmyadmin访问状态{},请检查phpmyadmin的php是否正常运行!".format(status_code)) elif status_code == 404: return public.ReturnMsg(False,"phpmyadmin访问状态{},请尝试重装phpmyadmin看是否正常!".format(status_code)) elif status_code == 403: return public.ReturnMsg(False,"phpmyadmin访问状态{},请检查/www/server/phpmyadmin目录及上级目录文件权限是否是755权限!".format(status_code)) elif status_code == 401: return public.ReturnMsg(True,"可以正常访问!") else: return public.ReturnMsg(False,"phpmyadmin无法访问!状态码{},请截图此处报错联系客服人员查看!".format(status_code)) except ConnectionError: return public.ReturnMsg(False,"phpmyadmin面板无法代理访问,请检查{}是否正常启动,或尝试使用【phpmyadmin】-【通过公共访问】进行访问".format(webserver)) except Timeout: return public.ReturnMsg(False,"phpmyadmin连接超时,请检查{}是否正常启动或处于高负载状态".format(webserver)) except HTTPError as http_err: return public.ReturnMsg(False,"phpmyadmin无法访问!,请联系客服人员查看") except Exception as err: return public.ReturnMsg(False,"phpmyadmin无法访问!,请联系客服人员查看") return public.ReturnMsg(True,"可以正常访问!") # 获取phpmyadmin ssl配置 def get_phpmyadmin_conf(self): if public.get_webserver() == "nginx": conf_file = "/www/server/panel/vhost/nginx/phpmyadmin.conf" rep = r"listen\s*(\d+)" else: conf_file = "/www/server/panel/vhost/apache/phpmyadmin.conf" rep = r"Listen\s*(\d+)" return {"conf_file": conf_file, "rep": rep} # 设置phpmyadmin路径 def set_phpmyadmin_session(self): import re conf_file = self.get_phpmyadmin_conf() conf = public.readFile(conf_file["conf_file"]) rep = conf_file["rep"] if conf: port = re.search(rep, conf).group(1) if session['phpmyadminDir']: path = session['phpmyadminDir'].split("/")[-1] ip = public.GetHost() session['phpmyadminDir'] = "https://{}:{}/{}".format( ip, port, path) # 获取phpmyadmin ssl状态 def get_phpmyadmin_ssl(self, get): import re auth = False conf_file = self.get_phpmyadmin_conf() conf = public.readFile(conf_file["conf_file"]) rep = conf_file["rep"] if conf: port = re.search(rep, conf).group(1) path = "/www/server/panel/vhost/{}/phpmyadmin.conf".format(public.get_webserver()) if os.path.exists(path): conf = public.readFile(path) if conf.find("AUTH_START") != -1: auth = True return {"status": True, "port": port, "auth": auth} if public.get_webserver() == "nginx": path = "/www/server/nginx/conf/nginx.conf" if os.path.exists(path): conf = public.readFile(path) if conf.find("AUTH_START") != -1: auth = True else: path = "/www/server/apache/conf/extra/httpd-vhosts.conf" if os.path.exists(path): conf = public.readFile(path) if conf.find("AUTH_START") != -1: auth = True return {"status": False, "port": "", "auth": auth} # 修改php ssl端口 def change_phpmyadmin_ssl_port(self, get): if public.get_webserver() == "openlitespeed": return public.returnMsg(False, 'OpenLiteSpeed 目前尚不支持该操作') import re try: port = int(get.port) if 1 > port > 65535: return public.returnMsg(False, '端口范围不正确') except: return public.returnMsg(False, '端口格式不正确') for i in ["nginx", "apache"]: file = "/www/server/panel/vhost/{}/phpmyadmin.conf".format(i) conf = public.readFile(file) if not conf: return public.returnMsg( False, "没有找到{}配置文件,请尝试关闭ssl端口设置后再打开".format(i)) rulePort = [ '80', '443', '21', '20', '8080', '8081', '8089', '11211', '6379' ] if get.port in rulePort: return public.returnMsg(False, 'AJAX_PHPMYADMIN_PORT_ERR') if i == "nginx": if not os.path.exists( "/www/server/panel/vhost/apache/phpmyadmin.conf"): return public.returnMsg( False, "没有找到 apache phpmyadmin ssl 配置文件,请尝试关闭ssl端口设置后再打开") rep = r"listen\s*([0-9]+)\s*.*;" oldPort = re.search(rep, conf) if not oldPort: return public.returnMsg( False, '没有检测到 nginx phpmyadmin监听的端口,请确认是否手动修改过文件') oldPort = oldPort.groups()[0] conf = re.sub(rep, 'listen ' + get.port + ' ssl;', conf) else: rep = r"Listen\s*([0-9]+)\s*\n" oldPort = re.search(rep, conf) if not oldPort: return public.returnMsg( False, '没有检测到 apache phpmyadmin监听的端口,请确认是否手动修改过文件') oldPort = oldPort.groups()[0] conf = re.sub(rep, "Listen " + get.port + "\n", conf, 1) rep = r"VirtualHost\s*\*:[0-9]+" conf = re.sub(rep, "VirtualHost *:" + get.port, conf, 1) if oldPort == get.port: return public.returnMsg(False, 'SOFT_PHPVERSION_ERR_PORT') public.writeFile(file, conf) public.serviceReload() if i == "apache": import firewalls get.ps = public.getMsg('SOFT_PHPVERSION_PS') fw = firewalls.firewalls() fw.AddAcceptPort(get) public.serviceReload() public.WriteLog('TYPE_SOFT', 'SOFT_PHPMYADMIN_PORT', (get.port,)) get.id = public.M('firewall').where('port=?', (oldPort,)).getField('id') get.port = oldPort fw.DelAcceptPort(get) return public.returnMsg(True, 'SET_PORT_SUCCESS') def _get_phpmyadmin_auth(self): import re nginx_conf = '/www/server/nginx/conf/nginx.conf' reg = '#AUTH_START(.|\n)*#AUTH_END' if os.path.exists(nginx_conf): nginx_conf = public.readFile(nginx_conf) auth_tmp = re.search(reg, nginx_conf) if auth_tmp: return True apache_conf = '/www/server/apache/conf/extra/httpd-vhosts.conf' if os.path.exists(apache_conf): apache_conf = public.readFile(apache_conf) auth_tmp = re.search(reg, apache_conf) if auth_tmp: return True # 设置phpmyadmin ssl def set_phpmyadmin_ssl(self, get): if public.get_webserver() == "openlitespeed": return public.returnMsg(False, 'OpenLiteSpeed 目前尚不支持该操作') if not os.path.exists("/www/server/panel/ssl/certificate.pem"): return public.returnMsg(False, '面板证书不存在,请申请面板证书后再试') if get.v == "1": # 获取auth信息 auth = "" if self._get_phpmyadmin_auth(): auth = """ #AUTH_START auth_basic "Authorization"; auth_basic_user_file /www/server/pass/phpmyadmin.pass; #AUTH_END """ # nginx配置文件 ssl_conf = """server { listen 887 ssl; server_name phpmyadmin; index index.html index.htm index.php; root /www/server/phpmyadmin; #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则 #error_page 404/404.html; ssl_certificate /www/server/panel/ssl/certificate.pem; ssl_certificate_key /www/server/panel/ssl/privateKey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; error_page 497 https://$host$request_uri; #SSL-END %s include enable-php.conf; location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ .*\.(js|css)?$ { expires 12h; } location ~ /\. { deny all; } access_log /www/wwwlogs/access.log; }""" % auth public.writeFile("/www/server/panel/vhost/nginx/phpmyadmin.conf", ssl_conf) import panelPlugin get.sName = "phpmyadmin" v = panelPlugin.panelPlugin().get_soft_find(get) if self._get_phpmyadmin_auth(): auth = """ #AUTH_START AuthType basic AuthName "Authorization " AuthUserFile /www/server/pass/phpmyadmin.pass Require user jose #AUTH_END """ # apache配置 ssl_conf = '''Listen 887 ServerAdmin webmaster@example.com DocumentRoot "/www/server/phpmyadmin" ServerName 0b842aa5.phpmyadmin ServerAlias phpmyadmin.com #ErrorLog "/www/wwwlogs/BT_default_error.log" #CustomLog "/www/wwwlogs/BT_default_access.log" combined #SSL SSLEngine On SSLCertificateFile /www/server/panel/ssl/certificate.pem SSLCertificateKeyFile /www/server/panel/ssl/privateKey.pem SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLProtocol All -SSLv2 -SSLv3 SSLHonorCipherOrder On #PHP SetHandler "proxy:{}" #DENY FILES Order allow,deny Deny from all #PATH {} SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All Require all granted DirectoryIndex index.php index.html index.htm default.php default.html default.htm '''.format( public.get_php_proxy(v["ext"]["phpversion"], 'apache'), auth) public.writeFile("/www/server/panel/vhost/apache/phpmyadmin.conf", ssl_conf) else: if os.path.exists("/www/server/panel/vhost/nginx/phpmyadmin.conf"): os.remove("/www/server/panel/vhost/nginx/phpmyadmin.conf") if os.path.exists( "/www/server/panel/vhost/apache/phpmyadmin.conf"): os.remove("/www/server/panel/vhost/apache/phpmyadmin.conf") public.serviceReload() return public.returnMsg(True, '关闭成功') public.serviceReload() return public.returnMsg(True, '开启成功,请手动放行phpmyadmin ssl端口') # 设置PHPMyAdmin def setPHPMyAdmin(self, get): import re # try: filename = self.__get_webserver_conffile() if public.get_webserver() == 'openlitespeed': filename = "/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf" conf = public.readFile(filename) if not conf: return public.returnMsg(False, '未安装nginx或apache,无法设置!') if hasattr(get, 'port'): mainPort = public.readFile('data/port.pl').strip() rulePort = [ '80', '443', '21', '20', '8080', '8081', '8089', '11211', '6379' ] oldPort = "888" if get.port in rulePort: return public.returnMsg(False, 'AJAX_PHPMYADMIN_PORT_ERR') if public.get_webserver() == 'nginx': rep = r"listen\s+([0-9]+)\s*;" match = re.search(rep, conf) if match: oldPort = match.groups()[0] conf = re.sub(rep, 'listen ' + get.port + ';\n', conf) elif public.get_webserver() == 'apache': rep = r"Listen\s+([0-9]+)\s*\n" oldPort = re.search(rep, conf).groups()[0] conf = re.sub(rep, "Listen " + get.port + "\n", conf, 1) rep = r"VirtualHost\s+\*:[0-9]+" conf = re.sub(rep, "VirtualHost *:" + get.port, conf, 1) else: filename = '/www/server/panel/vhost/openlitespeed/listen/888.conf' conf = public.readFile(filename) reg = r"address\s+\*:(\d+)" tmp = re.search(reg, conf) if tmp: oldPort = tmp.groups()[0] conf = re.sub(reg, "address *:{}".format(get.port), conf) if oldPort == get.port: return public.returnMsg(False, 'SOFT_PHPVERSION_ERR_PORT') public.writeFile(filename, conf) import firewalls get.ps = public.getMsg('SOFT_PHPVERSION_PS') fw = firewalls.firewalls() fw.AddAcceptPort(get) public.serviceReload() public.WriteLog('TYPE_SOFT', 'SOFT_PHPMYADMIN_PORT', (get.port,)) get.id = public.M('firewall').where('port=?', (oldPort,)).getField('id') get.port = oldPort fw.DelAcceptPort(get) return public.returnMsg(True, 'SET_PORT_SUCCESS') if hasattr(get, 'phpversion'): if public.get_webserver() == 'nginx': filename = public.GetConfigValue( 'setup_path') + '/nginx/conf/enable-php.conf' conf = public.readFile(filename) rep = r"(unix:/tmp/php-cgi.*\.sock|127.0.0.1:\d+)" conf = re.sub(rep, public.get_php_proxy(get.phpversion, 'nginx'), conf, 1) elif public.get_webserver() == 'apache': rep = r"(unix:/tmp/php-cgi.*\.sock\|fcgi://localhost|fcgi://127.0.0.1:\d+)" conf = re.sub(rep, public.get_php_proxy(get.phpversion, 'apache'), conf, 1) else: reg = r'/usr/local/lsws/lsphp\d+/bin/lsphp' conf = re.sub( reg, '/usr/local/lsws/lsphp{}/bin/lsphp'.format(get.phpversion), conf) public.writeFile(filename, conf) public.serviceReload() public.WriteLog('TYPE_SOFT', 'SOFT_PHPMYADMIN_PHP', (get.phpversion,)) return public.returnMsg(True, 'SOFT_PHPVERSION_SET') if hasattr(get, 'password'): import panelSite if (get.password == 'close'): return panelSite.panelSite().CloseHasPwd(get) else: return panelSite.panelSite().SetHasPwd(get) if hasattr(get, 'status'): pma_path = public.GetConfigValue('setup_path') + '/phpmyadmin' stop_path = public.GetConfigValue('setup_path') + '/stop' webserver = public.get_webserver() if conf.find(stop_path) != -1: conf = conf.replace(stop_path, pma_path) msg = public.getMsg('START') if webserver == 'nginx': has_restrictions = ( re.search(r'allow\s+127\.0\.0\.1\s*;', conf) and re.search(r'allow\s+::1\s*;', conf) and re.search(r'deny\s+all\s*;', conf) ) if has_restrictions: conf = re.sub(r'^[ \t]*allow\s+127\.0\.0\.1\s*;[ \t]*$\n?', '', conf, flags=re.MULTILINE) conf = re.sub(r'^[ \t]*allow\s+::1\s*;[ \t]*$\n?', '', conf, flags=re.MULTILINE) conf = re.sub(r'^[ \t]*deny\s+all\s*;[ \t]*$\n?', '', conf, flags=re.MULTILINE) msg = public.getMsg('START') else: root_pattern = r'(root\s+' + re.escape(pma_path) + r'\s*;)' replacement = r'\1\n allow 127.0.0.1;\n allow ::1;\n deny all;' conf = re.sub(root_pattern, replacement, conf) msg = public.getMsg('STOP') elif webserver == 'apache': src_string = 'AllowOverride All' sub_string = '''{} Deny from all Allow from 127.0.0.1 ::1 localhost'''.format(src_string, pma_path) if conf.find(sub_string) != -1: conf = conf.replace(sub_string, src_string) msg = public.getMsg('START') else: conf = conf.replace(src_string, sub_string) msg = public.getMsg('STOP') else: if conf.find(stop_path) != -1: conf = conf.replace(stop_path, pma_path) msg = public.getMsg('START') else: conf = conf.replace(pma_path, stop_path) msg = public.getMsg('STOP') public.writeFile(filename, conf) public.serviceReload() public.WriteLog('TYPE_SOFT', 'SOFT_PHPMYADMIN_STATUS', (msg,)) return public.returnMsg(True, 'SOFT_PHPMYADMIN_STATUS', (msg,)) # except: # return public.returnMsg(False,'ERROR'); def ToPunycode(self, get): import re get.domain = get.domain.encode('utf8') tmp = get.domain.split('.') newdomain = '' for dkey in tmp: # 匹配非ascii字符 match = re.search(u"[\x80-\xff]+", dkey) if not match: newdomain += dkey + '.' else: newdomain += 'xn--' + dkey.decode('utf-8').encode( 'punycode') + '.' return newdomain[0:-1] # 保存PHP排序 def phpSort(self, get): if public.writeFile('/www/server/php/sort.pl', get.ssort): return public.returnMsg(True, 'SUCCESS') return public.returnMsg(False, 'ERROR') # 获取广告代码 def GetAd(self, get): try: return public.HttpGet( public.GetConfigValue('home') + '/Api/GetAD?name=' + get.name + '&soc=' + get.soc) except: return '' # 获取进度 def GetSpeed(self, get): return public.getSpeed() # 检查登陆状态 def CheckLogin(self, get): return True # 获取警告标识 def GetWarning(self, get): warningFile = 'data/warning.json' if not os.path.exists(warningFile): return public.returnMsg(False, 'AJAX_WARNING_ERR') import json, time wlist = json.loads(public.readFile(warningFile)) wlist['time'] = int(time.time()) return wlist # 设置警告标识 def SetWarning(self, get): wlist = self.GetWarning(get) id = int(get.id) import time, json for i in xrange(len(wlist['data'])): if wlist['data'][i]['id'] == id: wlist['data'][i]['ignore_count'] += 1 wlist['data'][i]['ignore_time'] = int(time.time()) warningFile = 'data/warning.json' public.writeFile(warningFile, json.dumps(wlist)) return public.returnMsg(True, 'SET_SUCCESS') # 获取memcached状态 def GetMemcachedStatus(self, get): import telnetlib, re conf = public.readFile('/etc/init.d/memcached') if not conf: return public.returnMsg(False, '获取负载状态失败,请重新安装memcached后再试!') result = {} result['bind'] = re.search('IP=(.+)', conf).groups()[0] result['port'] = int(re.search('PORT=(\d+)', conf).groups()[0]) result['maxconn'] = int(re.search('MAXCONN=(\d+)', conf).groups()[0]) result['cachesize'] = int( re.search('CACHESIZE=(\d+)', conf).groups()[0]) try: tn = telnetlib.Telnet(result['bind'], result['port']) except: return public.returnMsg(False, '获取负载状态失败,请检查服务是否启动!') tn.write(b"stats\n") tn.write(b"quit\n") data = tn.read_all() if type(data) == bytes: data = data.decode('utf-8') data = data.replace('STAT', '').replace('END', '').split("\n") res = [ 'cmd_get', 'get_hits', 'get_misses', 'limit_maxbytes', 'curr_items', 'bytes', 'evictions', 'limit_maxbytes', 'bytes_written', 'bytes_read', 'curr_connections' ] for d in data: if len(d) < 3: continue t = d.split() if not t[0] in res: continue result[t[0]] = int(t[1]) result['hit'] = 1 if result['get_hits'] > 0 and result['cmd_get'] > 0: result['hit'] = float(result['get_hits']) / float( result['cmd_get']) * 100 return result # 设置memcached缓存大小 def SetMemcachedCache(self, get): import re confFile = '/etc/init.d/memcached' conf = public.readFile(confFile) conf = re.sub('IP=.+', 'IP=' + get.ip, conf) conf = re.sub('PORT=\d+', 'PORT=' + get.port, conf) conf = re.sub('MAXCONN=\d+', 'MAXCONN=' + get.maxconn, conf) conf = re.sub('CACHESIZE=\d+', 'CACHESIZE=' + get.cachesize, conf) public.writeFile(confFile, conf) public.ExecShell(confFile + ' reload') return public.returnMsg(True, 'SET_SUCCESS') # 取redis状态 def GetRedisStatus(self, get): import re c = public.readFile('/www/server/redis/redis.conf') port = re.findall('\n\s*port\s+(\d+)', c)[0] password = re.findall('\n\s*requirepass\s+(.+)', c) if password: password = ' -a ' + password[0] else: password = '' data = public.ExecShell('/www/server/redis/src/redis-cli -p ' + port + password + ' info')[0] res = [ 'tcp_port', 'uptime_in_days', # 已运行天数 'connected_clients', # 连接的客户端数量 'used_memory', # Redis已分配的内存总量 'used_memory_rss', # Redis占用的系统内存总量 'used_memory_peak', # Redis所用内存的高峰值 'mem_fragmentation_ratio', # 内存碎片比率 'total_connections_received', # 运行以来连接过的客户端的总数量 'total_commands_processed', # 运行以来执行过的命令的总数量 'instantaneous_ops_per_sec', # 服务器每秒钟执行的命令数量 'keyspace_hits', # 查找数据库键成功的次数 'keyspace_misses', # 查找数据库键失败的次数 'latest_fork_usec' # 最近一次 fork() 操作耗费的毫秒数 ] data = data.split("\n") result = {} for d in data: if len(d) < 3: continue t = d.strip().split(':') if not t[0] in res: continue result[t[0]] = t[1] return result # 取PHP-FPM日志 def GetFpmLogs(self, get): import re fpm_path = '/www/server/php/' + get.version + '/etc/php-fpm.conf' if not os.path.exists(fpm_path): return public.returnMsg(False, 'AJAX_LOG_FILR_NOT_EXISTS') fpm_conf = public.readFile(fpm_path) log_tmp = re.findall(r"error_log\s*=\s*(.+)", fpm_conf) if not log_tmp: return public.returnMsg(False, 'AJAX_LOG_FILR_NOT_EXISTS') log_file = log_tmp[0].strip() if log_file.find('var/log') == 0: log_file = '/www/server/php/' + get.version + '/' + log_file return public.returnMsg(True, public.GetNumLines(log_file, 1000)) # 取PHP慢日志 def GetFpmSlowLogs(self, get): import re fpm_path = '/www/server/php/' + get.version + '/etc/php-fpm.conf' if not os.path.exists(fpm_path): return public.returnMsg(False, 'AJAX_LOG_FILR_NOT_EXISTS') fpm_conf = public.readFile(fpm_path) log_tmp = re.findall(r"slowlog\s*=\s*(.+)", fpm_conf) if not log_tmp: return public.returnMsg(False, 'AJAX_LOG_FILR_NOT_EXISTS') log_file = log_tmp[0].strip() if log_file.find('var/log') == 0: log_file = '/www/server/php/' + get.version + '/' + log_file return public.returnMsg(True, public.GetNumLines(log_file, 1000)) # 取指定日志 def GetOpeLogs(self, get): if not os.path.exists(get.path): return public.returnMsg(False, 'AJAX_LOG_FILR_NOT_EXISTS') return public.returnMsg( True, public.xsssec(public.GetNumLines(get.path, 1000))) def get_pd(self, get): from BTPanel import cache tmp = -1 try: import panelPlugin # get = public.dict_obj() # get.init = 1 tmp1 = panelPlugin.panelPlugin().get_cloud_list(get) except: tmp1 = None if tmp1: tmp = tmp1[public.to_string([112, 114, 111])] ltd = tmp1.get('ltd', -1) else: ltd = -1 tmp4 = cache.get( public.to_string([112, 95, 116, 111, 107, 101, 110])) if tmp4: tmp_f = public.to_string([47, 116, 109, 112, 47]) + tmp4 if not os.path.exists(tmp_f): public.writeFile(tmp_f, '-1') tmp = public.readFile(tmp_f) if tmp: tmp = int(tmp) if not ltd: ltd = -1 if tmp == None: tmp = -1 if ltd < 1: if ltd == -2: tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 116, 100, 45, 103, 114, 97, 121, 34, 62, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, 114, 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 24050, 36807, 26399, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 108, 116, 100, 40, 41, 34, 62, 32493, 36153, 60, 47, 97, 62, 60, 47, 115, 112, 97, 110, 62 ]) elif tmp == -1: tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 112, 114, 111, 45, 102, 114, 101, 101, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 99, 111, 109, 109, 101, 114, 99, 105, 97, 108, 95, 118, 105, 101, 119, 40, 41, 34, 32, 116, 105, 116, 108, 101, 61, 34, 28857, 20987, 21319, 32423, 21040, 21830, 19994, 29256, 34, 62, 20813, 36153, 29256, 60, 47, 115, 112, 97, 110, 62 ]) elif tmp == -2: tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 112, 114, 111, 45, 103, 114, 97, 121, 34, 62, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, 114, 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 24050, 36807, 26399, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 112, 114, 111, 40, 41, 34, 62, 32493, 36153, 60, 47, 97, 62, 60, 47, 115, 112, 97, 110, 62 ]) if tmp >= 0 and ltd in [-1, -2]: if tmp == 0: tmp2 = public.to_string([27704, 20037, 25480, 26435]) tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 112, 114, 111, 34, 62, 123, 48, 125, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 34, 62, 123, 49, 125, 60, 47, 115, 112, 97, 110, 62, 60, 47, 115, 112, 97, 110, 62 ]).format( public.to_string([21040, 26399, 26102, 38388, 65306]), tmp2) else: tmp2 = time.strftime( public.to_string([37, 89, 45, 37, 109, 45, 37, 100]), time.localtime(tmp)) tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 112, 114, 111, 34, 62, 21040, 26399, 26102, 38388, 65306, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, 114, 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 123, 48, 125, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 112, 114, 111, 40, 41, 34, 62, 32493, 36153, 60, 47, 97, 62, 60, 47, 115, 112, 97, 110, 62 ]).format(tmp2) else: tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 116, 100, 45, 103, 114, 97, 121, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 108, 116, 100, 40, 41, 34, 32, 116, 105, 116, 108, 101, 61, 34, 28857, 20987, 21319, 32423, 21040, 20225, 19994, 29256, 34, 62, 20813, 36153, 29256, 60, 47, 115, 112, 97, 110, 62 ]) else: tmp3 = public.to_string([ 60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 116, 100, 34, 62, 21040, 26399, 26102, 38388, 65306, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, 114, 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 123, 125, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 108, 116, 100, 40, 41, 34, 62, 32493, 36153, 60, 47, 97, 62, 60, 47, 115, 112, 97, 110, 62 ]).format( time.strftime( public.to_string([37, 89, 45, 37, 109, 45, 37, 100]), time.localtime(ltd))) return tmp3, tmp, ltd # 检查用户绑定是否正确 def check_user_auth(self, get): m_key = 'check_user_auth' if m_key in session: return session[m_key] u_path = 'data/userInfo.json' try: userInfo = json.loads(public.ReadFile(u_path)) except: if os.path.exists(u_path): os.remove(u_path) return public.returnMsg(False, '宝塔帐户绑定已失效,请在[设置]页面重新绑定!') pdata = { 'access_key': userInfo['access_key'], 'secret_key': userInfo['secret_key'] } result = public.HttpPost( public.GetConfigValue('home') + '/api/panel/check_auth_key', pdata, 3) if result == '0': if os.path.exists(u_path): os.remove(u_path) return public.returnMsg(False, '宝塔帐户绑定已失效,请在[设置]页面重新绑定!') if result == '1': session[m_key] = public.returnMsg(True, '绑定有效!') return session[m_key] return public.returnMsg(True, result) # PHP探针 def php_info(self, args): php_version = args.php_version.replace('.', '') php_path = '/www/server/php/' if public.get_webserver() == 'openlitespeed': php_path = '/usr/local/lsws/lsphp' php_bin = php_path + php_version + '/bin/php' php_ini = php_path + php_version + '/etc/php.ini' php_ini_lit = "/www/server/php/80/etc/php/80/litespeed/php.ini" if os.path.exists(php_ini_lit): php_ini = php_ini_lit tmp = public.ExecShell( php_bin + ' -c {} /www/server/panel/class/php_info.php'.format(php_ini))[0] if tmp.find('Warning: JIT is incompatible') != -1: tmp = tmp.strip().split('\n')[-1] try: result = json.loads(tmp) result['phpinfo'] = {} if "modules" not in result: result['modules'] = [] if 'php_version' in result: result['phpinfo']['php_version'] = result['php_version'] except Exception as e: result = { 'php_version': php_version, 'phpinfo': {}, 'modules': [], 'ini': '' } result['phpinfo']['php_path'] = php_path result['phpinfo']['php_bin'] = php_bin result['phpinfo']['php_ini'] = php_ini result['phpinfo']['modules'] = ' '.join(result['modules']) result['phpinfo']['ini'] = result['ini'] result['phpinfo']['keys'] = { "1cache": "缓存器", "2crypt": "加密解密库", "0db": "数据库驱动", "4network": "网络通信库", "5io_string": "文件和字符串处理库", "3photo": "图片处理库", "6other": "其它第三方库" } del (result['php_version']) del (result['modules']) del (result['ini']) return result # 取指定行 def get_lines(self, args): if not hasattr(args, 'filename'): return public.returnMsg(False, "缺少参数! filename") if not os.path.exists(args.filename): return public.returnMsg(False, '指定日志文件不存在!') num = args.get('num/d', 10) s_body = public.GetNumLines(args.filename, num) return public.returnMsg(True, s_body) def log_analysis(self, get): public.set_module_logs('log_analysis', 'log_analysis', 1) import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.log_analysis(get) def speed_log(self, get): import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.speed_log(get) def get_result(self, get): import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.get_result(get) def remove_analysis(self, get): import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.remove_analysis(get) def set_cron_task(self, get): import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.set_cron_task(get) def get_cron_task(self, get): import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.get_cron_task(get) def get_detailed(self, get): import log_analysis log_analysis = log_analysis.log_analysis() return log_analysis.get_detailed(get) def download_pay_type(self, path): public.downloadFile(public.get_url() + '/install/lib/pay_type.json', path) return True def get_pay_type(self, get): """ @name 获取推荐列表 """ spath = '{}/data/pay_type.json'.format(public.get_panel_path()) if os.path.exists(spath) and os.path.getsize(spath) <= 0: os.remove(spath) if not os.path.exists(spath): public.run_thread(self.download_pay_type, (spath,)) try: res = public.readFile("data/pay_type.json") if 'monitor' not in res: os.remove(spath) public.run_thread(self.download_pay_type, (spath,)) data = json.loads(res) except json.decoder.JSONDecodeError: if os.path.exists(spath):os.remove(spath) public.run_thread(self.download_pay_type, (spath,)) # data = json.loads(public.readFile("data/pay_type.json")) data = self.get_default_pay_type() except Exception: data = self.get_default_pay_type() import panelPlugin plu_panel = panelPlugin.panelPlugin() plugin_list = plu_panel.get_cloud_list() if not 'pro' in plugin_list: plugin_list['pro'] = -1 for item in data: if 'list' in item: item['list'] = self.__get_home_list(item['list'], item['type'], plugin_list, plu_panel) if item['type'] == 1: if len(item['list']) > 4: item['list'] = item['list'][:4] if item['type'] == 0 and plugin_list['pro'] >= 0: item['show'] = False return data def __get_home_list(self, sList, stype, plugin_list, plu_panel): """ @name 获取首页软件列表推荐 """ nList = [] webserver = public.get_webserver() for x in sList: for plugin_info in plugin_list['list']: if x['name'] == plugin_info['name']: if not 'endtime' in plugin_info or plugin_info[ 'endtime'] >= 0: x['isBuy'] = True is_check = False if 'dependent' in x: if x['dependent'] == webserver: is_check = True else: is_check = True if is_check: info = plu_panel.get_soft_find(x['name']) if info: if stype == 1: if plugin_list['pro'] >= 0: continue if not info['setup']: x['install'] = info['setup'] nList.append(x) else: x['install'] = info['setup'] nList.append(x) return nList def ignore_version(self, get): """ @忽略版本更新 :param version 忽略的版本号 """ version = get.version path = '{}/data/no_update.pl'.format(public.get_panel_path()) try: data = json.loads(public.readFile(path)) except: data = [] if not version in data: data.append(version) public.writeFile(path, json.dumps(data)) try: del (session['updateInfo']) except: pass return public.returnMsg(True, "忽略成功,此版本将不再提醒更新.") def check_auth_ip(self, get): """ @name 检查授权ip """ return public.check_auth_ip() def get_panel_error_info(self, get): """ @name 处理面板常用错误 """ error = get.error force = 0 if 'force' in get: force = int(get.force) path = '{}/data/panel_error_info.json'.format(public.get_panel_path()) if not os.path.exists(path) or force: public.downloadFile( public.get_url() + '/linux/panel/panel_error_info.json', path) if not os.path.exists(path): return public.returnMsg(False, '获取失败!') data = [] try: data = json.loads(public.readFile(path)) except: pass for info in data: if 'key' in info: for val in info['key']: if not val: continue total, num = 0, 0 for tmp in val.split('&'): total += 1 if error.lower().find(tmp.lower()) >= 0: num += 1 if total > 0 and num == total: return public.returnMsg(True, info['value']) return public.returnMsg( False, '未识别的错误信息,请前往宝塔论坛 发帖提问.' ) def Clean_bt_host(self, get): ''' 清理本机bt.cn的hosts @author wzz @param get: @return: ''' return public.Clean_bt_host() def Get_ip_info(self, get): ''' 获取bt官网ip及用户服务器公网ip归属地列表 @author wzz @param get: @return: ''' if hasattr(get, "get_speed"): return public.Get_ip_info(get.get_speed) return public.Get_ip_info() def Set_bt_host(self, get): ''' 设置bt官网(www && api)指定hosts节点 @author wzz @param get: get.ip 官网传ip地址,从Get_ip_info方法获取 @return: ''' if hasattr(get, "ip"): return public.Set_bt_host(get.ip) return public.Set_bt_host() @staticmethod def get_default_pay_type(): spath = '{}/data/default_pay_type.json'.format(public.get_panel_path()) default = [ { "type": 0, "pay": "45", "describe": "首页-企业版推荐", "show": True, "route": "home", "name": "ltd", "price": "999.99", "preview": "https://www.bt.cn/new/product/linux_ltd.html", "ps": [ "5分钟极速响应", "15天无理由退款", "30+款付费插件", "20+企业版专享功能", "2张SSL商用证书(年付)", "1000条免费短信(年付)", "专享企业服务群(年付)" ] }, { "type": 1, "describe": "首页-软件管理-常用软件推荐", "show": True, "route": "home", "list": [ { "pay": "40", "title": "网站防火墙", "name": "btwaf", "isBuy": False, "install": False, "pid": 100000010, "dependent": "nginx", "pluginType": "pro", "preview": "https://www.bt.cn/new/product_nginx_firewall.html", "ps": "web防火墙,有效抵御CC攻击、SQL注入、XSS跨站攻击、建站程序漏洞、一句话木马等常见渗透攻击" }, { "pay": "40", "title": "网站防火墙", "name": "btwaf_httpd", "install": False, "isBuy": False, "pid": 100000012, "pluginType": "pro", "dependent": "apache", "preview": "https://www.bt.cn/new/product_nginx_firewall.html", "ps": "web防火墙,有效抵御CC攻击、SQL注入、XSS跨站攻击、建站程序漏洞、一句话木马等常见渗透攻击" }, { "pay": "41", "title": "网站监控报表-重构版", "name": "monitor", "isBuy": False, "install": False, "pluginType": "pro", "pid": 100000014, "preview": "https://www.bt.cn/new/product_website_total.html", "ps": "网站监控报表,实时精确统计网站流量、ip、uv、pv、请求、蜘蛛等数据" }, { "pay": "42", "title": "堡塔企业级防篡改-重构版", "name": "tamper_core", "isBuy": False, "install": False, "preview": "", "pid": 100000067, "pluginType": "ltd", "ps": "事件型防篡改程序,可有效保护网站重要文件不被木马篡改" }, { "pay": "43", "title": "堡塔防入侵", "name": "bt_security", "isBuy": False, "install": False, "pid": 100000054, "pluginType": "ltd", "preview": "", "ps": "防御大多数的入侵提权攻击造成的挂马和被挖矿" } ] }, { "type": 2, "pay": "33", "describe": "首页-状态-任务管理器", "show": True, "route": "home", "list": [] }, { "type": 3, "pay": "34", "describe": "首页-安全入口-推荐安全软件", "show": True, "route": "home", "list": [] }, { "type": 4, "pay": "35", "describe": "网站-网站加速", "show": False, "name": "waf_nginx", "title": "网站加速", "pluginName": "堡塔nginx站点加速", "ps": "基于nginx页面缓存的网站加速插件,推荐WordPress用户安装,效果显著,仅支持Nginx", "preview": "", "eventList": [ { "event": "", "version": "" } ] }, { "type": 5, "describe": "网站-设置推荐", "show": True, "list": [ { "title": "防火墙", "name": "btwaf", "pay": "46", "pluginName": "Nginx网站防火墙", "ps": "有效拦截SQL 注入、XSS跨站、恶意代码、网站挂马等常见攻击,过滤恶意访问,降低数据泄露的风险,保障网站的可用性。", "preview": "https://www.bt.cn/new/product_nginx_firewall.html", "dependent": "nginx", "pluginType": "pro", "eventList": [ { "event": "site_waf_config('$siteName')", "version": "5.2.0" } ] }, { "title": "防火墙", "name": "btwaf_httpd", "pay": "46", "pluginName": "网站防火墙", "ps": "有效拦截SQL 注入、XSS跨站、恶意代码、网站挂马等常见攻击,过滤恶意访问,降低数据泄露的风险,保障网站的可用性。", "preview": "https://www.bt.cn/new/product_nginx_firewall.html", "dependent": "apache", "pluginType": "pro", "eventList": [ { "event": "site_waf_config('$siteName')", "version": "5.2.0" } ] }, { "title": "统计", "name": "total", "pay": "47", "pluginName": "网站监控报表", "ps": "快速分析网站运行状况,实时精确统计网站流量、ip、uv、pv、请求、蜘蛛等数据,网站SEO优化利器", "preview": "https://www.bt.cn/new/product_website_total.html", "dependent": "apache", "pluginType": "pro", "eventList": [ { "event": "WebsiteReport('$siteName')", "version": "5.0" } ] }, { "title": "统计", "name": "total", "pay": "47", "pluginName": "网站监控报表", "ps": "快速分析网站运行状况,实时精确统计网站流量、ip、uv、pv、请求、蜘蛛等数据,网站SEO优化利器", "preview": "https://www.bt.cn/new/product_website_total.html", "dependent": "nginx", "pluginType": "pro", "eventList": [ { "event": "WebsiteReport('$siteName')", "version": "5.0" } ] } ] }, { "type": 6, "show": True, "describe": "网站管理-推荐安全软件", "list": [ { "title": "网站防篡改程序", "pay": "60", "name": "tamper_proof", "product_introduce": [ "保护站点内容安全", "阻止黑客非法修改网页", "阻止网站被挂马", "阻止其他入侵行为" ], "previewImg": "https://www.bt.cn/Public/new/plugin/introduce/site/tamper_proof_preview.png", "menu_id": 15, "menu_name":"防篡改", "isBuy": False, "pid": 100000015, "pluginType": "pro", "preview": "", "ps": "事件型防篡改程序,可有效保护网站重要文件不被木马篡改" }, { "title": "限制访问型证书", "pay": "61", "name": "ssl_verify", "pluginType": "ltd", "product_introduce": [ "限制指定人员访问", "双向认证", "内网自签SSL" ], "previewImg": "https://www.bt.cn/Public/new/plugin/introduce/site/ssl_verify_preview.png", "menu_id": 3, "menu_name":"访问限制", "isBuy": False, "pid": 100000062, "preview": "", "ps": "提供双向认证证书,可用于限制指定人员访问" } ] }, { "type": 7, "show": True, "describe": "文件管理-推荐安全软件", "list": [ { "title": "文件同步", "pluginName": "文件同步", "pay": "70", "name": "rsync", "pluginType": "pro", "ps": "基于rsync开发的文件同步工具,可用于异地备份、多台主机之间的文件实时或增量同步", "previewImg": "https://www.bt.cn/Public/new/plugin/rsync/1.png", "menu_id": 15, "isBuy": False, "pid": 100000005, "preview": "" } ] } ] if os.path.isfile(spath): try: res_data = json.loads(public.readFile(spath)) if isinstance(res_data, list): return res_data except json.JSONDecodeError: pass # 再次出错时,保障网站列表可以展示 return default return default # 获取指定天数的网络Io def GetNetWorkIoByDay(self, get): try: if not hasattr(get, 'day'): return public.returnMsg(False, '参数错误!') day = int(get.day) # 获取今天0点时间戳 result = [] end_time = int(time.time()) start_time = int(datetime.now().replace(tzinfo=timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0).timestamp()) for i in range(day): time_name = datetime.utcfromtimestamp(start_time).strftime('%Y-%m-%d') data = public.M('network').dbfile('system').where( "addtime>=? AND addtime<=?", (start_time, end_time) ).field( 'id,up,down,total_up,total_down,down_packets,up_packets,addtime' ).order('id desc').select() total_up = [i['total_up'] for i in data] total_down = [i['total_down'] for i in data] total_down.sort(reverse=True) total_up.sort(reverse=True) if total_down: total_down = total_down[0] - total_down[-1] else: total_down = 0 if total_up: total_up = total_up[0] - total_up[-1] else: total_up = 0 result.append({ 'time': time_name, 'total_up': total_up, 'total_down': total_down }) end_time = start_time start_time = start_time - 86400 return result except: pass def create_sql_index(self): try: import db sql = db.Sql().dbfile('system') if not sql.query("SELECT name FROM sqlite_master WHERE type='index' AND name='cpu'"): sql.execute("CREATE INDEX 'cpu' ON 'cpuio'('addtime')") if not sql.query("SELECT name FROM sqlite_master WHERE type='index' AND name='ntwk'"): sql.execute("CREATE INDEX 'ntwk' ON 'network'('addtime')") if not sql.query("SELECT name FROM sqlite_master WHERE type='index' AND name='disk'"): sql.execute("CREATE INDEX 'disk' ON 'diskio'('addtime')") if not sql.query("SELECT name FROM sqlite_master WHERE type='index' AND name='load'"): sql.execute("CREATE INDEX 'load' ON 'load_average'('addtime')") if not sql.query("SELECT name FROM sqlite_master WHERE type='index' AND name='proc'"): sql.execute("CREATE INDEX 'proc' ON 'process_top_list'('addtime')") public.writeFile('data/sql_index.pl', 'True') except: pass