# coding: utf-8 # ------------------------------------------------------------------- # aapanel # ------------------------------------------------------------------- # Copyright (c) 2015-2099 aapanel(http://www.aapanel.com) All rights reserved. # ------------------------------------------------------------------- import json import os import sys import time import warnings from typing import Dict, Any if "/www/server/panel/class" not in sys.path: sys.path.insert(0, "/www/server/panel/class") if "/www/server/panel/class_v2" not in sys.path: sys.path.insert(0, "/www/server/panel/class_v2") if "/www/server/panel" not in sys.path: sys.path.insert(0, "/www/server/panel") import public from public.exceptions import HintException import db_mysql from mod.project.backup_restore.modules.soft_module import SoftModule warnings.filterwarnings("ignore", category=SyntaxWarning) class DataManager(SoftModule): def __init__(self): super().__init__() self._init_db_connections() def _init_db_connections(self): """初始化数据库连接""" self.mysql_obj = db_mysql.panelMysql() def _get_current_site_name_by_pid(self, pid: int) -> str: p_site = public.M('sites').where('id=?', (pid,)).field('id,name').find() if p_site and not isinstance(p_site, str): return p_site.get('name', '') return '' def _get_current_pid_by_site_name(self, site_name: str) -> int: if not site_name: return 0 pid = 0 p_site = public.M('sites').where('name=?', (site_name,)).field('id').find() if p_site and not isinstance(p_site, str): pid = p_site.get('id', 0) return pid def get_data_list(self) -> dict: """获取所有数据列表""" def total_size(target: list | dict, keyword: str) -> int: if isinstance(target, list): res = 0 for t in target: if not (isinstance(t, dict) and keyword in t): continue try: res += int(t[keyword]) except: continue return res elif isinstance(target, dict) and keyword in target: try: return int(target[keyword]) except: return 0 return 0 # 先获取所有数据列表 soft_data = self.get_soft_list() site_list = self.get_site_list() wp_list = self.get_wp_tools_list() database_list = self.get_database_list() ssh_info = self.get_ssh_dict() plugin_list = self.get_plugin_list() vmail_info = self.get_vmail_info() # 计算所有数据的总大小 data_sources = [ (site_list, "size"), # 累加网站大小 (wp_list, "size"), # 累加WP工具大小 (database_list, "size"), # 累加数据库大小 (ssh_info, "ssh_size"), # 累加SSH大小 (ssh_info, "command_size"), # 累加commnd大小 (vmail_info, "size"), # 累加mail大小 (plugin_list, "size"), # 累加插件大小 ] disk_use = sum( map(lambda x: total_size(x[0], x[1]), data_sources) ) # 返回所有数据 return { "disk_free": self.get_free_space()['free_space'], "disk_use": disk_use, "oss_list": self.get_oss_list(), "soft_data": soft_data, "site_list": site_list, "wp_list": wp_list, "ssl_info": { "ssl_list": self.get_domian_ssl_list(), "provider_list": self.get_domian_provider_list(), }, "database_list": database_list, "ftp_list": self.get_ftp_list(), "ssh_info": ssh_info, "crontab_list": self.get_crontab_list(), "firewall_info": self.get_firewall_list(), "plugin_list": plugin_list, "vmail_info": vmail_info } def get_oss_list(self): data = [] configured = [] not_configured = [] tmp = public.readFile('data/libList.conf') if not tmp: return data try: libs = json.loads(tmp) except: public.print_log("Failed to read libList.conf file, probably because the file format is incorrect") libs = [] for lib in libs: if not 'opt' in lib: continue filename = 'plugin/{}'.format(lib['opt']) if not os.path.exists(filename): continue else: plugin_path = '/www/server/panel/plugin/{}/aes_status'.format(lib['opt']) status = 0 # 默认值为0,表示未配置 if os.path.exists(plugin_path): with open(plugin_path, 'r') as f: status_content = f.read().strip() if status_content.lower() == 'true': status = 1 # 如果 aes_status 文件内容为 'True' 则设置为1 # todo 所有oss插件认证后aes_status=1 # aws3 elif public.readFile('/www/server/panel/plugin/{}/config.conf'.format(lib['opt'])): status = 1 # ftp, 未实现断点续传 elif public.readFile("/www/server/panel/plugin/{}/ftp.config.conf".format(lib['opt'])): status = 1 if lib['opt'] == "msonedrive": status = 1 tmp = { 'name': lib['name'], 'value': lib['opt'], 'status': status, } if status == 1: configured.append(tmp) # else: # not_configured.append(tmp) # 未配置的暂时不显示 # 先添加已配置的,再添加未配置的 data.extend(configured) # data.extend(not_configured) return data def get_soft_list(self): soft_data = self.get_soft_data() simplified_soft_list = [] # 处理Web服务器 if 'web_server' in soft_data and soft_data['web_server']: simplified_soft_list.append({ 'name': soft_data['web_server'].get('name', ''), 'version': soft_data['web_server'].get('version', ''), 'size': soft_data['web_server'].get('size', 0) }) # 处理PHP版本 if 'php_server' in soft_data and soft_data['php_server']: for php in soft_data['php_server']: simplified_soft_list.append({ 'name': php.get('name', ''), 'version': php.get('version', ''), 'size': php.get('size', 0) }) # 处理MySQL服务器 if 'mysql_server' in soft_data and soft_data['mysql_server']: simplified_soft_list.append({ 'name': soft_data['mysql_server'].get('type', 'mysql'), 'version': soft_data['mysql_server'].get('version', ''), 'size': soft_data['mysql_server'].get('size', 0) }) # 处理FTP服务器 if 'ftp_server' in soft_data and soft_data['ftp_server']: simplified_soft_list.append({ 'name': soft_data['ftp_server'].get('name', 'ftp'), 'version': soft_data['ftp_server'].get('version', '1.0.47'), 'size': soft_data['ftp_server'].get('size', 0) }) # 处理JDK列表 if 'jdk_list' in soft_data and soft_data['jdk_list']: for jdk in soft_data['jdk_list']: if isinstance(jdk, dict): simplified_soft_list.append({ 'name': jdk.get('name', 'jdk'), 'version': jdk.get('version', ''), 'size': jdk.get('size', 0) }) else: simplified_soft_list.append({ 'name': 'jdk', 'version': jdk, 'size': 0 }) # 处理Node.js列表 if 'node_list' in soft_data and soft_data['node_list']: for node in soft_data['node_list']: simplified_soft_list.append({ 'name': node.get('name', ''), 'version': node.get('version', ''), 'size': node.get('size', 0) }) # 处理Golang if 'golang_list' in soft_data and soft_data['golang_list']: for golang in soft_data['golang_list'] or []: if isinstance(golang, dict): simplified_soft_list.append({ 'name': golang.get('name', 'golang'), 'version': golang.get('version', ''), 'size': golang.get('size', 0) }) else: simplified_soft_list.append({ 'name': 'golang', 'version': golang, 'size': 0 }) # 处理Tomcat if 'tomcat_list' in soft_data and soft_data['tomcat_list']: for tomcat in soft_data['tomcat_list'] or []: if isinstance(tomcat, dict): simplified_soft_list.append({ 'name': tomcat.get('name', 'tomcat'), 'version': tomcat.get('version', ''), 'size': tomcat.get('size', 0) }) else: simplified_soft_list.append({ 'name': 'tomcat', 'version': tomcat, 'size': 0 }) # 处理Redis服务器 if 'redis_server' in soft_data and soft_data['redis_server']: if isinstance(soft_data['redis_server'], dict): simplified_soft_list.append({ 'name': soft_data['redis_server'].get('name', 'redis'), 'version': soft_data['redis_server'].get('version', ''), 'size': soft_data['redis_server'].get('size', 0) }) else: simplified_soft_list.append({ 'name': 'redis', 'version': soft_data['redis_server'], 'size': 0 }) # 处理Memcached服务器 if 'memcached_server' in soft_data and soft_data['memcached_server']: if isinstance(soft_data['memcached_server'], dict): simplified_soft_list.append({ 'name': soft_data['memcached_server'].get('name', 'memcached'), 'version': soft_data['memcached_server'].get('version', '1.6.12'), 'size': soft_data['memcached_server'].get('size', 0) }) else: simplified_soft_list.append({ 'name': 'memcached', 'version': '1.16' if not isinstance(soft_data['memcached_server'], str) else soft_data[ 'memcached_server'], 'size': 0 }) # 处理MongoDB服务器 if 'mongodb_server' in soft_data and soft_data['mongodb_server']: if isinstance(soft_data['mongodb_server'], dict): simplified_soft_list.append({ 'name': soft_data['mongodb_server'].get('name', 'mongodb'), 'version': soft_data['mongodb_server'].get('version', ''), 'size': soft_data['mongodb_server'].get('size', 0) }) else: simplified_soft_list.append({ 'name': 'mongodb', 'version': soft_data['mongodb_server'], 'size': 0 }) # 处理PostgreSQL服务器 if 'pgsql_server' in soft_data and soft_data['pgsql_server']: if isinstance(soft_data['pgsql_server'], dict): simplified_soft_list.append({ 'name': soft_data['pgsql_server'].get('name', 'pgsql'), 'version': soft_data['pgsql_server'].get('version', ''), 'size': soft_data['pgsql_server'].get('size', 0) }) else: simplified_soft_list.append({ 'name': 'pgsql', 'version': soft_data['pgsql_server'], 'size': 0 }) # 处理phpMyAdmin if 'phpmyadmin_version' in soft_data and soft_data['phpmyadmin_version']: if isinstance(soft_data['phpmyadmin_version'], dict): simplified_soft_list.append({ 'name': soft_data['phpmyadmin_version'].get('name', 'phpmyadmin'), 'version': soft_data['phpmyadmin_version'].get('version', ''), 'size': soft_data['phpmyadmin_version'].get('size', 0) }) else: simplified_soft_list.append({ 'name': 'phpmyadmin', 'version': soft_data['phpmyadmin_version'], 'size': 0 }) return simplified_soft_list def get_site_list(self) -> list: """获取网站列表""" result = [] try: # todo 移除node项目, 项目文件有问题 data = public.M('sites').field('name,path,project_type,id,ps').where( "project_type != ? AND project_type != ?", ("WP2", "Node") ).select() if isinstance(data, str): return [] for site in data: try: path_size = public.ExecShell(f"du -sb {site['path']}")[0].split("\t")[0] except: path_size = 0 site_data = { "name": site["name"], "id": site["id"], "ps": site["ps"], "size": path_size, "type": site["project_type"] } result.append(site_data) return result except Exception as e: public.print_log(f"Failed to get site list: {str(e)}") return [] def get_wp_tools_list(self) -> list: """获取网站列表""" result = [] try: data = public.M('sites').field('name,path,project_type,id,ps').where( "project_type = ?", "WP2" ).select() if isinstance(data, str): return [] for site in data: try: path_size = public.ExecShell(f"du -sb {site['path']}")[0].split("\t")[0] except: path_size = 0 site_data = { "name": site["name"], "id": site["id"], "ps": site["ps"], "size": path_size, "type": site["project_type"] } result.append(site_data) return result except Exception as e: public.print_log(f"Failed to get site list: {str(e)}") return [] def get_domian_ssl_list(self) -> list: """获取SSL证书列表""" try: data = public.S("dns_domain_ssl").field("id", "subject", "info").select() if isinstance(data, str): return [] result = [] for i in data: try: info = json.loads(i.get("info", "{}")) name = info.get("issuer_O", "unknown") except: name = "unknown" result.append({ "name": name, "id": i['id'], "ps": i['subject'], "size": 0, }) return result except Exception as e: public.print_log(f"Failed to get SSL certificate list: {str(e)}") return [] def get_domian_provider_list(self) -> list: """获取SSL证书提供商列表""" try: data = public.S("dns_domain_provider").field( "id", "name", "api_user", "domains", "alias" ).select() if isinstance(data, str): return [] result = [ { "name": x.get("alias"), "id": x.get("id"), "ps": x.get("name"), "size": 0, } for x in data ] return result except Exception as e: public.print_log(f"Failed to get SSL certificate provider list: {str(e)}") return [] def get_database_list(self) -> list: """获取db数据库列表""" result: list = [] try: data = public.M('databases').field('name,type,id,sid,ps').select() for i in data: db_size = 0 db_name = None if i['type'].lower() in ['mysql'] and i['sid'] == 0: try: db_name = i['name'] query = ( "SELECT SUM(data_length + index_length) " "FROM information_schema.TABLES " f"WHERE table_schema = '{db_name}' AND table_type = 'BASE TABLE'" ) query_res = self.mysql_obj.query(query) if query_res and query_res[0] and query_res[0][0] is not None: db_size = int(query_res[0][0]) else: # 回退到文件大小计算 db_size = self.get_file_size(f"/www/server/data/{db_name}") except: db_size = self.get_file_size("/www/server/data/{}".format(db_name)) elif i['type'].lower() in ['pgsql'] and i['sid'] == 0: # todo pgsql get size pass elif i['type'].lower() in ['mongodb'] and i['sid'] == 0: # todo mg get size pass db_data = { "name": i['name'], "id": i['id'], "ps": i['ps'], "type": i['type'], "size": db_size } result.append(db_data) # 单独获取redis数据 if os.path.exists("/www/server/redis/src/redis-server"): if os.path.exists("/www/server/redis/dump.rdb"): redis_size = self.get_file_size("/www/server/redis/dump.rdb") elif os.path.exists("/www/server/redis/appendonly.aof"): redis_size = self.get_file_size("/www/server/redis/appendonly.aof") else: redis_size = 0 redis_data = { "name": "redis", "id": 0, "ps": "redis", "type": "redis", "size": redis_size } result.append(redis_data) return result except Exception as e: public.print_log("Failed to get database list: {}".format(str(e))) return [] def get_ftp_list(self) -> list: """获取FTP列表""" try: data = public.M('ftps').field('name,id').select() if isinstance(data, str): return [] return [{"name": i['name'], "id": i['id']} for i in data] except Exception as e: public.print_log("Failed to get FTP list: {}".format(str(e))) return [] def get_ssh_dict(self) -> dict: """获取SSH列表""" result = {} try: ssh_path = "/www/server/panel/config/ssh_info" ssh_user_command_file = "/www/server/panel/config/ssh_info/user_command.json" path_size = self.get_file_size(ssh_path) command_size = self.get_file_size(ssh_user_command_file) result['ssh_size'] = int(path_size) - int(command_size) result['command_size'] = int(command_size) return result except Exception as e: public.print_log("Failed to get SSH list: {}".format(str(e))) return {'ssh_size': 0, 'command_size': 0} def get_crontab_list(self) -> list: try: data = public.M('crontab').field('name,id').select() if isinstance(data, str): return [] return [{"name": i['name'], "id": i['id']} for i in data] except Exception as e: public.print_log("Failed to get scheduled task list: {}".format(str(e))) return [] def get_firewall_list(self) -> dict: result = {} try: result['firewall_ip'] = public.M('firewall_ip').count() result['firewall_conutry'] = public.M('firewall_country').count() result['firewall_new'] = public.M('firewall_new').count() result['firewall_forward'] = public.M('firewall_forward').count() # result['firewall_domain'] = public.M('firewall_domain').count() # result['firewall_malicious_ip'] = public.M('firewall_malicious_ip').count() return result except Exception as e: public.print_log("Failed to get firewall list: {}".format(str(e))) return {} def get_plugin_list(self) -> list: plugin_list = ['btwaf', 'syssafe', 'monitor', 'tamper_core'] result = [] plugin_name = None for plugin in plugin_list: if os.path.exists("/www/server/panel/plugin/{}".format(plugin)): if plugin == "btwaf": plugin_name = "btwaf" elif plugin == "syssafe": plugin_name = "syssafe" elif plugin == "monitor": plugin_name = "monitor" elif plugin == "tamper_core": plugin_name = "tamper_core" result.append({ "name": plugin_name, "size": self.get_file_size("/www/server/panel/plugin/{}".format(plugin)) }) return result def get_vmail_info(self) -> dict: result = {} if os.path.exists("/www/vmail"): result = { "name": "vmail data", "size": self.get_file_size("/www/vmail") } return result def get_web_status(self) -> dict: """获取Web服务器状态""" result = {} try: result['web'] = public.get_webserver() if result['web'] == "nginx": conf_result = public.ExecShell('ulimit -n 8192;nginx -t') if 'successful' not in conf_result[1]: result['status'] = "err" result['err'] = conf_result[1] else: result['status'] = "ok" return result except Exception as e: public.print_log("Failed to get web status: {}".format(str(e))) return {'web': None, 'status': str(e)} def get_free_space(self) -> dict: """获取可用空间""" try: path = "/www" diskstat = os.statvfs(path) free_space = diskstat.f_bavail * diskstat.f_frsize return {'free_space': free_space} except Exception as e: public.print_log("Failed to get free space: {}".format(str(e))) return {'free_space': 0} def get_server_config(self) -> dict: """获取服务器配置信息""" result = {} try: # 获取Web服务器信息 result['webserver'] = {} webserver = None if os.path.exists("/www/server/nginx/sbin/nginx"): webserver = "nginx" elif os.path.exists("/www/server/apache/bin/httpd"): webserver = "apache" result['webserver']['name'] = webserver result['webserver']['status'] = None # 获取PHP信息 result['php'] = {} php_dir = "/www/server/php" for dir_name in os.listdir(php_dir): dir_path = os.path.join(php_dir, dir_name) if os.path.isdir(dir_path) and os.path.exists(os.path.join(dir_path, 'bin/php')): php_ext = public.ExecShell(f"/www/server/php/{dir_name}/bin/php -m")[0].split("\n") filtered_data = [item for item in php_ext if item not in ('[PHP Modules]', '[Zend Modules]', '')] result['php'][dir_name] = { 'status': None, 'php_ext': filtered_data } # 获取MySQL信息 result['mysql'] = {} if os.path.exists("/www/server/mysql/bin/mysql"): # 添加MySQL相关信息 pass return result except Exception as e: public.print_log("Failed to get server configuration: {}".format(str(e))) return {} @staticmethod def _generate_soft_list(backup_info: dict) -> list: soft_list = [] if "soft" in backup_info['data_list']: soft_data = backup_info["data_list"]["soft"] # 处理Web服务器 if "web_server" in soft_data and soft_data["web_server"]: soft_list.append({ "name": soft_data["web_server"].get("name", ""), "version": soft_data["web_server"].get("version", ""), "size": soft_data["web_server"].get("size", 0), "status": soft_data["web_server"].get("status", 0), }) # 处理PHP版本 if "php_server" in soft_data and soft_data["php_server"]: for php in soft_data["php_server"]: soft_list.append({ "name": php.get("name", ""), "version": php.get("version", ""), "size": php.get("size", 0), "status": php.get("status", 0), }) # 处理MySQL服务器 if "mysql_server" in soft_data and soft_data["mysql_server"]: soft_list.append({ "name": soft_data["mysql_server"].get("type", "mysql"), "version": soft_data["mysql_server"].get("version", ""), "size": soft_data["mysql_server"].get("size", 0), "status": soft_data["mysql_server"].get("status", 0), }) # 处理FTP服务器 if "ftp_server" in soft_data and soft_data["ftp_server"]: soft_list.append({ "name": soft_data["ftp_server"].get("name", ""), "version": soft_data["ftp_server"].get("version", ""), "size": soft_data["ftp_server"].get("size", 0), "status": soft_data["ftp_server"].get("status", 0), }) # 处理node服务器 if "node_list" in soft_data and soft_data["node_list"]: for node in soft_data["node_list"]: soft_list.append({ "name": node.get("name", ""), "version": node.get("version", ""), "size": node.get("size", 0), "status": node.get("status", 0), }) # 处理Redis服务器 if "redis_server" in soft_data and soft_data["redis_server"]: soft_list.append({ "name": soft_data["redis_server"].get("name", ""), "version": soft_data["redis_server"].get("version", ""), "size": soft_data["redis_server"].get("size", 0), "status": soft_data["redis_server"].get("status", 0), }) # 处理Memcached服务器 if "memcached_server" in soft_data and soft_data["memcached_server"]: soft_list.append({ "name": soft_data["memcached_server"].get("name", ""), "version": soft_data["memcached_server"].get("version", ""), "size": soft_data["memcached_server"].get("size", 0), "status": soft_data["memcached_server"].get("status", 0), }) # 处理MongoDB服务器 if "mongodb_server" in soft_data and soft_data["mongodb_server"]: soft_list.append({ "name": soft_data["mongodb_server"].get("name", ""), "version": soft_data["mongodb_server"].get("version", ""), "size": soft_data["mongodb_server"].get("size", 0), "status": soft_data["mongodb_server"].get("status", 0), }) # 处理PostgreSQL服务器 if "pgsql_server" in soft_data and soft_data["pgsql_server"]: soft_list.append({ "name": soft_data["pgsql_server"].get("name", ""), "version": soft_data["pgsql_server"].get("version", ""), "size": soft_data["pgsql_server"].get("size", 0), "status": soft_data["pgsql_server"].get("status", 0), }) # 处理phpMyAdmin if "phpmyadmin_version" in soft_data and soft_data["phpmyadmin_version"]: soft_list.append({ "name": soft_data["phpmyadmin_version"].get("name", ""), "version": soft_data["phpmyadmin_version"].get("version", ""), "size": soft_data["phpmyadmin_version"].get("size", 0), "status": soft_data["phpmyadmin_version"].get("status", 0), }) return soft_list @staticmethod def process_detail(backup_info: dict) -> dict: try: disk_use = int(backup_info.get("backup_file_size", 0)) * 2 except Exception as e: public.print_log("Error calculating disk use: {}".format(str(e))) disk_use = backup_info.get("backup_file_size", 0) # 提取基本信息 result = { "data": { "type": "backup", "done_time": backup_info.get("done_time", ""), "total_time": backup_info.get("total_time", 0), "backup_file": backup_info.get("backup_file", ""), "backup_file_size": backup_info.get("backup_file_size", "0"), "backup_file_sha256": backup_info.get("backup_file_sha256", ""), "disk_use": disk_use, "disk_free": DataManager().get_free_space()['free_space'], "data_status": { "env_list": [], "site_list": [], "wp_list": [], "ssl_info": { "ssl_list": [], "provider_list": [], }, "ftp_list": [], "database_list": [], "crontab_list": [], "plugin_list": [], "soft_data": [], "vmail_info": {}, "firewall_info": {}, "ssh_info": {}, } } } # 处理软件列表 backup_data_detail = backup_info.get("data_list") or {} if "soft" in backup_data_detail: soft_list = DataManager()._generate_soft_list(backup_info) # 添加到结果中 result["data"]["data_status"]["soft_data"] = soft_list # 添加软件数据到环境列表 result["data"]["data_status"]["env_list"] = [ { "name": soft.get("name", ""), "version": soft.get("version", ""), "size": soft.get("size", 0), "status": soft.get("status", 2), "err_msg": None } for soft in soft_list ] # 处理网站列表 if "data_list" in backup_info and "site" in backup_info["data_list"]: for site in backup_info["data_list"].get("site", []): s_info = { "id": site.get("id"), "name": site.get("name", ""), "type": site.get("project_type", ""), "size": site.get("size", 0), "status": site.get("restore_status", site.get("status", 2)), "err_msg": site.get("msg", None) } # wp if site.get("project_type", "").lower() in ["wp", "wp2"]: result["data"]["data_status"]["wp_list"].append(s_info) # php 其他类型 else: result["data"]["data_status"]["site_list"].append(s_info) # 处理SSL信息 ssl_info = backup_data_detail.get("ssl", {"ssl_list": [], "provider_list": []}) for ssl in ssl_info.get("ssl_list", []): result["data"]["data_status"]["ssl_info"]["ssl_list"].append({ "id": ssl.get("id"), "name": ", ".join(ssl.get("dns", [])), "type": ssl["info"].get("issuer_O", ""), "size": ssl.get("size", 0), "status": ssl.get("restore_status", ssl.get("status", 2)), "err_msg": ssl.get("msg", None) }) for p in ssl_info.get("provider_list", []): result["data"]["data_status"]["ssl_info"]["provider_list"].append({ "id": p.get("id"), "name": p.get("alias", ""), "type": p.get("name", ""), "size": p.get("size", 0), "status": p.get("restore_status", p.get("status", 0)), "err_msg": p.get("msg", None) }) # 处理数据库列表 if "data_list" in backup_info and "database" in backup_info["data_list"]: result["data"]["data_status"]["database_list"] = [ { "id": db.get("id"), "name": db.get("name", ""), "type": db.get("type", ""), "size": db.get("size", 0), "status": db.get("restore_status", db.get("status", 0)), "err_msg": db.get("msg", None) } for db in backup_info["data_list"]["database"] ] # 处理FTP列表 if "data_list" in backup_info and "ftp" in backup_info["data_list"]: result["data"]["data_status"]["ftp_list"] = [ { "id": ftp.get("id"), "name": ftp.get("name", ""), "size": ftp.get("size", 0), "status": ftp.get("restore_status", ftp.get("status", 0)), "err_msg": ftp.get("msg", None) } for ftp in backup_info["data_list"]["ftp"] ] # 处理计划任务 if "data_list" in backup_info and "crontab" in backup_info["data_list"]: crontab_data = backup_info["data_list"]["crontab"] crontab_list = [] try: if crontab_data.get("crontab_json") and os.path.exists(crontab_data["crontab_json"]): cron_json_info = json.loads(public.ReadFile(crontab_data["crontab_json"])) if cron_json_info: crontab_list = [ { "id": crontab.get("id", 0), "name": crontab.get("name", ""), "size": crontab.get("id", 0), "status": 2, "err_msg": None } for crontab in cron_json_info ] except Exception as e: public.print_log("Error reading crontab JSON: {}".format(str(e))) result["data"]["data_status"]["crontab_list"] = crontab_list # 处理SSH列表 if "data_list" in backup_info and "ssh" in backup_info["data_list"]: ssh_data = backup_info["data_list"]["ssh"] ssh_size = 0 command_size = 0 try: if ssh_data.get("ssh_info_path") and os.path.exists(ssh_data["ssh_info_path"] + "/ssh_info"): real_path = os.path.join(ssh_data["ssh_info_path"], "ssh_info") for i in os.listdir(real_path): if i != "127.0.0.1" and i != "localhost": ssh_size += 1 if i == "user_command.json" and os.path.isfile(real_path + "/" + i): command_info = public.ReadFile(os.path.join(real_path, i)) if command_info: try: command_info = json.loads(command_info) except: command_info = [] command_size = len(command_info) except Exception as e: public.print_log("Error reading SSH info files: {}".format(str(e))) ssh_info: Dict[str, Any] = { "ssh_size": ssh_size, "command_size": command_size, "status": 2, "err_msg": None } result["data"]["data_status"]["ssh_info"] = ssh_info # 处理防火墙列表 if "data_list" in backup_info and "firewall" in backup_info["data_list"]: firewall_data = backup_info["data_list"]["firewall"] prot_list, ip_list, forward_list, contry_list = [], [], [], [] try: prot_data = public.ReadFile(firewall_data.get("port_data_path", "")) prot_list = json.loads(prot_data) except: pass try: ip_data = public.ReadFile(firewall_data.get("ip_data_path", "")) ip_list = json.loads(ip_data) except: pass try: forward_data = public.ReadFile(firewall_data.get("forward_data_path", "")) forward_list = json.loads(forward_data) except: pass try: contry_data = public.ReadFile(firewall_data.get("country_data_path", "")) if contry_data: contry_data = contry_data.strip() if not contry_data: contry_list = [] else: try: contry_json = json.loads(contry_data) if isinstance(contry_json, list): contry_list = contry_json elif isinstance(contry_json, dict): contry_list = list(contry_json.values()) else: contry_list = [] except: contry_list = [line for line in contry_data.splitlines() if line.strip()] except: pass firewall_info: Dict[str, Any] = { "firewall_new": len(prot_list), "firewall_ip": len(ip_list), "firewall_forward": len(forward_list), "firewall_conutry": len(contry_list), "status": firewall_data.get("restore_status", firewall_data.get("status", 2)), "err_msg": firewall_data.get("err_msg", None), } result["data"]["data_status"]["firewall_info"] = firewall_info # 处理插件列表 if "data_list" in backup_info and "plugin" in backup_info["data_list"]: plugin_data = backup_info["data_list"]["plugin"] # 检查插件数据格式 if isinstance(plugin_data, dict): # 新格式:{"plugin_name": {"status": x, "err_msg": y}} result["data"]["data_status"]["plugin_list"] = [ { "name": plugin_name, "display_name": plugin_name, "size": plugin_info.get("size", 0), "status": plugin_info.get("restore_status", plugin_info.get("status", 2)), "err_msg": plugin_info.get("err_msg", None) } for plugin_name, plugin_info in plugin_data.items() ] else: # 旧格式:[{"name": x, "size": y}] result["data"]["data_status"]["plugin_list"] = [ { "name": plugin.get("name", ""), "display_name": plugin.get("name", ""), "size": plugin.get("size", 0), "status": plugin.get("restore_status", 2), "err_msg": None } for plugin in plugin_data ] # 处理邮局数据 if "data_list" in backup_info and "vmail" in backup_info["data_list"]: vmail_data = backup_info["data_list"]["vmail"] vmail_info: Dict[str, Any] = { "name": "vmail", "size": vmail_data.get("size", 0), "status": 2, "err_msg": None } result["data"]["data_status"]["vmail_info"] = vmail_info return result["data"] def get_progress_with_type(self, my_type: str) -> dict: """获取备份进度""" def create_completed_result(backup_timestamp): if not backup_timestamp: return public.ReturnMsg(False, public.lang("Backup completed but unable to retrieve timestamp")) if not os.path.exists(self.bakcup_task_json): return public.ReturnMsg(False, public.lang("Backup configuration file does not exist")) backup_configs = json.loads(public.ReadFile(self.bakcup_task_json)) success_data = next( (item for item in backup_configs if str(item.get('timestamp')) == str(backup_timestamp)), {} ) return { "task_type": my_type, "task_status": 2, "backup_data": None, "backup_name": None, "data_backup_status": 2, "progress": 100, "msg": None, 'exec_log': public.ReadFile(log_file) if os.path.exists(log_file) else "", 'timestamp': backup_timestamp, 'backup_file_info': success_data, 'err_info': [] } if my_type not in ["backup", "restore"]: raise HintException("Invalid type. Must be 'backup' or 'restore'.") pl_file = self.base_path + f'/{my_type}.pl' log_file = self.base_path + f'/{my_type}.log' if my_type == "backup": success_file = self.base_path + f'/success.pl' else: success_file = self.base_path + f'/restore_success.pl' def read_log_data(): return public.ReadFile(log_file) if os.path.exists(log_file) else "" def create_initial_result(task_timestamp=None): init_state = self.get_initial_task_state(my_type) if task_timestamp is None and init_state: task_timestamp = init_state.get("timestamp") return public.ReturnMsg( True, self.build_initial_task_progress(my_type, task_timestamp, read_log_data()) ) count = 0 while 1: count += 1 if count >= 10: raise HintException(public.lang(f"{my_type} progress file not found or empty, please try again.")) if not os.path.exists(pl_file): if os.path.exists(success_file): success_time = int(os.path.getctime(success_file)) if success_time + 10 > int(time.time()): try: backup_timestamp = public.ReadFile(success_file).strip() return public.ReturnMsg(True, create_completed_result(backup_timestamp)) except Exception as e: public.ExecShell("rm -f {}".format(success_file)) raise HintException( public.lang("Error retrieving backup completion information: {}").format(str(e)) ) else: public.ExecShell("rm -f {}".format(success_file)) if self.get_initial_task_state(my_type): return create_initial_result() time.sleep(1) continue ts = (public.ReadFile(pl_file) or "").strip() if not ts: return create_initial_result() json_path = f"{self.base_path}/{ts}_backup/{my_type}.json" if not os.path.exists(json_path): return create_initial_result(ts) break ts = ts.strip() conf_data = json.loads(public.ReadFile(json_path)) log_data = read_log_data() status_key_name = "backup_status" if my_type == "backup" else "restore_status" if conf_data.get(status_key_name) == 1 and not conf_data.get("data_list"): return create_initial_result(ts) # 定义备份类型及其处理逻辑 types = [ { 'type': 'site', 'data_key': 'site', 'display_name': 'site', 'progress': 25 }, { 'type': 'database', 'data_key': 'database', 'display_name': 'database', 'progress': 50 }, { 'type': 'ssl', 'data_key': 'ssl', 'display_name': 'ssl', 'progress': 60 }, { 'type': 'ftp', 'data_key': 'ftp', 'display_name': 'ftp', 'progress': 65 }, { 'type': 'crontab', 'data_key': 'crontab', 'display_name': 'crontab', 'progress': 72 }, { 'type': 'ssh', 'data_key': 'ssh', 'display_name': 'ssh', 'progress': 78 }, { 'type': 'firewall', 'data_key': 'firewall', 'display_name': 'firewall', 'progress': 82 }, { 'type': 'vmail', 'data_key': 'vmail', 'display_name': 'mail', 'progress': 86 }, { 'type': 'node', 'data_key': 'node', 'display_name': 'node', 'progress': 88 }, { 'type': 'plugin', 'data_key': 'plugin', 'display_name': 'plugin', 'progress': 90 }, ] current_process = 0 last_progress = 0 current_task_info = None status_key = "status" if my_type == "backup" else "restore_status" def normalize_process_items(raw_items): if isinstance(raw_items, list): return raw_items if isinstance(raw_items, dict): if "ssl_list" in raw_items or "provider_list" in raw_items: return raw_items.get("ssl_list", []) + raw_items.get("provider_list", []) if raw_items and all(isinstance(item, dict) for item in raw_items.values()): return list(raw_items.values()) return [raw_items] if raw_items else [] return [] for index, process in enumerate(types): items = normalize_process_items(conf_data.get("data_list", {}).get(process['data_key'], [])) total = len(items) if total == 0: # 认为已完成 last_progress = process['progress'] continue done_count = len([ x for x in items if isinstance(x, dict) and x.get(status_key, x.get("status")) == 2 ]) rate = done_count / total progress_range = process['progress'] - (types[index - 1]['progress'] if index > 0 else 0) current_process = round(last_progress + progress_range * rate) if done_count < total: # 如果有未完成项 for item in items: if isinstance(item, dict) and item.get(status_key, item.get("status")) != 2: current_task_info = { "task_type": my_type, "task_status": 1, "data_type": process['type'], "name": item.get("name", f"unknow {process['display_name']}"), "data_backup_status": item.get(status_key, item.get("status", 0)), "progress": current_process if current_process > 5 else 5, "msg": item.get("msg"), 'exec_log': log_data, 'timestamp': ts } break break # 当前类型全部完成 last_progress = process['progress'] if current_task_info: # 正在处理的任务,更新其进度并返回 current_task_info['progress'] = current_process if current_process > 5 else 5 return public.ReturnMsg(True, current_task_info) # 检查数据打包进度 try: backup_status = conf_data.get(status_key_name) if backup_status == 1: return public.ReturnMsg(True, { "task_type": my_type, "task_status": 1, "data_type": "tar", "name": public.lang("Data Packaging"), "data_backup_status": 1, "progress": 90, 'exec_log': log_data, 'timestamp': ts }) except Exception as e: # 可能没有backup_status字段,继续处理 pass # 如果没有发现进行中的任务,但有进程 if ts: # 如果 current_process 已经是 types 定义的最大值,说明所有数据处理完成 if current_process >= types[-1]['progress']: return public.ReturnMsg(True, { "task_type": my_type, "task_status": 1, "data_type": "tar", "name": public.lang("Data Packaging"), "data_backup_status": 1, "progress": 95, 'exec_log': log_data, 'timestamp': ts }) return public.ReturnMsg(True, { "task_type": my_type, "task_status": 1, "name": public.lang("Preparing data"), "data_backup_status": 1, "progress": current_process if current_process > 5 else 5, 'exec_log': log_data, 'timestamp': ts }) return public.ReturnMsg(False, public.lang("No ongoing tasks found. Please check")) if __name__ == '__main__': # 获取命令行参数 if len(sys.argv) < 3: print("Usage: btpython data_manager.py ") sys.exit(1) method_name = sys.argv[1] # 方法名 data_manager = DataManager() # 实例化对象 if hasattr(data_manager, method_name): # 检查方法是否存在 method = getattr(data_manager, method_name) # 获取方法 method() # 调用方法 else: print(f"Error: Method '{method_name}' does not exist")