Files
yakpanel-core/class_v2/data_v2.py
2026-04-07 02:04:22 +05:30

1733 lines
73 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#coding: utf-8
# +-------------------------------------------------------------------
# | YakPanel
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2016 YakPanel(www.yakpanel.com) All rights reserved.
# +-------------------------------------------------------------------
# | Author: hwliang <hwl@yakpanel.com>
# +-------------------------------------------------------------------
import sys,os,re,time,json
from datetime import datetime, timedelta
import requests
if not 'class/' in sys.path:
sys.path.insert(0,'class/')
import db,public,panelMysql
import json
import public
from public.validate import Param
try:
from YakPanel import cache
except:
cache = None
class data:
__ERROR_COUNT = 0
#自定义排序字段
__SORT_DATA = ['site_ssl','php_version','backup_count']
DB_MySQL = None
web_server = None
setupPath = '/www/server'
siteorder_path = '/www/server/panel/data/siteorder.pl'
limit_path = '/www/server/panel/data/limit.pl'
# 删除排序记录
def del_sorted(self, get):
public.ExecShell("rm -rf {}".format(self.siteorder_path))
return public.returnMsg(True, public.lang("Clear sorting successfully"))
'''
* 设置备注信息
* @param String _GET['tab'] 数据库表名
* @param String _GET['id'] 条件ID
* @return Bool
'''
def setPs(self,get):
# 校验参数
try:
get.validate([
Param('table').Require().String(),
Param('ps').Require().String(),
Param('id').Require().Integer(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
id = get.id
get.ps = public.xssencode2(get.ps)
if public.M(get.table).where("id=?",(id,)).setField('ps',get.ps):
# public.get_msg_gettext(True, public.lang("Setup successfully!"))
return public.return_message(0, 0, public.lang("Setup successfully"))
# public.get_msg_gettext(False, public.lang("Failed to modify"))
return public.return_message(-1, 0, public.lang("Failed to modify"))
#端口扫描
def CheckPort(self,port):
import socket
localIP = '127.0.0.1'
temp = {}
temp['port'] = port
temp['local'] = True
try:
s = socket.socket()
s.settimeout(0.01)
s.connect((localIP,port))
s.close()
except:
temp['local'] = False
result = 0
if temp['local']: result +=2
return result
# 转换时间
def strf_date(self, sdate):
return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S'))
def get_cert_end(self,pem_file):
try:
import OpenSSL
result = {}
x509 = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, public.readFile(pem_file))
# 取产品名称
issuer = x509.get_issuer()
result['issuer'] = ''
if hasattr(issuer, 'CN'):
result['issuer'] = issuer.CN
if not result['issuer']:
is_key = [b'0', '0']
issue_comp = issuer.get_components()
if len(issue_comp) == 1:
is_key = [b'CN', 'CN']
for iss in issue_comp:
if iss[0] in is_key:
result['issuer'] = iss[1].decode()
break
# 取到期时间
result['notAfter'] = self.strf_date(
bytes.decode(x509.get_notAfter())[:-1])
# 取申请时间
result['notBefore'] = self.strf_date(
bytes.decode(x509.get_notBefore())[:-1])
# 取可选名称
result['dns'] = []
for i in range(x509.get_extension_count()):
s_name = x509.get_extension(i)
if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']:
s_dns = str(s_name).split(',')
for d in s_dns:
result['dns'].append(d.split(':')[1])
subject = x509.get_subject().get_components()
# 取主要认证名称
if len(subject) == 1:
result['subject'] = subject[0][1].decode()
else:
result['subject'] = result['dns'][0]
return result
except:
return public.get_cert_data(pem_file)
def get_site_ssl_info(self,siteName):
try:
s_file = 'vhost/nginx/{}.conf'.format(siteName)
is_apache = False
if not os.path.exists(s_file):
s_file = 'vhost/apache/{}.conf'.format(siteName)
is_apache = True
if not os.path.exists(s_file):
return -1
s_conf = public.readFile(s_file)
if not s_conf: return -1
ssl_file = None
if is_apache:
if s_conf.find('SSLCertificateFile') == -1:
return -1
s_tmp = re.findall(r"SSLCertificateFile\s+(.+\.pem)",s_conf)
if not s_tmp: return -1
ssl_file = s_tmp[0]
else:
if s_conf.find('ssl_certificate') == -1:
return -1
s_tmp = re.findall(r"ssl_certificate\s+(.+\.pem);",s_conf)
if not s_tmp: return -1
ssl_file = s_tmp[0]
ssl_info = self.get_cert_end(ssl_file)
if not ssl_info: return -1
ssl_info['endtime'] = int(int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
return ssl_info
except: return -1
#return "{}:{}".format(ssl_info['issuer'],ssl_info['notAfter'])
# 查询网站对应的PHP版本
def get_php_version(self,siteName):
try:
if not self.web_server:
self.web_server = public.get_webserver()
conf = public.readFile(self.setupPath + '/panel/vhost/'+self.web_server+'/'+siteName+'.conf')
if self.web_server == 'openlitespeed':
conf = public.readFile(
self.setupPath + '/panel/vhost/' + self.web_server + '/detail/' + siteName + '.conf')
if self.web_server == 'nginx':
rep = r"enable-php-(\w{2,5})[-\w]*\.conf"
elif self.web_server == 'apache':
rep = r"php-cgi-(\w{2,5})\.sock"
else:
rep = r"path\s*/usr/local/lsws/lsphp(\d+)/bin/lsphp"
tmp = re.search(rep,conf).groups()
if tmp[0] == '00':
return 'Static'
if tmp[0] == 'other':
return 'Other'
return tmp[0][0] + '.' + tmp[0][1]
except:
return 'Static'
def map_to_list(self,map_obj):
try:
if type(map_obj) != list and type(map_obj) != str: map_obj = list(map_obj)
return map_obj
except: return []
def get_database_size(self,databaseName):
try:
if not self.DB_MySQL:self.DB_MySQL = panelMysql.panelMysql()
db_size = self.map_to_list(self.DB_MySQL.query("select sum(DATA_LENGTH)+sum(INDEX_LENGTH) from information_schema.tables where table_schema='{}'".format(databaseName)))[0][0]
if not db_size: return 0
return int(db_size)
except:
return 0
def get_site_quota(self,path):
'''
@name 获取网站目录配额信息
@author hwliang<2022-02-15>
@param path<string> 网站目录
@return dict
'''
res = {
"used": 0,
"size": 0,
"quota_push": {
"size": 0,
"used": 0,
},
"quota_storage": {
"size": 0,
"used": 0,
}
}
try:
from projectModelV2.quotaModel import main
quota_info = main().get_quota_path(path)
if isinstance(quota_info, dict):
res.update(quota_info)
res['size'] = int(quota_info['quota_push']['size']) + int(quota_info['quota_storage']['size'])
return res
return res
except:
# from traceback import format_exc
# public.print_log(format_exc())
return res
#最新版本v2版本
# try:
# from projectModelV2.quotaModel import main
# quota_info = main().get_quota_path(path)
# if isinstance(quota_info,dict):
# return quota_info
# return res
# except: return res
def get_database_quota(self,db_name):
'''
@name 获取网站目录配额信息
@author hwliang<2022-02-15>
@param path<string> 网站目录
@return dict
'''
res = {
"used": 0,
"size": 0,
"quota_push": {
"size": 0,
"used": 0,
},
"quota_storage": {
"size": 0,
"used": 0,
}
}
try:
from projectModelV2.quotaModel import main
quota_info = main().get_quota_mysql(db_name)
if isinstance(quota_info, dict):
res.update(quota_info)
res['size'] = int(quota_info['quota_push']['size']) + int(quota_info['quota_storage']['size'])
return res
return res
except:
return res
#最新版本v2版本
# try:
# from projectModelV2.quotaModel import main
# quota_info = main().get_quota_mysql(db_name)
# if isinstance(quota_info,dict):
# return quota_info
# return res
# except: return res
'''
* 取数据列表
* @param String _GET['tab'] 数据库表名
* @param Int _GET['count'] 每页的数据行数
* @param Int _GET['p'] 分页号 要取第几页数据
* @return Json page.分页数 , count.总行数 data.取回的数据
'''
def getData(self, get):
# 校验参数
try:
get.validate([
Param('table').Require().String(),
Param('search').String(),
Param('limit').Integer(),
Param('p').Integer(),
Param('type').String(),
Param('project_type'),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
# 如果网站列表包含 rname 字段排序 先检查表内是否有 rname字段
if hasattr(get, "order") and get.table == 'sites':
if get.order.startswith('rname'):
data = public.M('sites').find()
if 'rname' not in data.keys():
public.M('sites').execute("ALTER TABLE 'sites' ADD 'rname' text DEFAULT ''", ())
# 先检查tasks表内是否有 install_status、message字段
if get.table == 'tasks':
data = public.M('tasks').find()
if 'install_status' not in data.keys():
public.M('tasks').execute("ALTER TABLE 'tasks' ADD 'install_status' INTEGER DEFAULT 1", ())
if 'message' not in data.keys():
public.M('tasks').execute("ALTER TABLE 'tasks' ADD 'message' TEXT DEFAULT ''", ())
table = get.table
data = self.GetSql(get)
SQL = public.M(table)
user_Data = self.get_user_power()
if user_Data != 'all' and table in ['sites', 'databases', 'ftps']:
data['data'] = [i for i in data['data'] if str(i['id']) in user_Data.get(table, [])]
try:
# table = get.table
# data = self.GetSql(get)
# SQL = public.M(table)
if table == 'backup':
import os
backup_path = public.M('config').where('id=?',(1,)).getField('backup_path')
for i in range(len(data['data'])):
if data['data'][i]['size'] == 0:
if os.path.exists(data['data'][i]['filename']):
data['data'][i]['size'] = os.path.getsize(data['data'][i]['filename'])
else:
if not os.path.exists(data['data'][i]['filename']):
if (data['data'][i]['filename'].find('/www/') != -1 or data['data'][i]['filename'].find(backup_path) != -1) and data['data'][i]['filename'][0] == '/' and data['data'][i]['filename'].find('|') == -1:
data['data'][i]['size'] = 0
data['data'][i]['ps'] = '文件不存在'
if data['data'][i]['ps'] in ['','']:
if data['data'][i]['name'][:3] == 'db_' or (data['data'][i]['name'][:4] == 'web_' and data['data'][i]['name'][-7:] == '.tar.gz'):
data['data'][i]['ps'] = '自动备份'
else:
data['data'][i]['ps'] = '手动备份'
#判断本地文件是否存在,以确定能否下载
data['data'][i]['local']=data['data'][i]['filename'].split('|')[0]
data['data'][i]['localexist']=0 if os.path.isfile(data['data'][i]['local']) else 1
elif table == 'sites' or table == 'databases':
type = '0'
site_ids=[]
if table == 'databases':
type = '1'
for i in range(len(data['data'])):
#将data['data'][i]['id']添加到site_ids列表中
site_ids.append(data['data'][i]['id'])
if table == 'databases': data['data'][i]['conn_config'] = json.loads(data['data'][i]['conn_config'])
data['data'][i]['quota'] = self.get_database_quota(data['data'][i]['name'])
try:
# 兼容wp备份时间转换格式
if get.get('project_type','') == 'WP2':
backup_records = public.S('wordpress_backups').where_in('s_id', site_ids).group('s_id').field(
's_id', 'count(*) as cnt', 'max(bak_time) as last_backup_time').select()
backup_info_map = {}
for record in backup_records:
s_id = record['s_id']
last_backup_time = record['last_backup_time']
if isinstance(last_backup_time, (int, float)):
formatted_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_backup_time))
else:
try:
formatted_time = last_backup_time.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
formatted_time = ''
formatted_record = {
's_id': s_id,
'cnt': record['cnt'],
'last_backup_time': formatted_time
}
# 添加到映射表
backup_info_map[s_id] = formatted_record
else:
backup_info_map = {j['pid']: j for j in public.S('backup').where_in('pid', site_ids).where("type", type).group('pid').field('pid','count(*) as cnt','max(addtime) as last_backup_time').select()}
except Exception as e:
backup_info_map = {}
for i in range(len(data['data'])): data['data'][i]['backup_count'] = backup_info_map.get(data['data'][i]['id'], {}).get("cnt", 0)
if table == 'sites':
if get.get('project_type','') == 'WP2':
# 检测类型表,是否正确
import one_key_wp_v2
ok, msg = one_key_wp_v2.checklist_fields()
if not ok and msg== "no such table: wordpress_onekey":
return public.return_message(0, 0, msg)
elif not ok:
return public.return_message(-1, 0, msg)
# 获取网站类型
if get.get('site_type', ''):
site_sql = public.M("wordpress_onekey").select()
site_type = public.M("wp_site_types").where('`id` = ?', (get.get('site_type', ''),)).find()
filtered_data = []
# 加载网站图标
import os
multi_webservice_status = public.get_multi_webservice_status()
webservice = public.get_webserver()
nginx_b64_path = public.image_to_base64(os.path.join(public.get_panel_path(), "YakPanel/static/img/soft_ico/ico-nginx.png"))
apache_b64_path = public.image_to_base64(os.path.join(public.get_panel_path(), "YakPanel/static/img/apache.png"))
ols_b64_path = public.image_to_base64(os.path.join(public.get_panel_path(), "YakPanel/static/img/soft_ico/ico-openlitespeed.png"))
# 按流量排序
re_data = None
if get.get('re_order'):
re_data = self.get_site_request(public.to_dict_obj({'site_type':'PHP'}))
if re_data['status'] == 0:
re_data = re_data['message']
for i in range(len(data['data'])):
# 添加流量排序
if re_data:
if data["data"][i]['name'] in re_data:
data['data'][i]['re_total'] = re_data[data["data"][i]['name']]['total']['request']
else:
data['data'][i]['re_total'] = 0
data["data"][i]["last_backup_time"] = backup_info_map.get(data['data'][i]['id'], {}).get("last_backup_time", "")
data['data'][i]['domain'] = SQL.table('domain').where("pid=?",(data['data'][i]['id'],)).count()
# data['data'][i]['ssl'] = self.get_site_ssl_info(data['data'][i]['name'])
ssl_info = self.get_site_ssl_info(data['data'][i]['name'])
data['data'][i]['ssl'] = ssl_info
data['data'][i]['site_ssl'] = ssl_info['endtime'] if ssl_info != -1 else -1
data['data'][i]['php_version'] = self.get_php_version(data['data'][i]['name'])
data['data'][i]['attack'] = self.get_analysis(get,data['data'][i])
data['data'][i]['project_type'] = SQL.table('sites').where('id=?',(data['data'][i]['id'])).field('project_type').find()['project_type']
data['data'][i]['ps']=data['data'][i]['ps'].replace("For panel Lets Encrypt certificate","For panel Let's Encrypt certificate",1)
if data['data'][i]['project_type'] in ['WP', 'WP2']:
import one_key_wp
one_key_wp_obj = one_key_wp.one_key_wp()
data['data'][i]['cache_status'] = False
data['data'][i]['wp_version'] = '0.0.0'
try:
data['data'][i]['cache_status'] = one_key_wp_obj.get_cache_status(data['data'][i]['id'])
data['data'][i]['wp_version'] = one_key_wp_obj.get_wp_version(data['data'][i]['id'])
except:
pass
data['data'][i]['login_url'] = '/v2/wp/login/{}'.format(data['data'][i]['id'])
if data['data'][i]['project_type'] == 'WP2':
from wp_toolkit import wpbackup
data['data'][i]['backup_count'] = wpbackup(data['data'][i]['id']).backup_count()
wordpress_scan_path = "/www/server/panel/data/wordpress_wp_scan.json"
data['data'][i]['scan']={"last_time": 0,"vulnerabilities": 0,"status": True}
import os
if os.path.exists(wordpress_scan_path):
try:
wordpress_scan_info = json.loads(public.readFile(wordpress_scan_path))
if data['data'][i]['path'] in wordpress_scan_info:
data['data'][i]['scan'] = wordpress_scan_info[data['data'][i]['path']]
except:
pass
if not data['data'][i]['status'] in ['0','1',0,1]:
data['data'][i]['status'] = '1'
data['data'][i]['quota'] = self.get_site_quota(data['data'][i]['path'])
site1 = SQL.table('sites').where('id=?', (data['data'][i]['id'])).find()
if hasattr(site1, 'rname'):
data['data'][i]['rname'] = \
SQL.table('sites').where('id=?', (data['data'][i]['id'])).field('rname').find()['rname']
if not data['data'][i].get('rname', ''):
data['data'][i]['rname'] = data['data'][i]['name']
data["net_flow_info"] = {}
# 网站图标
data['data'][i]['ico']=""
try:
import os
ico_b64_path = os.path.join(public.get_panel_path(), "data/site_favs", data['data'][i]['name'] + ".b64")
default = public.readFile(ico_b64_path) if os.path.exists(ico_b64_path) else ''
if multi_webservice_status:
if default:
data['data'][i]['ico'] = default
elif 'apache' == data['data'][i]['service_type']:
data['data'][i]['ico'] = apache_b64_path
elif 'nginx' == data['data'][i]['service_type'] or not data['data'][i]['service_type']:
data['data'][i]['ico'] = nginx_b64_path
elif 'openlitespeed' == data['data'][i]['service_type']:
data['data'][i]['ico'] = ols_b64_path
else:
data['data'][i]['ico'] = default
else:
if default:
data['data'][i]['ico'] = default
elif webservice == 'nginx':
data['data'][i]['ico'] = nginx_b64_path
elif webservice == 'apache':
data['data'][i]['ico'] = apache_b64_path
elif webservice == 'openlitespeed':
data['data'][i]['ico'] = ols_b64_path
else:
data['data'][i]['ico'] = default
# 判断是否启动类型筛选,启动后为数据添加类型
if get.get('site_type', ''):
for type_ in site_sql:
if data['data'][i]['id'] == type_['s_id'] and site_type['name'] == type_[
'site_type']:
data['data'][i]['site_type'] = type_['site_type']
filtered_data.append(data['data'][i])
except:
public.print_log(public.get_error_info())
# try:
# net_flow_json_info = json.loads(public.readFile(net_flow_json_file))
# data["net_flow_info"] = net_flow_json_info
# except Exception:
# data["net_flow_info"] = {}
# 判断是否进行了类型筛选
if get.get('site_type', ''):
data['data'] = filtered_data
if get.get('re_order') and re_data:
if get.get('re_order') == 'desc':
data['data'] = sorted(data['data'], key=lambda x: x["re_total"], reverse=True)
else:
data['data'] = sorted(data['data'], key=lambda x: x["re_total"])
elif table == 'firewall':
for i in range(len(data['data'])):
if data['data'][i]['port'].find(':') != -1 or data['data'][i]['port'].find('.') != -1 or data['data'][i]['port'].find('-') != -1:
data['data'][i]['status'] = -1
else:
data['data'][i]['status'] = self.CheckPort(int(data['data'][i]['port']))
elif table == 'ftps':
for i in range(len(data['data'])):
data['data'][i]['quota'] = self.get_site_quota(data['data'][i]['path'])
try:
for _find in data['data']:
_keys = _find.keys()
for _key in _keys:
_find[_key] = public.xsssec(_find[_key])
except:
pass
#返回
res = self.get_sort_data(data)
return public.return_message(0, 0, res)
except:
res = public.get_error_info()
# return public.get_error_info()
return public.return_message(0, 0, res)
def get_data_list(self, get):
try:
self.check_and_add_stop_column()
if get.table == 'sites':
if not hasattr(get, 'order'):
if os.path.exists(self.siteorder_path):
order = public.readFile(self.siteorder_path)
if order.split(' ')[0] in self.__SORT_DATA:
get.order = order
else:
public.writeFile(self.siteorder_path, get.order)
if not hasattr(get, 'limit') or get.limit == '' or int(get.limit) == 0:
try:
if os.path.exists(self.limit_path):
get.limit = int(public.readFile(self.limit_path))
else:
get.limit = 20
except:
get.limit = 20
else:
public.writeFile(self.limit_path, get.limit)
if not hasattr(get, 'order'):
get.order = 'addtime desc'
get = self._get_args(get)
try:
s_list = self.func_models(get, 'get_data_where')
except:
s_list = []
where_sql, params = self.get_where(get, s_list)
data = self.get_page_data(get, where_sql, params)
get.data_list = data['data']
try:
data['data'] = self.func_models(get, 'get_data_list')
except :
print(traceback.format_exc())
if get.table == 'sites':
if isinstance(data, dict):
file_path = os.path.join(public.get_panel_path(), "data/sort_list.json")
if os.path.exists(file_path):
sort_list_raw = public.readFile(file_path)
sort_list = json.loads(sort_list_raw)
sort_list_int = [int(item) for item in sort_list["list"]]
for i in range(len(data['data'])):
if int(data['data'][i]['id']) in sort_list_int:
data['data'][i]['sort'] = 1
else:
data['data'][i]['sort'] = 0
top_list = sort_list["list"]
if top_list:
top_list = top_list[::-1]
top_data = [item for item in data["data"] if str(item['id']) in top_list]
data1 = [item for item in data["data"] if str(item['id']) not in top_list]
top_data.sort(key=lambda x: top_list.index(str(x['id'])))
data['data'] = top_data + data1
public.set_search_history(get.table, get.search_key, get.search) # 记录搜索历史
# 字段排序
data = self.get_sort_data(data)
if 'type_id' in get:
type_id=int(get['type_id'])
if type_id:
filtered_data = []
target_type_id = type_id
# print(data['data'])
for item in data['data']:
if item.get('type_id') == target_type_id:
filtered_data.append(item)
data['data'] = filtered_data
if get.get("db_type",""):
if type_id < 0:
filtered_data = []
target_type_id = type_id
for item in data['data']:
if item.get('type_id') == target_type_id:
filtered_data.append(item)
data['data'] = filtered_data
return data
except:
return traceback.format_exc()
# 获取用户权限列表
def get_user_power(self, get=None):
user_Data = 'all'
try:
uid = session.get('uid')
if uid != 1 and uid:
plugin_path = '/www/server/panel/plugin/users'
if os.path.exists(plugin_path):
user_authority = os.path.join(plugin_path, 'authority')
if os.path.exists(user_authority):
if os.path.exists(os.path.join(user_authority, str(uid))):
try:
data = json.loads(self._decrypt(public.ReadFile(os.path.join(user_authority, str(uid)))))
if data['role'] == 'administrator':
user_Data = 'all'
else:
user_Data = json.loads(self._decrypt(public.ReadFile(os.path.join(user_authority, str(uid) + '.data'))))
except:
user_Data = {}
else:
user_Data = {}
except:
pass
return user_Data
def get_sort_data(self,data):
"""
@获取自定义排序数据
@param data: 数据
"""
if 'plist' in data:
plist = data['plist']
o_list = plist['order'].split(' ')
reverse = False
sort_key = o_list[0].strip()
if o_list[1].strip() == 'desc':
reverse = True
if sort_key in ['site_ssl']:
for info in data['data']:
if type(info['ssl']) == int:
info[sort_key] = info['ssl']
else:
try:
info[sort_key] = info['ssl']['endtime']
except :
info[sort_key] = ''
data['data'] = sorted(data['data'],key=lambda x:x[sort_key],reverse=reverse)
data['data'] = data['data'][plist['shift']: plist['shift'] + plist['row']]
return data
'''
* 取数据库行
* @param String _GET['tab'] 数据库表名
* @param Int _GET['id'] 索引ID
* @return Json
'''
def getFind(self,get):
tableName = get.table
id = get.id
field = self.GetField(get.table)
SQL = public.M(tableName)
where = "id=?"
find = SQL.where(where,(id,)).field(field).find()
try:
_keys = find.keys()
for _key in _keys:
find[_key] = public.xsssec(find[_key])
except:
pass
return find
'''
* 取字段值
* @param String _GET['tab'] 数据库表名
* @param String _GET['key'] 字段
* @param String _GET['id'] 条件ID
* @return String
'''
def getKey(self,get):
# 校验参数
try:
get.validate([
Param('table').Require().String(),
Param('key').Require().String(),
Param('id').Require().Integer(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.return_message(-1, 0, str(ex))
tableName = get.table
keyName = get.key
id = get.id
SQL = db.Sql().table(tableName)
where = "id=?"
result = SQL.where(where,(id,)).getField(keyName)
if result is None:
return public.return_message(-1, 0, None)
res = result
if isinstance(result, str):
res = public.xsssec(result)
return public.return_message(0, 0, res)
'''
* 获取数据与分页
* @param string table 表
* @param string where 查询条件
* @param int limit 每页行数
* @param mixed result 定义分页数据结构
* @return array
'''
def GetSql(self,get,result = '1,2,3,4,5,8'):
#判断前端是否传入参数
order = 'id desc'
if hasattr(get,'order'):
# 验证参数格式
if re.match(r"^[\w\s\-\.]+$",get.order):
order = get.order
search_key = 'get_list'
limit = 20
if hasattr(get,'limit'):
limit = int(get.limit)
if limit < 1: limit = 20
if hasattr(get,'result'):
# 验证参数格式
if re.match(r"^[\d\,]+$",get.result):
result = get.result
SQL = db.Sql()
data = {}
#取查询条件
where = ''
search = ''
param = ()
if hasattr(get,'search'):
search = get.search
if sys.version_info[0] == 2: get.search = get.search.encode('utf-8')
where,param = self.GetWhere(get.table,get.search)
if get.table == 'backup':
where += " and type='{}'".format(int(get.type))
if get.table == 'sites' and get.search:
conditions = ''
if '_' in get.search:
cs = ''
for i in get.search:
if i == '_':
cs += '/_'
else:
cs += i
get.search = cs
conditions = " escape '/'"
pid = SQL.table('domain').where("name LIKE ?{}".format(conditions),("%{}%".format(get.search),)).getField('pid')
if pid:
if where:
where += " or id=" + str(pid)
else:
where += "id=" + str(pid)
if get.table == 'sites':
search_key = 'php'
# 额外对 project_type 字段做处理
if 'project_type' in get:
extra_where = "`project_type` = '{}'".format(get.project_type)
if where:
where = r"({}) AND {}".format(where, extra_where)
else:
where = extra_where
else:
extra_where = "`project_type` IN ('PHP', 'WP')"
if where:
where = r"({}) AND {}".format(where, extra_where)
else:
where = extra_where
if hasattr(get,'type'):
if get.type != '-1':
where += " AND type_id={}".format(int(get.type))
if get.table == 'databases':
if hasattr(get,'db_type'):
if where:
where += " AND db_type='{}'".format(int(get.db_type))
else:
where = "db_type='{}'".format(int(get.db_type))
if hasattr(get,'sid'):
if where:
where += " AND sid='{}'".format(int(get.sid))
else:
where = "sid='{}'".format(int(get.sid))
if where:
where += " and type='MySQL'"
else:
where = 'type = "MySQL"'
field = self.GetField(get.table)
#实例化数据库对象
public.set_search_history(get.table,search_key,search) #记录搜索历史
#是否直接返回所有列表
if hasattr(get,'list'):
data = SQL.table(get.table).where(where,param).field(field).order(order).select()
return data
#取总行数
count = SQL.table(get.table).where(where,param).count()
#get.uri = get
#包含分页类
import page
#实例化分页类
page = page.Page()
info = {}
info['count'] = count
info['row'] = limit
info['p'] = 1
if hasattr(get,'p'):
info['p'] = int(get['p'])
if info['p'] <1: info['p'] = 1
try:
from flask import request
info['uri'] = public.url_encode(request.full_path)
except:
info['uri'] = ''
info['return_js'] = ''
if hasattr(get,'tojs'):
if re.match(r"^[\w\.\-]+$",get.tojs):
info['return_js'] = get.tojs
data['where'] = where
#获取分页数据
data['page'] = page.GetPage(info,result)
#取出数据
#data['data'] = SQL.table(get.table).where(where,param).order(order).field(field).limit(str(page.SHIFT)+','+str(page.ROW)).select()
o_list = order.split(' ')
if o_list[0] in self.__SORT_DATA:
data['data'] = SQL.table(get.table).where(where,param).field(field).select()
data['plist'] = {'shift':page.SHIFT,'row':page.ROW,'order':order}
else:
data['data'] = SQL.table(get.table).where(where,param).order(order).field(field).limit(str(page.SHIFT)+','+str(page.ROW)).select() #取出数据
data['search_history'] = public.get_search_history(get.table,search_key)
return data
#获取条件
def GetWhere(self,tableName,search):
if not search: return "",()
if type(search) == bytes: search = search.encode('utf-8').strip()
try:
search = re.search(r"[\w\x80-\xff\.\_\-]+",search).group()
except:
return '',()
conditions = ''
if '_' in search:
cs = ''
for i in search:
if i == '_':
cs += '/_'
else:
cs += i
search = cs
conditions = " escape '/'"
wheres = {
'sites': ("name LIKE ? OR ps LIKE ?{}".format(conditions), ('%' + search + '%', '%' + search + '%')),
'ftps': ("name LIKE ? OR ps LIKE ?{}".format(conditions), ('%' + search + '%', '%' + search + '%')),
'databases': (
"(name LIKE ? {} OR ps LIKE ?{})".format(conditions, conditions),
("%" + search + "%", "%" + search + "%")),
'crontab': ("name LIKE ?{}".format(conditions), ('%' + (search) + '%')),
'logs': ("username=? OR type LIKE ?{} OR log LIKE ?{}".format(conditions, conditions),
(search, '%' + search + '%', '%' + search + '%')),
'backup' : ("pid=?",(search,)),
'users' : ("id='?' OR username=?",(search,search)),
'domain' : ("pid=? OR name=?",(search,search)),
'tasks' : ("status=? OR type=?",(search,search)),
}
# wheres = {
# 'sites' : ("name LIKE ? OR ps LIKE ?",('%'+search+'%','%'+search+'%')),
# 'ftps' : ("name LIKE ? OR ps LIKE ?",('%'+search+'%','%'+search+'%')),
# 'databases' : ("(name LIKE ? OR ps LIKE ?)",("%"+search+"%","%"+search+"%")),
# 'logs' : ("username=? OR type LIKE ? OR log LIKE ?",(search,'%'+search+'%','%'+search+'%')),
# 'backup' : ("pid=?",(search,)),
# 'users' : ("id='?' OR username=?",(search,search)),
# 'domain' : ("pid=? OR name=?",(search,search)),
# 'tasks' : ("status=? OR type=?",(search,search)),
# }
try:
return wheres[tableName]
except:
return '',()
# 获取返回的字段
def GetField(self,tableName):
fields = {
'sites' : "id,name,path,status,ps,addtime,edate,service_type",
'ftps' : "id,pid,name,password,status,ps,addtime,path",
'databases' : "id,sid,pid,name,username,password,accept,ps,addtime,db_type,conn_config",
'logs' : "id,uid,username,type,log,addtime",
'backup' : "id,pid,name,filename,addtime,size,ps",
'users' : "id,username,phone,email,login_ip,login_time",
'firewall' : "id,port,ps,addtime",
'domain' : "id,pid,name,port,addtime",
'tasks' : "id,name,type,status,addtime,start,end,install_status"
}
try:
return fields[tableName]
except:
return ''
def get_analysis(self,get,i):
import log_analysis
get.path = '/www/wwwlogs/{}.log'.format(i['name'])
get.action = 'get_result'
data = log_analysis.log_analysis().get_result(get)
return int(data['php']) + int(data['san']) + int(data['sql']) + int(data['xss'])
def get_date_range(self,
start_date: str = None,
end_date: str = None,
date_format: str = "%Y-%m-%d"
) -> list[str]:
"""
根据起止日期获取日期列表,支持指定日期格式
参数:
start_date: 开始日期字符串格式与date_format一致默认None表示end_date往前推29天
end_date: 结束日期字符串格式与date_format一致默认None表示今天
date_format: 日期字符串格式(默认"%Y-%m-%d"
返回:
日期字符串列表按从end_date到start_date的顺序排列包含两端日期
异常:
ValueError: 日期格式错误或start_date晚于end_date
"""
# 处理默认结束日期(默认为今天)
if end_date is None:
end_date_obj = datetime.now().date()
else:
try:
end_date_obj = datetime.strptime(end_date, date_format).date()
except ValueError:
raise ValueError(f"结束日期格式错误,应为{date_format}")
# 处理默认开始日期默认是结束日期往前推29天共30天
if start_date is None:
start_date_obj = end_date_obj - timedelta(days=29)
else:
try:
start_date_obj = datetime.strptime(start_date, date_format).date()
except ValueError:
raise ValueError(f"开始日期格式错误,应为{date_format}")
# 校验日期逻辑(开始日期不能晚于结束日期)
if start_date_obj > end_date_obj:
raise ValueError(f"开始日期({start_date})不能晚于结束日期({end_date})")
# 生成日期列表从start_date递增到end_date自然从小到大
date_list = []
current_date = start_date_obj # 从开始日期开始
while current_date <= end_date_obj: # 循环到结束日期为止
date_list.append(current_date.strftime(date_format))
current_date += timedelta(days=1) # 日期递增
return date_list
def get_site_data(self,
base_dir: str,
site_name: str,
date_list: list[str]
) -> dict[str, any]:
"""
获取单个站点的统计数据
参数:
base_dir: 基础数据目录
site_name: 站点名称
date_list: 日期列表YYYY-MM-DD格式
返回:
包含list和total的站点数据字典
"""
site_dir = os.path.join(base_dir, site_name)
daily_data = []
total_requests = 0
# 处理每个日期的数据
for date_str in date_list:
# 转换日期格式为YYYYMMDD整数类型
date_int = int(date_str.replace("-", ""))
file_path = os.path.join(site_dir, f"{date_str}.json")
# 初始化当日请求数为0
requests = 0
# 尝试读取文件
if os.path.exists(file_path) and os.path.isfile(file_path):
try:
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
# 确保requests字段存在且为数字
if isinstance(data.get("requests"), (int, float)):
requests = int(data["requests"])
except (json.JSONDecodeError, Exception):
# 解析错误或其他异常时保持请求数为0
pass
# 添加当日数据
daily_data.append({
"request": requests,
"date": date_int
})
# 累计总请求数
total_requests += requests
return {
"list": daily_data,
"total": {
"request": total_requests
}
}
def get_multi_site_stats(self,
base_dir: str = "/www/server/site_total/data/total",
site_names: list[str] = None,
start_date: str = None,
end_date: str = None
) -> dict[str, any]:
"""
获取多个站点的统计数据,返回指定格式的结果
参数:
base_dir: 基础数据目录
site_names: 要查询的站点名称列表,默认包含示例站点
返回:
符合指定格式的统计数据字典
"""
message = {}
# 默认站点列表
if site_names is None:
return message
# 获取日期列表
date_list = self.get_date_range(start_date, end_date)
# 收集所有站点数据
for site in site_names:
# 检查站点目录是否存在
site_dir = os.path.join(base_dir, site)
if os.path.exists(site_dir) and os.path.isdir(site_dir):
message[site] = self.get_site_data(base_dir, site, date_list)
else:
# 站点不存在时返回空数据结构
message[site] = {
"list": [{"request": 0, "date": int(date.replace("-", ""))} for date in date_list],
"total": {"request": 0}
}
# 构建最终返回格式
return message
def __check_auth(self):
from plugin_auth_v2 import Plugin as Plugin
plugin_obj = Plugin(False)
plugin_list = plugin_obj.get_plugin_list()
import PluginLoader
self.__IS_PRO_MEMBER = PluginLoader.get_auth_state() > 0
return int(plugin_list["pro"]) > time.time() or self.__IS_PRO_MEMBER
# 获取网站监控报表数据
def getSiteThirtyTotal(self, get=None):
"""
2026/2/26 调整由前端传入site_type控制输出
"""
prorject_type = ['PHP','WP2','proxy' , 'Node' , 'Python']
if get is not None:
if get.get("site_type", None) in prorject_type:
prorject_type = [get.get("site_type")]
#检测插件是否安装
if not os.path.exists(os.path.join(public.get_panel_path(),"plugin/monitor/info.json")):
site_names = [ i.get('name','') for i in public.S('sites').where_in('project_type',prorject_type).field('name').select() if i.get('name','') !='' ]
start_date= None if get==None else get.get('start_date',None)
end_date= None if get==None else get.get('end_date',None)
return {'status': 0, "timestamp": int(time.time()), "message": self.get_multi_site_stats(site_names=site_names,start_date=start_date,end_date=end_date)}
cache_file = os.path.join(public.get_panel_path(), 'plugin/monitor/site_thirty_total.json')
result ={}
try:
version=self.get_plugin_version(os.path.join(public.get_panel_path(),"plugin/monitor/info.json"))
version_list=version.split(".")
if len(version_list)<3:return {'status': 0, "timestamp": int(time.time()), "message": result}
if int(version_list[0])<4:return {'status': 0, "timestamp": int(time.time()), "message": result}
if int(version_list[0])==4 and int(version_list[1])<1:return {'status': 0, "timestamp": int(time.time()), "message": result}
if int(version_list[0])==4 and int(version_list[1])<1 and int(version_list[2])<2:return {'status': 0, "timestamp": int(time.time()), "message": result}
#取网站域名列表
try:
domain_list = public.S('sites').where_in('project_type',prorject_type).field('name').select()
except Exception as ex:
domain_list = []
now_time = int(time.time())
start_date = public.format_date(format="%Y-%m-%d", times=now_time - 86400 * 30)
end_date = public.format_date(format="%Y-%m-%d", times=now_time)
cache_info = {}
if get!=None:
content = public.readFile(cache_file)
if content:
cache_info = json.loads(content)
#取网站统计信息
for domain in domain_list:
result[domain["name"]]={"list":[],"total":{"request":0}}
site_requests= {}
if get==None:
# 等待500ms
time.sleep(0.1)
args=public.dict_obj()
args.start_date=start_date
args.end_date=end_date
args.part_type="date"
args.SiteName=domain["name"]
site_requests = public.run_plugin("monitor","get_site_total_list_custom",args)
try:
if "list" in site_requests:
result[domain["name"]]["total"]["request"]=site_requests["total"]["request"]
for site in site_requests["list"]:
# 等待100ms
time.sleep(0.03)
result[domain["name"]]["list"].append({"request":site["request"],"date":site["date"]})
except Exception as e:
# public.print_log('Error__________ {}'.format(str(e)))
pass
else:
try:
if cache_info and "list" in cache_info[domain["name"]]:
result[domain["name"]]=cache_info[domain["name"]]
except Exception as e:
# public.print_log('Error__________ {}'.format(str(e)))
result[domain["name"]]={}
except Exception as e:
# public.print_log(public.get_error_info())
pass
if get==None:
public.writeFile(cache_file, json.dumps(result))
return {'status': 0, "timestamp": int(time.time()), "message": result}
# 简化版获取网站请求数用于排序
def get_site_request(self,get=None):
project_type = ['PHP','WP2','proxy' , 'Node' , 'Python']
if get is not None:
if get.get("site_type") and get.get("site_type") in project_type:
project_type = [get.get("site_type")]
#保留免费版
if not os.path.exists(os.path.join(public.get_panel_path(),"plugin/monitor/info.json")):
site_names = [ i.get('name','') for i in public.S('sites').where_in('project_type',project_type).field('name').select() if i.get('name','') !='' ]
start_date= None if get==None else get.get('start_date',None)
end_date= None if get==None else get.get('end_date',None)
return {'status': 0, "timestamp": int(time.time()), "message": self.get_multi_site_stats(site_names=site_names,start_date=start_date,end_date=end_date)}
# 直接读取监控报表数据缓存
path = os.path.join(public.get_panel_path(), "plugin/monitor/site_thirty_total.json")
try:
re_data = {}
if os.path.exists(path):
re_data = json.loads(public.readFile(path))
return {'status': 0, "timestamp": int(time.time()), "message": re_data}
except:
return {'status': -1, "timestamp": int(time.time()), "message": {}}
# 获取waf报表数据
def getSiteWafConfig(self, get=None):
cache_file = os.path.join(public.get_panel_path(), "plugin/btwaf/site_waf_config_php.json")
if get!=None:
try:
result=json.loads(public.readFile(cache_file))
if "status" in result:
result={}
except Exception as e:
result={}
return {'status': 0, "timestamp": int(time.time()), "message": result}
result ={}
try:
version=self.get_plugin_version(os.path.join(public.get_panel_path(),"plugin/btwaf/info.json"))
version_list=version.split(".")
#9.6.8版本以上才支持waf报表
if len(version_list)<3:return result
if int(version_list[0])<9:return result
if int(version_list[0])==9 and int(version_list[1])<6:return result
if int(version_list[0])==9 and int(version_list[1])<6 and int(version_list[2])<8:return result
get=public.dict_obj()
get.p=1
get.limit=10000
get.search=""
result = public.run_plugin("btwaf","get_site_config3",get)
if "status" in result:
result={}
public.writeFile(cache_file, json.dumps(result))
except Exception as e:
public.print_log(public.get_error_info())
pass
return result
def get_plugin_version(self,filename):
if os.path.exists(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
data = json.load(f)
return data.get("versions")
except: return "0.0.0"
return "0.0.0"
def find_stored_favicons(self):
try:
# 检查是否开关是否开启
icon_path = '/www/server/panel/config/auto_favicon.conf'
if os.path.exists(icon_path):
return
cur_time = int(time.time())
last_find_stored_favicons_time = cache.get('last_find_stored_favicons_time')
if last_find_stored_favicons_time and cur_time - last_find_stored_favicons_time < 3600 * 12:
return
import requests
import base64
site_favs_root = os.path.join(public.get_panel_path(), "data/site_favs")
if not os.path.exists(site_favs_root):
os.makedirs(site_favs_root, 0o755)
sites = public.S('sites').field('id', 'name', 'path').select()
reg_obj = re.compile(
r'<link (?:rel="(?:shortcut|icon| )+" *|type="image/x-icon" *|href="([^"]+)" *)+[^>]*>')
for site in sites:
site_name = site['name']
site_path = site['path']
ico_path = os.path.join(site_path, "favicon.ico")
stored_ico_path = os.path.join(site_favs_root, site_name + '.ico')
if not os.path.exists(ico_path) and (not os.path.exists(stored_ico_path) or os.path.getmtime(stored_ico_path) < cur_time - 86400):
# 尝试请求favicon.ico
domains = public.S('domain').where('pid=?', (site['id'],)).field('name', 'port').select()
# 只在前2个域名中查找
if len(domains) > 2:
domains = domains[:2]
for domain in domains:
domain_name = domain['name']
port = domain['port'] if domain['port'] else 80
protocol = 'https' if port == 443 else 'http'
url = "{}://127.0.0.1{}/".format(protocol, ':{}'.format(port) if port not in [80, 443] else '')
# public.print_log('url: {} {}'.format(domain, url))
try:
# 首先尝试直接请求favicon.ico
ico_url = "{}/favicon.ico".format(url.strip('/'))
# public.print_log('ico_url: {}'.format(ico_url))
# 等待1s
time.sleep(1)
try:
ico_response = requests.get(ico_url, headers={
'host': domain_name,
'user-agent': 'YakPanel',
}, verify=False, timeout=15)
if ico_response.status_code == 200 and ico_response.headers.get('Content-Type','').lower() == 'image/x-icon':
ico_content = ico_response.content
with open(stored_ico_path, 'wb') as f:
f.write(ico_content)
# public.print_log('Successfully fetched favicon.ico from {}'.format(ico_url))
break
except:
pass
# 等待500ms
time.sleep(1)
max_redirects = 2
for _ in range(max_redirects):
# 无法获取favicon.ico尝试从首页中获取
response = requests.get(url, headers={
'host': domain_name,
'user-agent': 'YakPanel',
}, verify=False, timeout=15)
# 检查是否重定向
if response.status_code >= 300 and response.status_code < 400:
new_url = response.headers.get('location')
# 从new_url中提取域名和端口
if new_url:
# public.print_log('Redirected to: {}'.format(new_url))
parsed_url = requests.utils.urlparse(new_url)
domain_name = parsed_url.netloc.split(':')[0]
continue
# 等待500ms
time.sleep(0.5)
continue
break
if response.status_code == 200:
# 尝试从首页中获取favicon.ico
m = reg_obj.search(response.text)
# public.print_log('matched ico_url: {}'.format(m.group(1) if m else 'None'))
if m:
ico_url = m.group(1)
headers = {
'user-agent': 'YakPanel',
}
if not ico_url.startswith('http'):
headers['host'] = domain_name
# 如果favicon.ico是相对路径拼接完整URL
if ico_url.startswith('//') and ico_url[2:].startswith(domain_name):
ico_url = url + ico_url[2:].split('/', 2)[-1]
else:
ico_url = url + ico_url.lstrip('/')
# 等待500ms
time.sleep(0.5)
try:
ico_response = requests.get(ico_url, headers=headers, verify=False, timeout=15)
if ico_response.status_code == 200:
ico_content = ico_response.content
# 校验图片格式
is_valid = False
# 检查是否为.ico
if len(ico_content) >= 4 and ico_content[:4] == b'\x00\x00\x01\x00':
is_valid = True
# 检查是否为PNG
elif len(ico_content) >= 8 and ico_content[:8] == b'\x89PNG\r\n\x1a\n':
is_valid = True
# 检查是否为JPG
elif len(ico_content) >= 3 and ico_content[:3] == b'\xff\xd8\xff':
is_valid = True
# 检查是否为GIF
elif len(ico_content) >= 6 and (
ico_content[:6] == b'GIF87a' or ico_content[:6] == b'GIF89a'):
is_valid = True
if is_valid:
with open(stored_ico_path, 'wb') as f:
f.write(ico_content)
break
except requests.RequestException as e:
public.print_log("Error fetching favicon from {}: {}".format(ico_url, str(e)),
_level='error')
break # 成功获取favicon.ico后跳出循环
except requests.RequestException as e:
public.print_log("Error fetching favicon for {}: {}".format(domain_name, str(e)),
_level='error')
if not os.path.exists(ico_path):
# 如果仍然没有favicon.ico尝试从存储的favicon中读取
if os.path.exists(stored_ico_path):
ico_path = stored_ico_path
if os.path.exists(ico_path):
try:
with open(ico_path, 'rb') as f:
ico_content = f.read()
base64_ico = "data:image/x-icon;base64," + base64.b64encode(ico_content).decode('utf-8')
public.writeFile(os.path.join(site_favs_root, site_name + '.b64'), base64_ico)
except Exception as e:
public.print_log("Error storing favicon for {}: {}".format(site_name, str(e)), _level='error')
cache.set('last_find_stored_favicons_time', cur_time, timeout=3600 * 12)
except Exception as e:
public.print_log(str(e))
# 获取wp类型
def get_wp_classification(self, get=None):
data = public.M("wp_site_types").select()
return public.return_message(0, 0, data)
# 获取wp网站列表get_wp_site_list
def get_wp_site_list(self, get=None):
try:
# 检测类型表,是否正确
import one_key_wp_v2
ok, msg = one_key_wp_v2.checklist_fields()
# 未安装wp toolkit时
if not ok and msg == "no such table: wordpress_onekey":
return public.return_message(0, 0, "")
elif not ok:
return public.return_message(-1, 0, msg)
# 校验参数
try:
p = int(get.get('p', 1))
limit = int(get.get('limit', 100))
search = get.get('search', '').strip()
site_type = get.get('site_type', '').strip()
order = get.get('order', 'addtime DESC')
table = get.get('table', '')
if not isinstance(p, int) or p < 1:
p = 1
if not isinstance(limit, int) or limit < 1:
limit = 100
except Exception as e:
return public.return_message(-1, 0, str(e))
# wp页域名查询
if table == 'domain':
data = self.GetSql(get)
return public.return_message(0, 0, data)
# wp部署类型
import one_key_wp_v2
one_key_wp_obj = one_key_wp_v2.one_key_wp()
# 查询网站列表
query = public.S('sites').prefix('').where('project_type = ?', 'WP2').alias('s')
# 处理搜索条件
if search:
query = query.where('s.name LIKE ? or s.ps LIKE ?', (f'%{search}%',f'%{search}%'))
# # 获取网站类型
if site_type:
query = query.join(
'wordpress_onekey wok',
's.id = wok.s_id',
'INNER'
).join(
'wp_site_types wst',
'wok.site_type = wst.name',
'INNER',
).where('wst.id = ?',site_type)
# 检查wordpress_backups表是否存在
has_backup_table = True
try:
has_backup_table = public.S('wordpress_backups').exists()
except Exception as e:
has_backup_table = False
# 关联备份表(仅当表存在时)
if has_backup_table:
query = query.left_join(
'wordpress_backups wb',
's.id = wb.s_id',
)
# 筛选字段并添加备份统计字段
query = query.field(
's.id',
's.name',
's.path',
's.status',
's.addtime',
's.ps',
'edate',
'project_type',
'service_type',
'COALESCE(COUNT(wb.id), 0) AS backup_count',
'MAX(wb.bak_time) AS last_backup_time'
).group('s.id')
else:
# 表不存在时,不查询备份相关字段
query = query.field(
's.id',
's.name',
's.path',
's.status',
's.addtime',
's.ps',
'edate',
'project_type',
'service_type'
)
# 排序分页
order_list = order.split(' ')
# ssl排序特殊处理后面排序
if order_list[0] != 'site_ssl':
data = query.order(order_list[0],order_list[1]).select()
else:
data = query.select()
total = len(data)
data = data[(p - 1) * limit:p * limit]
# 加载网站图标
multi_webservice_status = public.get_multi_webservice_status()
webservice = public.get_webserver()
nginx_b64_path = public.image_to_base64(os.path.join(public.get_panel_path(), "YakPanel/static/img/soft_ico/ico-nginx.png"))
apache_b64_path = public.image_to_base64(os.path.join(public.get_panel_path(), "YakPanel/static/img/apache.png"))
ols_b64_path = public.image_to_base64(os.path.join(public.get_panel_path(), "YakPanel/static/img/soft_ico/ico-openlitespeed.png"))
# 处理网站流量排序
re_data = None
if get.get('re_order'):
re_data = self.get_site_request(public.to_dict_obj({'site_type': 'WP2'}))
if re_data['status'] == 0:
re_data = re_data['message']
# 补充字段
for item in data:
if re_data:
if item['name'] in re_data:
item['re_total'] = re_data[item['name']]['total']['request']
else:
item['re_total'] = 0
# 处理备份时间
if item.get('last_backup_time', None):
try:
item['last_backup_time'] = public.format_date("%Y-%m-%d %H:%M:%S", item['last_backup_time'])
except:
item['last_backup_time'] = ''
else:
item['last_backup_time'] = ''
# 维护模式状态
item['maintenance'] = False
path = os.path.join(item['path'], '.maintenance')
if os.path.exists(path):
item['maintenance'] = True
# 添加ssl字段
ssl_info = self.get_site_ssl_info(item['name'])
item['ssl'] = ssl_info
item['site_ssl'] = ssl_info['endtime'] if ssl_info != -1 else -1
# domain
# item['domain'] = public.S('domain').where("pid=?", (item['id'],)).count()
# PHP版本信息
item['php_version'] = self.get_php_version(item['name'])
# wp版本与缓存改用v2版本
try:
item['cache_status'] = one_key_wp_obj.get_cache_status(item['id'])
item['wp_version'] = one_key_wp_obj.get_wp_version(item['id'])
except Exception as e:
item['cache_status'] = False
item['wp_version'] = '0.0.0'
# 登录url
item['login_url'] = '/v2/wp/login/{}'.format(item['id'])
# 站点图标(用于网站多服务区分)
ico_b64_path = os.path.join(public.get_panel_path(), "data/site_favs", item['name'] + ".b64") # 用户自主图标
default = public.readFile(ico_b64_path) if os.path.exists(ico_b64_path) else ''
if multi_webservice_status:
if default:
item['ico'] = default
elif 'apache' == item['service_type']:
item['ico'] = apache_b64_path
elif 'nginx' == item['service_type'] or not item['service_type']:
item['ico'] = nginx_b64_path
elif 'openlitespeed' == item['service_type']:
item['ico'] = ols_b64_path
else:
item['ico'] = ''
else:
if default:
item['ico'] = default
elif webservice == 'nginx':
item['ico'] = nginx_b64_path
elif webservice == 'apache':
item['ico'] = apache_b64_path
elif webservice == 'openlitespeed':
item['ico'] = ols_b64_path
else:
item['ico'] = ''
# 处理ssl排序
if order_list[0] == 'site_ssl':
if order_list[1] == "asc":
data = sorted(data, key=lambda x: x[order_list[0]])
else:
data = sorted(data, key=lambda x: x[order_list[0]], reverse=True)
# 处理网站流量排序
if get.get('re_order') and re_data:
if get.get('re_order') == 'desc':
data = sorted(data, key=lambda x: x["re_total"], reverse=True)
else:
data = sorted(data, key=lambda x: x["re_total"])
res = {
'data': data,
'total': total
}
return public.return_message(0, 0, res)
except Exception as e:
return public.return_message(-1, 0, str(e))
# 获取aacloud数据一次性返回
def get_aacloud_data(self,get):
webname = public.GetConfigValue("title")
version = ''
conf_path = os.path.join(public.get_panel_path(), 'class/common.py')
try:
data = public.readFile(conf_path)
match = re.search(r"g\.version\s*=\s*['\"](.*?)['\"]", data)
if match:
version = match.group(1)
except:
pass
return {'webname' : webname,'version': version}