Initial YakPanel commit

This commit is contained in:
Niranjan
2026-04-07 02:04:22 +05:30
commit 2826d3e7f3
5359 changed files with 1390724 additions and 0 deletions

View File

View File

@@ -0,0 +1,616 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <wzz@yakpanel.com>
# -------------------------------------------------------------------
import datetime
import json
import os
import re
import sys
import time
import warnings
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 mod.project.backup_restore.modules.site_module import SiteModule
from mod.project.backup_restore.modules.database_module import DatabaseModule
from mod.project.backup_restore.modules.ftp_module import FtpModule
from mod.project.backup_restore.modules.crontab_module import CrontabModule
from mod.project.backup_restore.modules.ssh_module import SshModule
from mod.project.backup_restore.modules.firewall_module import FirewallModule
from mod.project.backup_restore.modules.plugin_module import PluginModule
from mod.project.backup_restore.modules.mail_module import MailModule
from mod.project.backup_restore.modules.ssl_model import SSLModel
warnings.filterwarnings("ignore", category=SyntaxWarning)
class BackupManager(SiteModule, DatabaseModule, FtpModule, SSLModel):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.backup_log_file = self.base_path + '/backup.log'
self.backup_pl_file = self.base_path + '/backup.pl'
self.backup_success_file = self.base_path + '/success.pl'
self.backup_save_config = self.base_path + '/backup_save_config.json'
self.history_log_path = '/www/backup/backup_restore/history/log'
self.history_info_path = '/www/backup/backup_restore/history/info'
self.migrate_backup_info_path = '/www/backup/backup_restore/migrate_backup_info.json'
def get_local_backup(self, get=None):
backup_list = []
if os.path.exists(self.bakcup_task_json):
backup_list = json.loads(public.ReadFile(self.bakcup_task_json))
file_names = os.listdir(self.base_path)
pattern = re.compile(r"\d{8}-\d{4}_\d+_backup\.tar\.gz")
matched_files = [f for f in file_names if pattern.match(f)]
for file in matched_files:
if "upload.tmp" in file:
continue
file_timestamp = file.split('_')[1]
matched = any(item["timestamp"] == int(file_timestamp) for item in backup_list)
if not matched:
done_time = datetime.datetime.fromtimestamp(int(file_timestamp)).strftime('%Y-%m-%d %H:%M:%S')
# file_size = public.ExecShell("du -sb /www/backup/backup_restore/{}".format(file))[0].split("\t")[0]
file_conf = {
'backup_name': str(file),
'timestamp': int(file_timestamp),
'create_time': done_time,
'backup_time': done_time,
'backup_file': self.base_path + "/" + file,
'storage_type': "local",
'auto_exit': 0,
'restore_status': 0,
'backup_status': 2,
'backup_path': self.base_path + "/" + file,
'done_time': done_time,
'backup_file_size': str(self.get_file_size(self.base_path + "/" + file)),
'backup_file_sha256': self.get_file_sha256(self.base_path + "/" + file),
'backup_count': {
"success": None,
"failed": None,
"total_time": None
},
}
backup_list.append(file_conf)
if os.path.exists(self.migrate_backup_info_path):
migrate_backup_info = json.loads(public.ReadFile(self.migrate_backup_info_path))
backup_list.append(migrate_backup_info)
public.ExecShell("rm -f {}".format(self.migrate_backup_info_path))
public.WriteFile(self.bakcup_task_json, json.dumps(backup_list))
return backup_list
def get_backup_file_msg(self, timestamp):
import tarfile
backup_file = str(timestamp) + "_backup.tar.gz"
print(backup_file)
file_names = os.listdir(self.base_path)
for file in file_names:
if backup_file in file:
backup_file = file
path = self.base_path + "/" + backup_file
path_data = {}
if not os.path.exists(path):
return path_data
try:
with tarfile.open(path, 'r:gz') as tar:
# 提前获取文件列表
members = tar.getnames()
# 提取备份 JSON 配置
if '{}_backup/backup.json'.format(timestamp) in members:
json_file_name = '{}_backup/backup.json'.format(timestamp)
json_file = tar.extractfile(json_file_name)
json_content = json_file.read().decode('utf-8')
path_data['config'] = json.loads(json_content)
# 提取备份日志文件
if '{}_backup/backup.log'.format(timestamp) in members:
log_file_name = '{}_backup/backup.log'.format(timestamp)
log_file = tar.extractfile(log_file_name)
log_content = log_file.read().decode('utf-8')
path_data['log'] = log_content + path + "\n" + public.lang("Packaging completed")
except:
return False
# path_data['server_config']=self.get_server_config()
# path_data['backup_path_size']=25256044
# path_data['free_size'] = self.get_free_space()['free_space']
self.history_log_path = '/www/backup/backup_restore/history/log'
self.history_info_path = '/www/backup/backup_restore/history/info'
if not os.path.exists(self.history_log_path):
public.ExecShell("mkdir -p {}".format(self.history_log_path))
if not os.path.exists(self.history_info_path):
public.ExecShell("mkdir -p {}".format(self.history_info_path))
try:
public.WriteFile(self.history_log_path + "{}_backup.log".format(timestamp), path_data['log'])
except:
pass
try:
public.WriteFile(self.history_info_path + "/{}_backup.info".format(timestamp),
json.dumps(path_data['config']))
except:
return False
try:
backup_task_info = self.get_backup_conf(timestamp)
hitory_info = json.loads(public.ReadFile(self.history_info_path + "/{}_backup.info".format(timestamp)))
hitory_info['create_time'] = backup_task_info['create_time']
hitory_info['backup_time'] = backup_task_info['backup_time']
hitory_info['backup_file'] = backup_task_info['backup_file']
hitory_info['backup_path'] = backup_task_info['backup_path']
hitory_info['done_time'] = backup_task_info['done_time']
hitory_info['total_time'] = backup_task_info['total_time']
hitory_info['backup_file_size'] = backup_task_info['backup_file_size']
hitory_info['backup_file_sha256'] = backup_task_info['backup_file_sha256']
public.WriteFile(self.history_info_path + "/{}_backup.info".format(timestamp), json.dumps(hitory_info))
except:
pass
return True
def add_backup_task(self, timestamp: int):
"""
构造备份初始配置
"""
backup_path = self.base_path + '/{timestamp}_backup/'.format(timestamp=timestamp)
if not os.path.exists(backup_path):
os.makedirs(backup_path)
backup_conf = self.get_backup_conf(timestamp)
if not backup_conf:
print(public.lang("Backup configuration file does not exist"))
return public.returnMsg(False, public.lang("Backup configuration file does not exist"))
backup_conf['data_list'] = {}
backup_conf['data_list']['soft'] = self.get_soft_data(timestamp, packet=True)
backup_conf['data_list']['site'] = self.get_site_backup_conf(timestamp)
backup_conf['data_list']['ssl'] = self.get_ssl_backup_conf(timestamp)
backup_conf['data_list']['database'] = self.get_database_backup_conf(timestamp)
backup_conf['data_list']['ftp'] = self.backup_ftp_data(timestamp)
backup_conf['backup_status'] = 1
public.WriteFile(backup_path + 'backup.json', json.dumps(backup_conf))
def backup_data(self, timestamp: int):
if os.path.exists(self.backup_log_file):
public.ExecShell("rm -f {}".format(self.backup_log_file))
if os.path.exists(self.backup_pl_file):
print(public.lang("A backup process is already running!"))
return public.returnMsg(False, public.lang("A backup process is already running!"))
try:
public.WriteFile(self.backup_pl_file, timestamp)
backup_conf = self.get_backup_conf(timestamp)
backup_conf['backup_status'] = 1
self.save_backup_conf(timestamp, backup_conf)
start_time = int(time.time())
# 构造备份初始配置
self.add_backup_task(timestamp)
try:
self.backup_site_data(timestamp)
except:
pass
try:
self.backup_database_data(timestamp)
except:
pass
try:
self.backup_ssl_data(timestamp)
except:
pass
try:
CrontabModule().backup_crontab_data(timestamp)
except:
pass
# TODO: 存在问题,下个版本修复
# try:
# SshModule().backup_ssh_data(timestamp)
# except:
# pass
try:
FirewallModule().backup_firewall_data(timestamp)
except:
pass
try:
MailModule().backup_vmail_data(timestamp)
except:
pass
try:
PluginModule().backup_plugin_data(timestamp)
except:
pass
try:
self.write_backup_data(timestamp)
except:
pass
end_time = int(time.time())
done_time = datetime.datetime.fromtimestamp(int(end_time)).strftime('%Y-%m-%d %H:%M:%S')
total_time = end_time - start_time
backup_conf = self.get_backup_conf(timestamp)
backup_conf['backup_status'] = 2
backup_conf['done_time'] = done_time
backup_conf['total_time'] = total_time
self.save_backup_conf(timestamp, backup_conf)
self.sync_backup_info(timestamp)
public.WriteFile(self.backup_success_file, timestamp)
public.ExecShell("rm -f {}".format(self.backup_pl_file))
self.create_history_file(timestamp)
except Exception as e:
return public.returnMsg(False, public.lang(f"something went wrong! Error: {str(e)}"))
finally:
if os.path.exists(self.backup_pl_file):
public.ExecShell("rm -f {}".format(self.backup_pl_file))
def create_history_file(self, timestamp):
if not os.path.exists(self.history_log_path):
public.ExecShell("mkdir -p {}".format(self.history_log_path))
if not os.path.exists(self.history_info_path):
public.ExecShell("mkdir -p {}".format(self.history_info_path))
hitory_log_file = self.history_log_path + '/' + str(timestamp) + '_backup.log'
history_info_file = self.history_info_path + '/' + str(timestamp) + '_backup.info'
public.WriteFile(hitory_log_file, public.ReadFile("/www/backup/backup_restore/backup.log".format(timestamp)))
public.WriteFile(history_info_file,
public.ReadFile("/www/backup/backup_restore/{}_backup/backup.json".format(timestamp)))
def sync_backup_info(self, timestamp):
backup_conf = self.get_backup_conf(timestamp)
data_list = self.get_backup_data_list(timestamp)
data_list['backup_status'] = backup_conf['backup_status']
data_list['backup_file'] = backup_conf['backup_file']
data_list['backup_file_sha256'] = backup_conf['backup_file_sha256']
data_list['backup_file_size'] = backup_conf['backup_file_size']
data_list['done_time'] = backup_conf['done_time']
data_list['total_time'] = backup_conf['total_time']
data_list['backup_count'] = backup_conf['backup_count']
self.update_backup_data_list(timestamp, data_list)
def count_backup_status(self, data, status_code):
return sum(
1 for category in data.values()
for item in category
if isinstance(item, dict) and item.get('status') == status_code
)
def write_backup_data(self, timestamp):
self.print_log("====================================================", "backup")
self.print_log(public.lang("Start compressing and packaging all data"), "backup")
from datetime import datetime
backup_conf = self.get_backup_conf(timestamp)
backup_log_path = self.base_path + str(timestamp) + "_backup/"
public.ExecShell('\cp -rpa {} {}'.format(self.backup_log_file, backup_log_path))
conf_data = json.loads((public.ReadFile("/www/backup/backup_restore/{}_backup/backup.json".format(timestamp))))
status_2_count = self.count_backup_status(conf_data['data_list'], 2)
status_3_count = self.count_backup_status(conf_data['data_list'], 3)
dt_object = datetime.fromtimestamp(int(timestamp))
file_time = dt_object.strftime('%Y%m%d-%H%M')
tar_file_name = file_time + "_" + str(timestamp) + "_backup.tar.gz"
conf_data['backup_status'] = 1
public.WriteFile("/www/backup/backup_restore/{}_backup/backup.json".format(timestamp), json.dumps(conf_data))
public.ExecShell("cd /www/backup/backup_restore && tar -czvf {} {}_backup".format(tar_file_name, timestamp))
file_size = public.ExecShell("du -sb /www/backup/backup_restore/{}".format(tar_file_name))[0].split("\t")[0]
backup_conf["backup_status"] = 2
backup_conf["backup_file"] = "/www/backup/backup_restore/" + tar_file_name
backup_conf["backup_file_sha256"] = self.get_file_sha256("/www/backup/backup_restore/" + tar_file_name)
backup_conf["backup_file_size"] = file_size
backup_conf["backup_count"] = {}
backup_conf["backup_count"]['success'] = status_2_count
backup_conf["backup_count"]['failed'] = status_3_count
storage_type = backup_conf['storage_type']
backup_size = self.format_size(int(file_size))
self.print_log(
public.lang("Compression and packaging of all data completed. Data size: {}").format(backup_size),
'backup'
)
self.print_log(public.lang("Backup completed. Backup file: {}").format(tar_file_name), "backup")
self.print_log("====================================================", "backup")
tar_file_name = "/www/backup/backup_restore/" + tar_file_name
if storage_type != "local" and os.path.exists(tar_file_name):
cloud_name_cn = "cloud storage"
self.print_log(public.lang("Uploading backup file to cloud storage..."), "backup")
try:
from cloud_stora_upload_v2 import CloudStoraUpload
_cloud = CloudStoraUpload()
_cloud.run(storage_type)
cloud_name_cn = _cloud.obj._title
if int(file_size) > 100 * 1024 * 1024:
self.print_log(
public.lang("{} Uploading in chunks...").format(cloud_name_cn), "backup"
)
else:
self.print_log(
public.lang("{} Uploading...").format(cloud_name_cn), "backup"
)
backup_path = _cloud.obj.backup_path
if not backup_path.endswith('/'):
backup_path += '/'
upload_path = os.path.join(backup_path, "backup_restore", os.path.basename(tar_file_name))
if _cloud.cloud_upload_file(tar_file_name, upload_path):
self.print_log(public.lang("Successfully uploaded to {}").format(cloud_name_cn), "backup")
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
self.print_log(
public.lang("Error occurred while uploading to {}: {}").format(cloud_name_cn, str(e)),
"backup"
)
self.save_backup_conf(timestamp, backup_conf)
def get_backup_details(self, timestamp):
history_info_file = self.history_info_path + '/' + str(timestamp) + '_backup.info'
if not os.path.exists(history_info_file):
get_info = self.get_backup_file_msg(timestamp)
if not get_info:
return public.fail_v2(public.lang("Backup info not found"))
backup_info = json.loads(public.ReadFile(history_info_file))
result = self.process_detail(backup_info)
return public.success_v2(result)
def get_backup_log(self, timestamp):
backup_log_file = self.base_path + '/backup.log'
history_log_file = self.history_log_path + '/' + str(timestamp) + '_backup.log'
if os.path.exists(self.backup_pl_file):
backup_timestamp = int(public.ReadFile(self.backup_pl_file))
if int(backup_timestamp) == int(timestamp):
return public.ReadFile(backup_log_file)
if os.path.exists(history_log_file):
return public.ReadFile(history_log_file)
else:
return None
# todo 弃用
def get_backup_progress(self, get=None):
"""
获取备份进度信息
@param get: object 包含请求参数
@return: dict 备份进度信息
"""
# 设置相关文件路径
backup_pl_file = self.base_path + '/backup.pl'
backup_log_file = self.base_path + '/backup.log'
backup_success_file = self.base_path + '/success.pl'
# 创建处理已完成备份的函数,减少代码重复
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": "backup",
"task_status": 2,
"backup_data": None,
"backup_name": None,
"data_backup_status": 2,
"progress": 100,
"msg": None,
'exec_log': public.ReadFile(backup_log_file) if os.path.exists(backup_log_file) else "",
'timestamp': backup_timestamp,
'backup_file_info': success_data,
'err_info': []
}
# 检查备份是否已完成
if os.path.exists(backup_success_file):
success_time = int(os.path.getctime(backup_success_file))
local_time = int(time.time())
# 如果success文件创建时间在10秒内说明备份刚刚完成
if success_time + 10 > local_time:
try:
backup_timestamp = public.ReadFile(backup_success_file).strip()
return public.ReturnMsg(True, create_completed_result(backup_timestamp))
except Exception as e:
public.ExecShell("rm -f {}".format(backup_success_file))
return public.ReturnMsg(False,
public.lang("Error retrieving backup completion information: {}").format(
str(e)))
else:
# 超过10秒删除success文件
public.ExecShell("rm -f {}".format(backup_success_file))
# 检查是否有备份进程运行
try:
# 检查备份进程锁文件
if os.path.exists(backup_pl_file):
timestamp = public.ReadFile(backup_pl_file).strip()
if not timestamp:
return public.ReturnMsg(False,
public.lang("Backup process is running, but unable to retrieve timestamp"))
else:
# 等待2秒可能是备份刚刚完成
time.sleep(2)
if os.path.exists(backup_success_file):
success_time = int(os.path.getctime(backup_success_file))
local_time = int(time.time())
if success_time + 10 > local_time:
backup_timestamp = public.ReadFile(backup_success_file).strip()
return public.ReturnMsg(True, create_completed_result(backup_timestamp))
# 再次检查是否有备份进程
if os.path.exists(backup_pl_file):
timestamp = public.ReadFile(backup_pl_file).strip()
if not timestamp:
return public.ReturnMsg(False, public.lang(
"Backup process is running, but unable to retrieve timestamp"))
else:
return public.ReturnMsg(False, public.lang(
"No ongoing backup tasks found. Please check the backup list to see if the backup is completed"))
# 读取备份配置文件
backup_json_path = f"{self.base_path}/{timestamp}_backup/backup.json"
count = 0
while 1:
if count >= 3:
return public.ReturnMsg(False, public.lang("Backup configuration file does not exist: {}").format(
backup_json_path))
count += 1
if not os.path.exists(backup_json_path):
time.sleep(1)
else:
break
conf_data = json.loads(public.ReadFile(backup_json_path))
except Exception as e:
return public.ReturnMsg(False,
public.lang("Error retrieving backup progress information: {}").format(str(e)))
# 读取备份日志
backup_log_data = public.ReadFile(backup_log_file) if os.path.exists(backup_log_file) else ""
# 定义备份类型及其处理逻辑
backup_types = [
{
'type': 'site',
'data_key': 'site',
'display_name': 'site',
'progress': 30
},
{
'type': 'database',
'data_key': 'database',
'display_name': 'database',
'progress': 60
},
{
'type': 'ftp',
'data_key': 'ftp',
'display_name': 'ftp',
'progress': 70
},
{
'type': 'terminal',
'data_key': 'terminal',
'display_name': 'terminal',
'progress': 75
},
{
'type': 'firewall',
'data_key': 'firewall',
'display_name': 'firewall',
'progress': 90
}
]
# 检查各类型备份进度
for backup_type in backup_types:
items = conf_data.get("data_list", {}).get(backup_type['data_key'], [])
for item in items:
try:
if item.get("status") == 2:
continue
return public.ReturnMsg(True, {
"task_type": "backup",
"task_status": 1,
"data_type": backup_type['type'],
"name": item.get("name", f"unknow {backup_type['display_name']}"),
"data_backup_status": item.get("status", 0),
"progress": backup_type['progress'],
"msg": item.get("msg"),
'exec_log': backup_log_data,
'timestamp': timestamp
})
except:
return public.ReturnMsg(True, {
"task_type": "backup",
"task_status": 1,
"data_type": public.lang("Server Configuration"),
"name": public.lang("Server Configuration"),
"data_backup_status": 1,
"progress": 80,
"msg": public.lang("Backing up server configuration"),
'exec_log': backup_log_data,
'timestamp': timestamp
})
# 检查数据打包进度
try:
backup_status = conf_data.get('backup_status')
if backup_status == 1:
return public.ReturnMsg(True, {
"task_type": "backup",
"task_status": 1,
"data_type": "tar",
"name": public.lang("Data Packaging"),
"data_backup_status": 1,
"progress": 90,
'exec_log': backup_log_data,
'timestamp': timestamp
})
except Exception:
# 可能没有backup_status字段继续处理
pass
# 如果没有发现进行中的任务,但有备份进程
if timestamp:
return {
"backup_data": "unknown",
"backup_name": "unknow",
"data_backup_status": 1,
"progress": 10,
'backup_msg': public.lang("Preparing backup data"),
'backup_log': backup_log_data,
'timestamp': timestamp
}
return public.ReturnMsg(False, public.lang(
"No ongoing backup tasks found. Please check the backup list to see if the backup is completed"))
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 3:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2] # IP地址
backup_manager = BackupManager() # 实例化对象
if hasattr(backup_manager, method_name): # 检查方法是否存在
method = getattr(backup_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: Method '{method_name}' does not exist")

View File

@@ -0,0 +1,112 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <wzz@yakpanel.com>
# -------------------------------------------------------------------
import hashlib
import os
import sys
import time
import warnings
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")
os.chdir("/www/server/panel")
import public
warnings.filterwarnings("ignore", category=SyntaxWarning)
class BaseUtil:
def __init__(self):
self.base_path = '/www/backup/backup_restore'
if not os.path.exists(self.base_path):
public.ExecShell('mkdir -p {}'.format(self.base_path))
self.history_path = '/www/backup/backup_restore/history'
self.history_info_path = '/www/backup/backup_restore/history/info'
self.nginx_bin_path = '/www/server/nginx/sbin/nginx'
self.overwrite = 0 # 0 跳过, 1 覆盖
self.auto_exit = 0 # 异常打断, 默认0
def print_log(self, log: str, type: str):
time_str = time.strftime('%Y-%m-%d %H:%M:%S')
log = "[{}] {}".format(time_str, log)
log_file = self.base_path + '/{}.log'.format(type)
public.writeFile(log_file, log + "\n", 'a+')
def replace_log(self, old_str: str, new_str: str, type: str):
log_file = self.base_path + '/{}.log'.format(type)
log_data = public.ReadFile(log_file)
if old_str in log_data:
log_data = log_data.replace(old_str, new_str)
public.WriteFile(log_file, log_data)
def get_file_sha256(self, file_path) -> str:
sha256 = hashlib.sha256()
with open(file_path, "rb") as f:
while True:
chunk = f.read(4096) # 先读取再判断
if not chunk:
break
sha256.update(chunk)
return sha256.hexdigest()
def get_free_space(self):
result = {}
path = "/www"
diskstat = os.statvfs(path)
free_space = diskstat.f_bavail * diskstat.f_frsize
# total_space = diskstat.f_blocks * diskstat.f_frsize
# used_space = (diskstat.f_blocks - diskstat.f_bfree) * diskstat.f_frsize
result['free_space'] = free_space
return result
def get_file_size(self, path: str) -> int:
try:
if os.path.isfile(path):
return os.path.getsize(path)
elif os.path.isdir(path):
return int(public.ExecShell(f"du -sb {path}")[0].split("\t")[0])
return 0
except:
return 0
def format_size(self, size: int):
if size < 1024:
return f"{size}B"
elif size < 1024 * 1024:
return f"{size / 1024:.2f}KB"
elif size < 1024 * 1024 * 1024:
return f"{size / 1024 / 1024:.2f}MB"
elif size < 1024 * 1024 * 1024 * 1024:
return f"{size / 1024 / 1024 / 1024:.2f}GB"
else:
return f"{size / 1024 / 1024 / 1024 / 1024:.2f}TB"
def web_config_check(self):
if os.path.exists(self.nginx_bin_path):
nginx_conf_test = public.ExecShell("ulimit -n 8192 ;{} -t".format(self.nginx_bin_path))[1]
if "successful" in nginx_conf_test:
return {
"status": True,
"msg": None
}
else:
return {
"status": False,
"msg": public.lang("Nginx Configuration file error, inventory!:{}".format(nginx_conf_test))
}
else:
return {
"status": True,
"msg": None
}

View File

@@ -0,0 +1,691 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <wzz@yakpanel.com>
# -------------------------------------------------------------------
import datetime
import json
import os
import re
import socket
import sys
import time
import warnings
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
import public.validate
from public import Param
from public.exceptions import HintException
from mod.project.backup_restore.data_manager import DataManager
from mod.project.backup_restore.backup_manager import BackupManager
from mod.project.backup_restore.restore_manager import RestoreManager
from mod.project.backup_restore.ssh_manager import BtInstallManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
class main(DataManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.backup_pl_file = self.base_path + '/backup.pl'
self.restore_pl_file = self.base_path + '/restore.pl'
self.migrate_task_json = self.base_path + '/migration_task.json'
self.migrate_pl_file = self.base_path + '/migrate.pl'
self.migrate_success_pl = self.base_path + '/migrate_success.pl'
def return_data(self, status: bool = None, msg: str = None, error_msg: str = None, data: list | dict = None):
aa_status = 0 if status else -1
result = None
if not isinstance(data, type(None)):
result = data
elif msg:
result = public.lang(msg)
elif error_msg:
result = public.lang(error_msg)
return public.return_message(aa_status, 0, result)
def add_backup(self, get):
""" 备份"""
try:
get.validate([
Param("backup_name").String().Require(),
Param("storage_type").String().Require(),
Param("timestamp").Integer().Require(),
Param("auto_exit").Integer("in", [0, 1]).Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
if os.path.exists(self.backup_pl_file):
self.task_stop()
if os.path.exists(self.base_path + "/success.pl"):
try:
if int(os.path.getctime(self.base_path + "/success.pl")) + 10 > int(time.time()):
return public.fail_v2(public.lang("Please do not operate frequently, please wait a moment"))
except:
pass
web_check = self.web_config_check()
if web_check['status'] is False:
return self.return_data(error_msg=web_check['msg'])
backup_config = []
if os.path.exists(self.bakcup_task_json):
backup_config = json.loads(public.ReadFile(self.bakcup_task_json))
get.auto_exit = 0 # 强制不打断, 通过error msg交互
backup_now = False
if not hasattr(get, "timestamp"):
get_time = ""
else:
try:
get_time = int(get.timestamp)
except:
get_time = get.timestamp
local_timestamp = int(time.time())
if get_time == "" or get_time == "0" or get_time == 0:
backup_timestamp = local_timestamp
get_time = local_timestamp
backup_now = True
else:
backup_timestamp = get_time
backup_conf = {
'backup_name': get.backup_name,
'timestamp': get_time,
'create_time': datetime.datetime.fromtimestamp(int(local_timestamp)).strftime('%Y-%m-%d %H:%M:%S'),
'backup_time': datetime.datetime.fromtimestamp(int(backup_timestamp)).strftime('%Y-%m-%d %H:%M:%S'),
'storage_type': get.storage_type,
'auto_exit': int(get.auto_exit),
'backup_status': 0 if not backup_now else 1,
'restore_status': 0,
'backup_path': self.base_path + "/" + str(get_time) + "_backup",
'backup_file': "",
'backup_file_sha256': "",
'backup_file_size': "",
'backup_count': {
'success': None,
'failed': None,
},
'total_time': None,
'done_time': None,
}
backup_config.append(backup_conf)
public.WriteFile(self.bakcup_task_json, json.dumps(backup_config))
if backup_now:
public.ExecShell(
"nohup btpython /www/server/panel/mod/project/backup_restore/backup_manager.py backup_data {} > /dev/null 2>&1 &".format(
int(get_time)
)
)
else:
# todo at time
# 2024-05-20 14:00
at_time_str = time.strftime("%Y-%m-%d %H:%M", time.localtime(int(get_time)))
exec_script = "btpython /www/server/panel/mod/project/backup_restore/backup_manager.py"
exec_command = f"cd {public.get_panel_path()} && echo 'nohup {exec_script} backup_data {int(get_time)} > /dev/null 2>&1' | at {at_time_str}"
public.print_log(f"{exec_command}")
public.ExecShell(exec_command)
public.set_module_logs('backup_restore', 'add_backup', 1)
return self.return_data(True, public.lang("Added successfully"))
def get_backup_list(self, get=None):
if not os.path.exists(self.base_path):
public.ExecShell("mkdir -p {}".format(self.base_path))
backup_config = BackupManager().get_local_backup()
backup_config = sorted(backup_config, key=lambda x: int(x["timestamp"]), reverse=True)
return self.return_data(True, public.lang("Successfully retrieved"), None, backup_config)
def del_backup(self, get):
if not hasattr(get, "timestamp"):
return self.return_data(False, public.lang("Parameter error"), public.lang("Parameter error"))
backup_config = []
if os.path.exists(self.bakcup_task_json):
backup_config = json.loads(public.ReadFile(self.bakcup_task_json))
for backup_conf in backup_config:
if backup_conf['timestamp'] == int(get.timestamp):
for i in [
backup_conf.get("backup_file", ""),
backup_conf.get("backup_path", ""),
]:
if i and os.path.exists(i):
public.ExecShell(f"rm -rf {i}")
backup_config.remove(backup_conf)
public.WriteFile(self.bakcup_task_json, json.dumps(backup_config))
info_path = os.path.join(self.base_path, "history", "info")
log_path = os.path.join(self.base_path, "history", "log")
if os.path.exists(info_path):
for item in os.listdir(info_path):
if item.startswith(str(get.timestamp)):
public.ExecShell("rm -rf {}".format(os.path.join(info_path, item)))
if os.path.exists(log_path):
for item in os.listdir(log_path):
if item.startswith(str(get.timestamp)):
public.ExecShell("rm -rf {}".format(os.path.join(log_path, item)))
return self.return_data(True, public.lang("Deleted successfully"))
backup_file_list = os.listdir(self.base_path)
for backup_file in backup_file_list:
if backup_file.endswith(".tar.gz") or backup_file.endswith("_backup") or backup_file.endswith("_migration"):
if str(get.timestamp) in backup_file:
if os.path.exists(os.path.join(self.base_path, backup_file)):
public.ExecShell("rm -rf {}".format(os.path.join(self.base_path, backup_file)))
return self.return_data(True, public.lang("Deleted successfully"))
return self.return_data(False, public.lang("Deletion failed"))
def get_data_total(self, get=None):
server_data = self.get_data_list()
return self.return_data(status=True, data=server_data)
def get_progress(self, get=None):
try:
get.validate([
Param("type").String(opt="in", length_or_list=["backup", "restore"]).Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
type = get.type
progress_data = self.get_progress_with_type(type)
if progress_data['status'] is True:
return self.return_data(True, public.lang("Successfully retrieved"), data=progress_data.get('msg'))
return self.return_data(False, error_msg=progress_data.get('msg', public.lang("Failed to get progress")))
def get_details(self, get):
""" 获取备份或还原任务的详细信息"""
try:
get.validate([
Param("type").String(opt="in", length_or_list=["backup", "restore"]).Require(),
Param("timestamp").Timestamp().Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
if get.type == "backup":
return BackupManager().get_backup_details(get.timestamp)
elif get.type == "restore":
return RestoreManager().get_restore_details(get.timestamp)
raise HintException(public.lang("Unknown Type"))
def get_exec_logs(self, get=None):
try:
get.validate([
Param("timestamp").Integer().Require(),
Param("type").String("in", ["backup", "restore"]).Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
timestamp = get.timestamp
type = get.type
exec_logs = ""
if type == "backup":
exec_logs = BackupManager().get_backup_log(timestamp)
elif type == "restore":
exec_logs = RestoreManager().get_restore_log(timestamp)
return self.return_data(True, public.lang("Successfully retrieved"), "", exec_logs)
def task_stop(self, get=None):
backup_task_pid = public.ExecShell(
"ps -ef|grep 'backup_manager.py'|grep -v grep|awk '{print $2}'"
)[0].replace("\n", "")
if backup_task_pid:
public.ExecShell("kill {}".format(backup_task_pid))
restore_task_pid = public.ExecShell(
"ps -ef|grep 'restore_manager.py'|grep -v grep|awk '{print $2}'"
)[0].replace("\n", "")
if restore_task_pid:
public.ExecShell("kill {}".format(restore_task_pid))
if os.path.exists(self.backup_pl_file):
public.ExecShell("rm -f {}".format(self.backup_pl_file))
if os.path.exists(self.restore_pl_file):
public.ExecShell("rm -f {}".format(self.restore_pl_file))
try:
task_json_data = json.loads(public.ReadFile(self.bakcup_task_json))
for item in task_json_data:
if 'backup_status' in item and item['backup_status'] == 1:
item['backup_status'] = 0
if 'restore_status' in item and item['restore_status'] == 1:
item['restore_status'] = 0
public.WriteFile(self.bakcup_task_json, json.dumps(task_json_data))
except:
pass
if os.path.exists("/www/server/panel/data/migration.pl"):
public.ExecShell("rm -f /www/server/panel/data/migration.pl")
return self.return_data(True, public.lang("Task stopped successfully"), None, None)
def get_backup_detail(self, get=None):
try:
get.validate([
Param("timestamp").Integer().Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
timestamp = get.timestamp
data = BackupManager().get_backup_file_msg(timestamp)
return self.return_data(True, public.lang("Successfully retrieved"), "", data)
def exec_backup(self, get=None):
if not hasattr(get, "timestamp"):
return self.return_data(False, public.lang("Parameter error"), public.lang("Parameter error"))
timestamp = get.timestamp
public.ExecShell(
"nohup btpython /www/server/panel/mod/project/backup_restore/backup_manager.py backup_data {} > /dev/null 2>&1 &".format(
int(timestamp)
)
)
return self.return_data(True, public.lang("Executed successfully"), public.lang("Executed successfully"))
def add_restore(self, get=None):
"""
还原
"""
try:
get.validate([
Param("timestamp").Integer().Require(),
Param("auto_exit").Integer("in", [0, 1]).Require(), # 打断任务
Param("force_restore").Integer("in", [0, 1]).Require(), # 覆盖强制还原
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
if os.path.exists(self.restore_pl_file):
self.task_stop()
if os.path.exists(self.base_path + "/restore_success.pl"):
try:
if int(os.path.getctime(self.base_path + "/restore_success.pl")) + 10 > int(time.time()):
return public.fail_v2(public.lang("Please do not operate frequently, please wait a moment"))
except:
pass
timestamp = get.timestamp
public.ExecShell(
"nohup btpython /www/server/panel/mod/project/backup_restore/restore_manager.py restore_data {} {} > /dev/null 2>&1 &".format(
int(timestamp), int(get.force_restore)
)
)
public.set_module_logs('backup_restore', 'add_restore', 1)
return self.return_data(True, public.lang("Restore task added successfully"))
def ssh_auth_check(self, get):
"""验证SSH连接信息是否正常"""
try:
get.validate([
Param("server_ip").String().Require(),
Param("ssh_port").Integer(),
Param("ssh_user").String().Require(),
Param("password").String(),
Param("auth_type").String("in", ["password", "key"]).Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
web_check = self.web_config_check()
if web_check['status'] is False:
return self.return_data(error_msg="{}".format(web_check['msg']))
port = int(get.ssh_port) if hasattr(get, "ssh_port") and get.ssh_port else 22
ssh_client = self.ssh_net_client_check(get.server_ip, port)
if not ssh_client:
return self.return_data(
error_msg=public.lang("SSH connection test failed, please check if the IP and port are correct"))
password = None
key_file = None
# 至少需要提供密码或密钥文件之一
if hasattr(get, "password") and get.password:
password = get.password
if get.auth_type == "password":
key_file = None
elif get.auth_type == "key":
key_file = "/www/backup/backup_restore/key_file"
public.WriteFile(key_file, get.password)
public.ExecShell("chmod 600 {}".format(key_file))
# 创建SSH管理器实例并验证连接
manager = BtInstallManager(
host=get.server_ip,
port=port,
username=get.ssh_user,
password=password,
key_file=key_file
)
result = manager.verify_ssh_connection()
if result["status"]:
return self.return_data(True, public.lang("SSH connection verified successfully"), None, None)
return self.return_data(error_msg=result["msg"])
def add_migrate_task(self, get=None):
try:
get.validate([
Param("server_ip").String().Require(),
Param("ssh_port").Integer(),
Param("ssh_user").String().Require(),
Param("password").String(),
Param("auth_type").String("in", ["password", "key"]).Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
self.stop_migrate()
if os.path.exists("/www/backup/backup_restore/migration.log"):
public.ExecShell("rm -f /www/backup/backup_restore/migration.log")
server_ip = get.server_ip
ssh_port = get.ssh_port
ssh_user = get.ssh_user
auth_type = get.auth_type
password = get.password
if auth_type == "key":
key_file = "/www/backup/backup_restore/key_file"
public.WriteFile(key_file, password)
public.ExecShell("chmod 600 {}".format(key_file))
else:
key_file = None
timestamp = int(time.time())
migrate_conf = {
'server_ip': server_ip,
'ssh_port': ssh_port,
'ssh_user': ssh_user,
'auth_type': auth_type,
'password': password,
'timestamp': timestamp,
'run_type': "INIT",
'run_status': 1,
'confirm': 0,
'step': 1,
'migrate_progress': 5,
'migrate_msg': public.lang("Migration task initializing"),
'task_info': None,
}
public.WriteFile(self.migrate_task_json, json.dumps(migrate_conf))
if auth_type == "password":
public.ExecShell(
"nohup btpython /www/server/panel/mod/project/backup_restore/ssh_manager.py --action migrate -H {server_ip} -P {ssh_port} -u {ssh_user} --password='{password}' --task-name '{task_name}' > /dev/null 2>&1 &".format(
server_ip=server_ip, ssh_port=ssh_port, ssh_user=ssh_user, password=password,
task_name=public.lang("My Migration Task")
)
)
elif auth_type == "key":
public.ExecShell(
"nohup btpython /www/server/panel/mod/project/backup_restore/ssh_manager.py --action migrate -H {server_ip} -P {ssh_port} -u {ssh_user} --key-file {key_file} --task-name '{task_name}' > /dev/null 2>&1 &".format(
server_ip=server_ip, ssh_port=ssh_port, ssh_user=ssh_user, key_file=key_file,
task_name=public.lang("My Migration Task")
)
)
public.set_module_logs('backup_restore', 'add_migrate_task', 1)
return self.return_data(True, public.lang("Migration task added successfully"), None, None)
def get_migrate_status(self, get=None):
if os.path.exists(self.migrate_task_json):
migrate_config = json.loads(public.ReadFile(self.migrate_task_json))
result = {
"server_ip": migrate_config.get('server_ip', ''),
"ssh_port": migrate_config.get('ssh_port', 22),
"ssh_user": migrate_config.get('ssh_user', ''),
"auth_type": migrate_config.get('auth_type', 'password'),
"password": migrate_config.get('password', ''),
"migrate_progress": migrate_config.get('migrate_progress', 0),
"timestamp": migrate_config.get("timestamp", 0),
"total_time": migrate_config.get("total_time", 0),
"is_running": migrate_config['run_status'] == 1 or migrate_config.get("confirm", 0) == 0,
}
else:
result = {
"is_running": False,
}
return self.return_data(True, public.lang("Successfully retrieved"), None, result)
def close_migrate_popup(self, get=None):
"""用户二次确认, 关闭迁移弹窗"""
if os.path.exists(self.migrate_task_json):
migrate_config = json.loads(public.ReadFile(self.migrate_task_json))
if migrate_config.get("run_status") == 2:
migrate_config['confirm'] = 1
public.WriteFile(self.migrate_task_json, json.dumps(migrate_config))
return self.return_data(True, public.lang("Successfully migrated"))
self.stop_migrate()
return self.return_data(True, public.lang("Successfully"))
def stop_migrate(self, get=None):
migrate_pid = public.ExecShell(
"ps -ef|grep 'ssh_manager.py'|grep -v grep|awk '{print $2}'"
)[0].replace("\n", "")
if migrate_pid:
public.ExecShell("kill {}".format(migrate_pid))
public.ExecShell("rm -f /www/backup/backup_restore/migrate_backup.pl")
public.ExecShell("rm -f /www/backup/backup_restore/migration.pl")
public.ExecShell("rm -f /www/backup/backup_restore/migrate_backup_success.pl")
if os.path.exists(self.migrate_task_json):
public.ExecShell("rm -f {}".format(self.migrate_task_json))
return self.return_data(True, public.lang("Task stopped successfully"), None, None)
else:
return self.return_data(error_msg=public.lang("No migration task currently"))
def get_migrate_progress(self, get=None):
if os.path.exists(self.migrate_task_json):
try:
migrate_config = json.loads(public.ReadFile(self.migrate_task_json))
except:
return self.return_data(error_msg=public.lang("read migration task fail, please try again later!"))
migrate_config['migrate_log'] = public.ReadFile('/www/backup/backup_restore/migration.log')
if migrate_config['run_type'] == "PANEL_INSTALL":
migrate_config['migrate_log'] = public.ReadFile('/www/backup/backup_restore/migration.log')
if migrate_config['run_type'] == "LOCAL_BACKUP":
if os.path.exists('/www/backup/backup_restore/backup.log'):
backup_log_data = public.ReadFile('/www/backup/backup_restore/backup.log')
else:
backup_log_data = public.lang("Starting backup task...")
migration_log_data = public.ReadFile('/www/backup/backup_restore/migration.log')
migrate_config['migrate_log'] = migration_log_data + "\n" + backup_log_data
if migrate_config['run_status'] == 2:
if migrate_config['run_type'] == "COMPLETED":
migrate_config['migrate_progress'] = 100
migrate_config['migrate_err_msg'] = None
migrate_config['migrate_msg'] = public.lang("yakpanel installation completed!")
try:
migrate_config['panel_addr'] = migrate_config['task_info']['panel_info']['panel_url']
migrate_config['panel_user'] = migrate_config['task_info']['panel_info']['username']
migrate_config['panel_password'] = migrate_config['task_info']['panel_info']['password']
except KeyError:
return self.return_data(error_msg=public.lang(
f"Remote panel info not found! please cancel the task and try again!"
))
except Exception as e:
return self.return_data(error_msg=public.lang(f"Migration task failed! {e}"))
else:
migrate_config['run_status'] = 1
else:
migrate_config['migrate_err_msg'] = migrate_config['migrate_msg']
run_name = public.lang("Migration Task")
err_info = []
if migrate_config['run_type'] == "PANEL_INSTALL":
run_name = public.lang("yakpanel Installation")
elif migrate_config['run_type'] == "LOCAL_BACKUP":
run_name = public.lang("Local Backup")
elif migrate_config['run_type'] == "UPLOAD_FILE":
run_name = public.lang("File Upload")
elif migrate_config['run_type'] == "REMOTE":
run_name = public.lang("Restore Task")
err_info_result = {
"name": run_name,
"type": public.lang("Environment"),
"msg": migrate_config['migrate_msg']
}
err_info.append(err_info_result)
migrate_config['err_info'] = err_info
return self.return_data(True, public.lang("Successfully retrieved"), None, migrate_config)
else:
return self.return_data(error_msg=public.lang("No migration task currently"))
def get_history_migrate_list(self, get=None):
history_migrate = []
if os.path.exists(self.base_path):
for item in os.listdir(self.base_path):
item_path = os.path.join(self.base_path, item)
if os.path.isdir(item_path) and re.match(r'^(\d+)_migration$', item):
timestamp = re.match(r'^(\d+)_migration$', item).group(1)
if os.path.exists(os.path.join(item_path, "status.json")):
status_data = json.loads(public.ReadFile(os.path.join(item_path, "status.json")))
migrate_ip = status_data['server_ip']
else:
migrate_ip = None
migrate_data = {
"timestamp": int(timestamp),
"migrate_time": int(timestamp),
"migrate_path": item_path,
"migrate_ip": migrate_ip
}
history_migrate.append(migrate_data)
return history_migrate
def get_history_migrate_log(self, get=None):
timestamp = get.timestamp
history_migrate_log = self.base_path + "/" + str(timestamp) + "_migration/migration.log"
if os.path.exists(history_migrate_log):
return self.return_data(True, public.lang("Successfully retrieved"), None,
public.ReadFile(history_migrate_log))
else:
return self.return_data(False, public.lang("Migration log does not exist"), None, None)
def get_history_migrate_info(self, get=None):
try:
get.validate([
Param("timestamp").Timestamp().Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
timestamp = get.timestamp
history_migrate_info = self.base_path + "/" + str(timestamp) + "_migration/status.json"
if os.path.exists(history_migrate_info):
return self.return_data(True, public.lang("Successfully retrieved"), None,
json.loads(public.ReadFile(history_migrate_info)))
else:
return self.return_data(error_msg=public.lang("Migration log does not exist"))
def get_backup_log(self, get=None):
if not hasattr(get, "timestamp"):
return self.return_data(False, public.lang("Parameter error"), public.lang("Parameter error"))
timestamp = get.timestamp
return self.return_data(True, public.lang("Successfully retrieved"), "",
BackupManager().get_backup_log(timestamp))
def ssh_net_client_check(self, server_ip, ssh_port):
try:
# 使用requests库测试SSH连接设置3秒超时
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3)
result = sock.connect_ex((server_ip, int(ssh_port)))
sock.close()
if result == 0:
return True
else:
return False
except Exception as e:
public.print_log(public.lang("SSH connection test failed: {}").format(e))
return False
def del_migrate_tips(self, get=None):
if os.path.exists("/www/server/panel/data/migration.pl"):
public.ExecShell("rm -f /www/server/panel/data/migration.pl")
return public.returnMsg(True, public.lang("Migration reminder deleted successfully"))
def del_history_migrate(self, get=None):
try:
get.validate([
Param("timestamp").Timestamp().Require(),
], [
public.validate.trim_filter(),
])
except Exception as ex:
public.print_log("error info: {}".format(ex))
return public.fail_v2(str(ex))
timestamp = get.timestamp
if os.path.exists(self.base_path + "/" + str(timestamp) + "_migration"):
public.ExecShell("rm -rf {}".format(self.base_path + "/" + str(timestamp) + "_migration"))
return self.return_data(True, public.lang("Migration history deleted successfully"))
else:
return self.return_data(error_msg=public.lang("Migration history does not exist"))
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 2:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名 p
timestamp = sys.argv[2]
com_manager = main() # 实例化对象
if hasattr(com_manager, method_name): # 检查方法是否存在
method = getattr(com_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: {public.lang('Method')} '{method_name}' {public.lang('does not exist')}")

View File

@@ -0,0 +1,112 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <wzz@yakpanel.com>
# -------------------------------------------------------------------
import json
import os
import sys
import warnings
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
warnings.filterwarnings("ignore", category=SyntaxWarning)
class ConfigManager:
def __init__(self):
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
def get_backup_conf(self, timestamp):
if not os.path.exists(self.bakcup_task_json):
return None
task_json_data = json.loads(public.ReadFile(self.bakcup_task_json))
data = [item for item in task_json_data if str(item['timestamp']) == timestamp]
if not data:
return None
return data[0]
def save_backup_conf(self, timestamp, data):
if not os.path.exists(self.bakcup_task_json):
return None
task_json_data = json.loads(public.ReadFile(self.bakcup_task_json))
for item in task_json_data:
if str(item['timestamp']) == timestamp:
item.update(data)
break
public.WriteFile(self.bakcup_task_json, json.dumps(task_json_data))
def get_backup_data_list(self, timestamp):
data_list_json = self.base_path + '/{timestamp}_backup/backup.json'.format(timestamp=timestamp)
if not os.path.exists(data_list_json):
return None
data_list_data = json.loads(public.ReadFile(data_list_json))
return data_list_data
def update_backup_data_list(self, timestamp, data_list):
data_list_json = self.base_path + '/{timestamp}_backup/backup.json'.format(timestamp=timestamp)
try:
# 读取现有配置
if os.path.exists(data_list_json):
current_data = json.loads(public.ReadFile(data_list_json))
# 更新数据
current_data.update(data_list)
data_list = current_data
# 写入更新后的配置
public.WriteFile(data_list_json, json.dumps(data_list))
return True
except Exception as e:
public.print_log("update_backup_data_list error: {}".format(str(e)))
return False
def get_restore_data_list(self, timestamp):
data_list_json = self.base_path + '/{timestamp}_backup/restore.json'.format(timestamp=timestamp)
if not os.path.exists(data_list_json):
return None
data_list_data = json.loads(public.ReadFile(data_list_json))
return data_list_data
def update_restore_data_list(self, timestamp, data_list):
data_list_json = self.base_path + '/{timestamp}_backup/restore.json'.format(timestamp=timestamp)
# 读取现有配置
try:
# 读取现有配置
if os.path.exists(data_list_json):
current_data = json.loads(public.ReadFile(data_list_json))
# 更新数据
current_data.update(data_list)
data_list = current_data
# 写入更新后的配置
public.WriteFile(data_list_json, json.dumps(data_list))
return True
except Exception as e:
public.print_log("update_restore_data_list error: {}".format(str(e)))
return False
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 3:
print("Usage: btpython config_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2] # IP地址
config = ConfigManager() # 实例化对象
if hasattr(config, method_name): # 检查方法是否存在
method = getattr(config, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: method '{method_name}' not found")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import json
import os
import re
import sys
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 YakPanel import app
import crontab_v2 as crontab
from mod.project.backup_restore.base_util import BaseUtil
from mod.project.backup_restore.config_manager import ConfigManager
class CrontabModule(BaseUtil, ConfigManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
def backup_crontab_data(self, timestamp):
self.print_log("====================================================", "backup")
self.print_log(public.lang("Starting a Backup Scheduled Task"), "backup")
backup_path = self.base_path + "/{timestamp}_backup/crontab".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
field = ('id,name,type,where1,where_hour,where_minute,echo,addtime,status,'
'save,backupTo,sName,sBody,sType,urladdress,save_local,notice,'
'notice_channel,db_type,split_type,split_value,type_id,rname,'
'keyword,post_param,flock,time_set,backup_mode,db_backup_path,'
'time_type,special_time,log_cut_path,user_agent,version,table_list,result,second')
crontab_data = public.M('crontab').order("id asc").field(field).select()
for task in crontab_data:
task['type_id'] = ""
crontab_json_path = "{}/crontab.json".format(backup_path)
public.WriteFile(crontab_json_path, json.dumps(crontab_data))
for item in crontab_data:
self.print_log(public.lang("Crontab Task {}".format(item['name'])), "backup")
public.ExecShell(f"\cp -rpa /www/server/cron/* {backup_path}/")
crontab_info = {
'status': 2,
'msg': None,
'crontab_json': crontab_json_path,
'file_sha256': self.get_file_sha256(crontab_json_path)
}
self.print_log(public.lang("Backup Crontab Task completion"), 'backup')
data_list = self.get_backup_data_list(timestamp)
data_list['data_list']['crontab'] = crontab_info
self.update_backup_data_list(timestamp, data_list)
def _add_crontab(self, crontab_item: dict, timestamp: int) -> None:
if crontab_item['name'] in ("Domain SSL Renew Let's Encrypt Certificate", "Renew Let's Encrypt Certificate"):
import acme_v2
if crontab_item['name'] == "Domain SSL Renew Let's Encrypt Certificate":
acme_v2.acme_v2().set_crond_v2()
elif crontab_item['name'] == "Renew Let's Encrypt Certificate":
acme_v2.acme_v2().set_crond()
self.print_log(
public.lang(f"Crontab Task: {crontab_item['name']} Add successfully ✓"),
"restore"
)
return
if crontab_item['name'] == "[Do not delete] Resource Manager - Get Process Traffic":
return
s_body = crontab_item['sBody']
s_body = re.sub(r'sudo -u .*? bash -c \'(.*?)\'', r'\1', s_body)
new_crontab = {
"name": crontab_item['name'],
"echo": crontab_item['echo'],
"type": crontab_item['type'],
"where1": crontab_item['where1'],
"hour": crontab_item['where_hour'],
"minute": crontab_item['where_minute'],
"status": crontab_item['status'],
"save": crontab_item['save'],
"backupTo": crontab_item['backupTo'],
"sType": crontab_item['sType'],
"sBody": s_body,
"sName": crontab_item['sName'],
"urladdress": crontab_item['urladdress'],
"save_local": crontab_item['save_local'],
"notice": crontab_item['notice'],
"notice_channel": crontab_item['notice_channel'],
"db_type": crontab_item['db_type'],
"split_type": crontab_item['split_type'],
"split_value": crontab_item['split_value'],
"keyword": crontab_item['keyword'],
"post_param": crontab_item['post_param'],
"flock": crontab_item['flock'],
"time_set": crontab_item['time_set'],
"backup_mode": crontab_item['backup_mode'],
"db_backup_path": crontab_item['db_backup_path'],
"time_type": crontab_item['time_type'],
"special_time": crontab_item['special_time'],
"user_agent": crontab_item['user_agent'],
"version": crontab_item['version'],
"table_list": crontab_item['table_list'],
"result": crontab_item['result'],
"log_cut_path": crontab_item['log_cut_path'],
"rname": crontab_item['rname'],
"type_id": crontab_item['type_id'],
"second": crontab_item.get('second', ''),
}
result = crontab.crontab().AddCrontab(new_crontab)
crontab_backup_path = self.base_path + f"/{timestamp}_backup/crontab"
back_up_echo_file = os.path.join(crontab_backup_path, crontab_item['echo'])
panel_echo_file = f"/www/server/cron/{crontab_item['echo']}"
if self.overwrite or not os.path.exists(panel_echo_file):
public.ExecShell(
f"\cp -rpa {back_up_echo_file} {panel_echo_file}"
)
if result['status'] != 0:
error_res = public.find_value_by_key(result, key="result", default="fail")
self.print_log(
public.lang(
f"Crontab Task: {crontab_item['name']} add fail, "
f"error: {error_res}, skip..."),
"restore"
)
else:
self.print_log(
public.lang(f"Crontab Task: {crontab_item['name']} Add successfully ✓"),
"restore"
)
def restore_crontab_data(self, timestamp):
self.print_log("==================================", "restore")
self.print_log(public.lang("Start restoring Crontab Task"), "restore")
restore_data = self.get_restore_data_list(timestamp)
cron_list = public.M('crontab').select()
cron_name_list = [i['name'] for i in cron_list]
crontab_data_json = restore_data['data_list']['crontab']['crontab_json']
restore_data['data_list']['crontab']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
crontab_data = json.loads(public.ReadFile(crontab_data_json))
with app.app_context():
for crontab_item in crontab_data:
if self.overwrite:
try:
crontab.crontab().DelCrontab(public.to_dict_obj({"id": crontab_item['id']}))
except:
pass
self._add_crontab(crontab_item, timestamp)
else: # not overwrite
if crontab_item['name'] not in cron_name_list:
self._add_crontab(crontab_item, timestamp)
else:
self.print_log(public.lang(f"Crontab Task: {crontab_item['name']}"), "restore")
self.print_log(public.lang("Crontab Task complished"), "restore")
restore_data['data_list']['crontab']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
def reload_crontab(self):
try:
crontab.crontab().CrondReload()
except:
pass
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 2:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2]
crontab_manager = CrontabModule() # 实例化对象
if hasattr(crontab_manager, method_name): # 检查方法是否存在
method = getattr(crontab_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: method '{method_name}' not found")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import os
import sys
import warnings
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 YakPanel import app
from mod.project.backup_restore.base_util import BaseUtil
from mod.project.backup_restore.config_manager import ConfigManager
from firewallModelV2.comModel import main as firewall_com
from safeModelV2.firewallModel import main as safe_firewall_main
warnings.filterwarnings("ignore", category=SyntaxWarning)
class FirewallModule(BaseUtil, ConfigManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
def backup_firewall_data(self, timestamp):
with app.app_context():
try:
self.print_log("====================================================", "backup")
self.print_log(public.lang("Starting backup of firewall data"), "backup")
backup_path = self.base_path + "/{timestamp}_backup/firewall".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
data_list = self.get_backup_data_list(timestamp)
port_data_path = firewall_com().export_rules(
public.to_dict_obj({"rule": 'port', 'chain': 'ALL'})
)['message'].get('result', '')
ip_data_path = firewall_com().export_rules(
public.to_dict_obj({"rule": 'ip', 'chain': 'ALL'})
)['message'].get('result', '')
forward_data_path = firewall_com().export_rules(
public.to_dict_obj({"rule": 'forward'})
)['message'].get('result', '')
country_data_path = safe_firewall_main().export_rules(
public.to_dict_obj({'rule_name': 'country_rule'})
)['message'].get('result', '')
firewall_info = {
"status": 2,
"err_msg": None
}
for data_path in [
port_data_path, ip_data_path, forward_data_path, country_data_path
]:
if "json" in data_path:
public.ExecShell('\cp -rpa {} {}'.format(data_path, backup_path))
file_name = data_path.split("/")[-1]
if "port_rule" in file_name:
self.print_log(public.lang("Firewall port rules ✓"), 'backup')
firewall_info["port_data_path"] = backup_path + "/" + file_name
elif "ip_rules" in file_name:
self.print_log(public.lang("Firewall IP rules ✓"), 'backup')
firewall_info["ip_data_path"] = backup_path + "/" + file_name
elif "port_forward" in file_name:
self.print_log(public.lang("Firewall forwarding rules ✓"), 'backup')
firewall_info["forward_data_path"] = backup_path + "/" + file_name
elif "country" in file_name:
self.print_log(public.lang("Firewall region rules ✓"), 'backup')
firewall_info["country_data_path"] = backup_path + "/" + file_name
# 将防火墙信息写入备份配置文件
data_list = self.get_backup_data_list(timestamp)
data_list['data_list']['firewall'] = firewall_info
self.update_backup_data_list(timestamp, data_list)
except Exception as e:
data_list['data_list']['firewall'] = {
"status": 3,
"err_msg": e
}
self.update_backup_data_list(timestamp, data_list)
self.print_log(public.lang("Firewall data backup completed"), "backup")
def init_firewall_data(self):
self.print_log(public.lang("Initializing firewall data"), "restore")
if not os.path.exists('/etc/systemd/system/BT-FirewallServices.service'):
panel_path = public.get_panel_path()
exec_shell = '('
if not os.path.exists('/usr/sbin/ipset'):
exec_shell = exec_shell + '{} install ipset -y;'.format(public.get_sys_install_bin())
exec_shell = exec_shell + 'sh {panel_path}/script/init_firewall.sh;btpython -u {panel_path}/script/upgrade_firewall.py )'.format(
panel_path=panel_path
)
public.ExecShell(exec_shell)
return {'status': True, 'msg': public.lang('Installed.')}
elif public.ExecShell("iptables -C INPUT -j IN_BT")[1] != '': # 丢失iptable链 需要重新创建
exec_shell = 'sh {}/script/init_firewall.sh'.format(public.get_panel_path())
public.ExecShell(exec_shell)
return {'status': True, 'msg': public.lang('Installed.')}
else:
return {'status': True, 'msg': public.lang('Installed.')}
def restore_firewall_data(self, timestamp):
with app.app_context():
self.print_log("====================================================", "restore")
self.print_log(public.lang("Starting restoration of firewall data"), "restore")
self.init_firewall_data()
resotre_data = self.get_restore_data_list(timestamp)
firewall_data = resotre_data['data_list']['firewall']
port_rule_file = firewall_data.get('port_data_path')
try:
if port_rule_file:
if os.path.exists(port_rule_file):
self.print_log(public.lang("Starting restoration of firewall port rules"), "restore")
result = firewall_com().import_rules(public.to_dict_obj({"rule": 'port', 'file': port_rule_file}))
if result['status'] == 0:
self.print_log(public.lang("Firewall port rules restored successfully ✓"), "restore")
else:
self.print_log(public.lang("Failed to restore firewall port rules"), "restore")
ip_rule_file = firewall_data.get('ip_data_path')
if ip_rule_file:
if os.path.exists(ip_rule_file):
self.print_log(public.lang("Starting restoration of firewall IP rules"), "restore")
result = firewall_com().import_rules(public.to_dict_obj({"rule": 'ip', 'file': ip_rule_file}))
if result['status'] == 0:
self.print_log(public.lang("Firewall IP rules restored successfully ✓"), "restore")
else:
self.print_log(public.lang("Failed to restore firewall IP rules"), "restore")
forward_rule_file = firewall_data.get('forward_data_path')
if forward_rule_file:
if os.path.exists(forward_rule_file):
self.print_log(public.lang("Starting restoration of firewall forwarding rules"), "restore")
result = firewall_com().import_rules(
public.to_dict_obj({"rule": 'forward', 'file': forward_rule_file}))
if result['status'] == 0:
self.print_log(public.lang("Firewall forwarding rules restored successfully ✓"), "restore")
else:
self.print_log(public.lang("Failed to restore firewall forwarding rules"), "restore")
country_rule_file = firewall_data.get('country_data_path')
if country_rule_file:
if os.path.exists(country_rule_file):
self.print_log(public.lang("Starting restoration of firewall region rules"), "restore")
public.ExecShell('\cp -rpa {} /www/server/panel/data/firewall'.format(country_rule_file))
country_rule_file_last_path = country_rule_file.split("/")[-1]
result = safe_firewall_main().import_rules(
public.to_dict_obj({'rule_name': 'country_rule', 'file_name': country_rule_file_last_path}))
if result['status'] == 0:
self.print_log(public.lang("Firewall region rules restored successfully ✓"), "restore")
else:
self.print_log(public.lang("Failed to restore firewall region rules"), "restore")
# 重启防火墙
self.print_log(public.lang("Starting firewall restart"), "restore")
firewall_com().set_status(public.to_dict_obj({'status': 1}))
self.print_log(public.lang("Firewall restart completed"), "restore")
resotre_data['data_list']['firewall']['status'] = 2
self.update_restore_data_list(timestamp, resotre_data)
except Exception as e:
self.print_log(public.lang("Failed to restore firewall data: {}").format(str(e)), "restore")
resotre_data['data_list']['firewall']['status'] = 3
resotre_data['data_list']['firewall']['err_msg'] = str(e)
self.update_restore_data_list(timestamp, resotre_data)
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 2:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2]
firewall_manager = FirewallModule() # 实例化对象
if hasattr(firewall_manager, method_name): # 检查方法是否存在
method = getattr(firewall_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: method '{method_name}' not found")

View File

@@ -0,0 +1,138 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import os.path
import sys
import time
import warnings
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 YakPanel import app
import ftp_v2 as ftp_client
from mod.project.backup_restore.data_manager import DataManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
class FtpModule(DataManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
def backup_ftp_data(self, timestamp=None):
self.print_log("==================================", "backup")
self.print_log(public.lang("Start backing up FTP account information"), "backup")
ftp_data = public.M('ftps').field('name,pid,id,password,path,ps').select()
filtered_ftp = []
for ftp in ftp_data:
try:
if ftp.get('pid', 0) == 0:
ftp['related_site'] = ''
else:
ftp['related_site'] = self._get_current_site_name_by_pid(ftp.get('pid'))
ftp['status'] = 2
ftp['msg'] = None
filtered_ftp.append(ftp)
self.print_log(public.lang("{} user ✓").format(ftp['name']), "backup")
except Exception as e:
self.print_log(public.lang("Failed to backup FTP account information: {}").format(str(e)), "backup")
ftp['status'] = 3
ftp['msg'] = str(e)
filtered_ftp.append(ftp)
continue
self.print_log(public.lang("FTP account information backup completed"), "backup")
return filtered_ftp
def _add_ftp_user(self, ftp_client: ftp_client, ftp_data: dict) -> int:
"""
:return: ftp_data restore_status
"""
log_str = public.lang("Restoring {} account").format(ftp_data['name'])
args = public.dict_obj()
args.ftp_username = ftp_data['name']
args.path = ftp_data['path']
args.ftp_password = ftp_data['password']
args.ps = ftp_data['ps']
args.pid = self._get_current_pid_by_site_name(
os.path.basename(ftp_data.get('path', ''))
)
res = ftp_client.ftp().AddUser(args)
if res['status'] is False:
self.replace_log(log_str, public.lang("FTP creation failed: {}").format(res.get('message', 'create fail')),
"restore")
return 3
else:
new_log_str = public.lang("{} account ✓").format(ftp_data['name'])
self.replace_log(log_str, new_log_str, "restore")
return 2
def restore_ftp_data(self, timestamp=None):
self.print_log("====================================================", "restore")
self.print_log(public.lang("Start restoring FTP account configuration"), "restore")
restore_data = self.get_restore_data_list(timestamp)
with app.app_context():
for ftp_data in restore_data['data_list']['ftp']:
try:
if_exist = public.M('ftps').where('name=?', (ftp_data["name"],)).find()
log_str = public.lang("Restoring {} account").format(ftp_data['name'])
if if_exist:
self.print_log(log_str, "restore")
if not self.overwrite:
self.replace_log(log_str, public.lang("{} account ✓").format(if_exist.get('name', 'ftp')),
"restore")
continue
else:
ftp_client.ftp().DeleteUser(public.to_dict_obj(
{"id": if_exist['id'], "username": if_exist['name']}
))
time.sleep(0.5)
ftp_data['restore_status'] = self._add_ftp_user(ftp_client, ftp_data)
else:
ftp_data['restore_status'] = self._add_ftp_user(ftp_client, ftp_data)
self.replace_log(
log_str,
public.lang("ftp: [{}] account restored successfully ✓").format(
ftp_data.get('name', 'ftp')),
"restore"
)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
self.print_log(public.lang("Failed to restore FTP account configuration: {}").format(str(e)),
"restore")
self.update_restore_data_list(timestamp, restore_data)
self.print_log(public.lang("FTP account configuration restoration completed"), "restore")
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 3:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2] # IP地址
ftp_module = FtpModule() # 实例化对象
if hasattr(ftp_module, method_name): # 检查方法是否存在
method = getattr(ftp_module, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: Method '{method_name}' not found")

View File

@@ -0,0 +1,114 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import os
import sys
import warnings
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 mod.project.backup_restore.base_util import BaseUtil
from mod.project.backup_restore.config_manager import ConfigManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
class MailModule(BaseUtil, ConfigManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.vmail_data_path = '/www/vmail'
def backup_vmail_data(self, timestamp):
if not os.path.exists(self.vmail_data_path):
return False
data_list = self.get_backup_data_list(timestamp)
if not data_list:
return None
backup_path = self.base_path + "/{timestamp}_backup/vmail".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
self.print_log("====================================================", "backup")
self.print_log(public.lang("Start backing up mail server data"), 'backup')
maill_info = {
'status': 1,
'msg': None,
'vmail_backup_name': None,
}
data_list['data_list']['vmail'] = maill_info
self.update_backup_data_list(timestamp, data_list)
vmail_backup_name = "vmail_{timestamp}.tar.gz".format(timestamp=timestamp)
public.ExecShell("cd /www && tar -czvf {} vmail".format(vmail_backup_name))
public.ExecShell("mv /www/{} {}".format(vmail_backup_name, backup_path))
maill_info['vmail_backup_name'] = backup_path + "/" + vmail_backup_name
maill_info['status'] = 2
maill_info['msg'] = None
maill_info['size'] = self.get_file_size(backup_path + "/" + vmail_backup_name)
data_list['data_list']['vmail'] = maill_info
self.update_backup_data_list(timestamp, data_list)
backup_size = self.format_size(self.get_file_size(backup_path + "/" + vmail_backup_name))
self.print_log(public.lang("Mail server data backup completed. Data size: {}").format(backup_size), 'backup')
def restore_vmail_data(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
if not restore_data:
return None
vmail_data = restore_data['data_list']['vmail']
if not vmail_data:
return None
self.print_log("==================================", "restore")
self.print_log(public.lang("Start restoring mail server data"), "restore")
restore_data['data_list']['vmail']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
vmail_backup_name = vmail_data['vmail_backup_name']
if not os.path.exists(vmail_backup_name):
self.print_log(public.lang("Restoration failed, file does not exist"), "restore")
return
if os.path.exists(self.vmail_data_path):
public.ExecShell("mv {} {}.bak".format(self.vmail_data_path, self.vmail_data_path))
public.ExecShell("\cp -rpa {} /www/{}".format(vmail_backup_name, os.path.basename(self.vmail_data_path)))
public.ExecShell("cd /www && tar -xzvf {}".format(os.path.basename(self.vmail_data_path)))
restore_data['data_list']['vmail']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
self.print_log(public.lang("Mail server data restoration completed"), "restore")
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 3:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2] # IP地址
mail_module = MailModule() # 实例化对象
if hasattr(mail_module, method_name): # 检查方法是否存在
method = getattr(mail_module, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: Method '{method_name}' not found")

View File

@@ -0,0 +1,541 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import json
import os
import subprocess
import sys
import time
import warnings
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 YakPanel import app
from mod.project.backup_restore.base_util import BaseUtil
from mod.project.backup_restore.config_manager import ConfigManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
def get_plugin_object():
import PluginLoader
from panel_plugin_v2 import panelPlugin
p = panelPlugin()
soft_list = PluginLoader.get_plugin_list(0)
# noinspection PyTypeChecker
setattr(p, "_panelPlugin__plugin_s_list", panelPlugin.set_coexist(None, soft_list["list"]))
return p
class PluginModule(BaseUtil, ConfigManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.plugin_path = '/www/server/panel/plugin'
self._safe_flag = False
def backup_plugin_data(self, timestamp):
self.print_log("====================================================", "backup")
self.print_log(public.lang("Start backing up plugin data"), "backup")
backup_path = self.base_path + "/{timestamp}_backup/plugin".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
plugin_info = {}
btwaf_path = os.path.join(self.plugin_path, "btwaf")
monitor_path = os.path.join(self.plugin_path, "monitor")
tamper_core_path = os.path.join(self.plugin_path, "tamper_core")
syssafe_path = os.path.join(self.plugin_path, "syssafe")
if os.path.exists(btwaf_path):
result = self.backup_btwaf_data(timestamp)
plugin_info['btwaf'] = result
if os.path.exists(monitor_path):
result = self.backup_monitor_data(timestamp)
plugin_info['monitor'] = result
if os.path.exists(tamper_core_path):
result = self.backup_tamper_core_data(timestamp)
plugin_info['tamper_core'] = result
if os.path.exists(syssafe_path):
result = self.backup_syssafe_data(timestamp)
plugin_info['syssafe'] = result
data_list = self.get_backup_data_list(timestamp)
data_list['data_list']['plugin'] = plugin_info
self.update_backup_data_list(timestamp, data_list)
def backup_btwaf_data(self, timestamp):
backup_path = self.base_path + "/{timestamp}_backup/plugin/btwaf".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
try:
btwaf_info_json = json.loads(public.ReadFile(os.path.join(self.plugin_path, "btwaf", "info.json")))
result = {
"status": 2,
"err_msg": None,
"version": btwaf_info_json['versions'],
"size": self.get_file_size(os.path.join(self.plugin_path, "btwaf"))
}
public.ExecShell(
"\cp -rpa /www/server/btwaf/config.json {backup_path}/config.json".format(backup_path=backup_path))
public.ExecShell(
"\cp -rpa /www/server/btwaf/site.json {backup_path}/site.json".format(backup_path=backup_path))
public.ExecShell("\cp -rpa /www/server/btwaf/rule {backup_path}/rule".format(backup_path=backup_path))
backup_size = self.format_size(self.get_file_size(backup_path))
self.print_log(public.lang("Nginx firewall ✓ ({})").format(backup_size), 'backup')
return result
except:
return None
def backup_monitor_data(self, timestamp):
backup_path = self.base_path + "/{timestamp}_backup/plugin/monitor".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
try:
monitor_info_json = json.loads(public.ReadFile(os.path.join(self.plugin_path, "monitor", "info.json")))
result = {
"status": 2,
"err_msg": None,
"version": monitor_info_json['versions'],
"size": self.get_file_size(os.path.join(self.plugin_path, "monitor"))
}
if os.path.exists("/www/server/panel/plugin/monitor/site_robots.json"):
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/monitor/site_robots.json {backup_path}/site_robots.json".format(
backup_path=backup_path
)
)
if os.path.exists("/www/server/panel/plugin/monitor/site_sitemap_info.json"):
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/monitor/site_sitemap_info.json {backup_path}/site_sitemap_info.json".format(
backup_path=backup_path
)
)
if os.path.exists("/www/server/panel/plugin/monitor/spider_api.config"):
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/monitor/spider_api.config {backup_path}/spider_api.config".format(
backup_path=backup_path
)
)
if os.path.exists("/www/server/panel/plugin/monitor/baidu_user.config"):
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/monitor/baidu_user.config {backup_path}/baidu_user.config".format(
backup_path=backup_path
)
)
if os.path.exists("/www/server/panel/plugin/monitor/360_user.config"):
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/monitor/360_user.config {backup_path}/360_user.config".format(
backup_path=backup_path
)
)
public.ExecShell(
"\cp -rpa /www/server/monitor/config {backup_path}/config".format(backup_path=backup_path))
backup_size = self.format_size(self.get_file_size(backup_path))
self.print_log(public.lang("Website monitoring report ✓ ({})").format(backup_size), 'backup')
return result
except:
return None
def backup_tamper_core_data(self, timestamp):
backup_path = self.base_path + "/{timestamp}_backup/plugin/tamper_core".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
try:
tamper_core_info_json = json.loads(
public.ReadFile(os.path.join(self.plugin_path, "tamper_core", "info.json")))
result = {
"status": 2,
"err_msg": None,
"version": tamper_core_info_json['versions'],
"size": self.get_file_size(os.path.join(self.plugin_path, "tamper_core"))
}
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/tamper_core/tamper_push_template.json {backup_path}/tamper_push_template.json".format(
backup_path=backup_path
)
)
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/tamper_core/rule.json {backup_path}/rule.json".format(
backup_path=backup_path
)
)
public.ExecShell(
"\cp -rpa /www/server/tamper/config_ps.json {backup_path}/config_ps.json".format(
backup_path=backup_path
)
)
public.ExecShell(
"\cp -rpa /www/server/tamper/tamper.conf {backup_path}/tamper.conf".format(
backup_path=backup_path
)
)
backup_size = self.format_size(self.get_file_size(backup_path))
self.print_log(public.lang("Enterprise tamper protection ✓ ({})").format(backup_size), 'backup')
return result
except:
return None
def backup_syssafe_data(self, timestamp):
backup_path = self.base_path + "/{timestamp}_backup/plugin/syssafe".format(timestamp=timestamp)
if not os.path.exists(backup_path):
public.ExecShell('mkdir -p {}'.format(backup_path))
try:
syssafe_info_json = json.loads(public.ReadFile(os.path.join(self.plugin_path, "syssafe", "info.json")))
result = {
"status": 2,
"err_msg": None,
"version": syssafe_info_json['versions'],
"size": self.get_file_size(os.path.join(self.plugin_path, "syssafe"))
}
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/syssafe/config.json {backup_path}/config.json".format(
backup_path=backup_path
)
)
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/syssafe/sys_process.json {backup_path}/sys_process.json".format(
backup_path=backup_path
)
)
public.ExecShell(
"\cp -rpa /www/server/panel/plugin/syssafe/config {backup_path}/".format(
backup_path=backup_path
)
)
backup_size = self.format_size(self.get_file_size(backup_path))
self.print_log(public.lang("System hardening ✓ ({})").format(backup_size), 'backup')
return result
except:
return None
def _sys_safe_pids(self) -> list:
try:
cmd = """ps aux |grep -E "(bt_syssafe|syssafe_main|syssafe_pub)"|
grep -v grep|grep -v systemctl|grep -v "init.d"|awk '{print $2}'|xargs"""
sysafe_output = subprocess.check_output(cmd, shell=True, text=True)
pids = sysafe_output.strip().split()
return pids if pids else []
except Exception:
return []
def restore_plugin_data(self, timestamp):
""""
恢复插件数据
"""
self.print_log("====================================================", "restore")
self.print_log(public.lang("Start restoring plugin data"), "restore")
self.print_log(public.lang("If the migrated machine is not bound to a yakpanel user and upgraded to Professional version, plugin restoration failures may occur"), "restore")
restore_data = self.get_restore_data_list(timestamp)
plugin_info = restore_data['data_list']['plugin']
# ============================= start =====================================
self._safe_flag = True if self._sys_safe_pids() else False
try:
public.ExecShell("/etc/init.d/bt_syssafe stop")
except:
pass
# ============================== btwaf =====================================
if 'btwaf' in plugin_info and plugin_info['btwaf']:
log_str = public.lang("Start installing Nginx firewall")
self.print_log(log_str, "restore")
restore_data['data_list']['plugin']['btwaf']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
plugin_version = plugin_info['btwaf']['version']
# 检查btwaf目录下是否有正在运行的进程
self.before_install_plugin('btwaf')
install_result = self.install_plugin('btwaf', plugin_version)
if install_result['status'] is True:
new_log_str = public.lang("Nginx firewall ✓")
self.replace_log(log_str, new_log_str, "restore")
log_str = public.lang("Start restoring Nginx firewall data")
self.print_log(log_str, "restore")
self.restore_btwaf_data(timestamp)
restore_data['data_list']['plugin']['btwaf']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("Nginx firewall data ✓")
self.replace_log(log_str, new_log_str, "restore")
else:
restore_data['data_list']['plugin']['btwaf']['restore_status'] = 3
restore_data['data_list']['plugin']['btwaf']['err_msg'] = install_result['msg']
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("Nginx firewall ✗")
self.replace_log(log_str, new_log_str, "restore")
# ====================================== monitor ==============================================
if 'monitor' in plugin_info and plugin_info['monitor']:
log_str = public.lang("Start installing website monitoring report")
self.print_log(log_str, "restore")
restore_data['data_list']['plugin']['monitor']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
plugin_version = plugin_info['monitor']['version']
self.before_install_plugin('monitor')
install_result = self.install_plugin('monitor', plugin_version)
if install_result['status'] is True:
new_log_str = public.lang("Website monitoring report ✓")
self.replace_log(log_str, new_log_str, "restore")
log_str = public.lang("Start restoring website monitoring report data")
self.print_log(log_str, "restore")
self.restore_monitor_data(timestamp)
restore_data['data_list']['plugin']['monitor']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("Website monitoring report data ✓")
self.replace_log(log_str, new_log_str, "restore")
else:
restore_data['data_list']['plugin']['monitor']['restore_status'] = 3
restore_data['data_list']['plugin']['monitor']['err_msg'] = install_result['msg']
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("Website monitoring report ✗")
self.replace_log(log_str, new_log_str, "restore")
# ========================= tamper_core ======================================
if 'tamper_core' in plugin_info and plugin_info['tamper_core']:
log_str = public.lang("Start installing enterprise tamper protection")
self.print_log(log_str, "restore")
restore_data['data_list']['plugin']['tamper_core']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
plugin_version = plugin_info['tamper_core']['version']
self.before_install_plugin('tamper_core')
install_result = self.install_plugin('tamper_core', plugin_version)
if install_result['status'] is True:
public.ExecShell("/etc/init.d/bt-tamper stop")
new_log_str = public.lang("Enterprise tamper protection ✓")
self.replace_log(log_str, new_log_str, "restore")
log_str = public.lang("Start restoring enterprise tamper protection data")
self.print_log(log_str, "restore")
self.restore_tamper_core_data(timestamp)
restore_data['data_list']['plugin']['tamper_core']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("Enterprise tamper protection data ✓")
self.replace_log(log_str, new_log_str, "restore")
else:
restore_data['data_list']['plugin']['tamper_core']['restore_status'] = 3
restore_data['data_list']['plugin']['tamper_core']['err_msg'] = install_result['msg']
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("Enterprise tamper protection ✗")
self.replace_log(log_str, new_log_str, "restore")
# ========================= syssafe ===========================================
if 'syssafe' in plugin_info and plugin_info['syssafe']:
log_str = public.lang("Start installing system hardening")
self.print_log(log_str, "restore")
restore_data['data_list']['plugin']['syssafe']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
plugin_version = plugin_info['syssafe']['version']
self.before_install_plugin('syssafe')
install_result = self.install_plugin('syssafe', plugin_version)
if install_result['status'] is True:
public.ExecShell("/etc/init.d/bt_syssafe stop")
new_log_str = public.lang("System hardening ✓")
self.replace_log(log_str, new_log_str, "restore")
log_str = public.lang("Start restoring system hardening data")
self.print_log(log_str, "restore")
self.restore_syssafe_data(timestamp)
restore_data['data_list']['plugin']['syssafe']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("System hardening data ✓")
self.replace_log(log_str, new_log_str, "restore")
else:
restore_data['data_list']['plugin']['syssafe']['restore_status'] = 3
restore_data['data_list']['plugin']['syssafe']['err_msg'] = install_result['msg']
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang("System hardening ✗")
self.replace_log(log_str, new_log_str, "restore")
# ============================= end =====================================
if self._safe_flag is True:
try:
if os.path.exists("/www/server/panel/plugin/syssafe/syssafe_main.py"):
from plugin.syssafe.syssafe_main import syssafe_main as safe_main
res = safe_main().set_open(None)
public.print_log("Syssafe set_open result: {}".format(res))
except Exception as e:
public.print_log("Failed to start syssafe: {}".format(str(e)))
self.print_log(public.lang("Plugin data restoration complete"), "restore")
def restore_btwaf_data(self, timestamp):
restore_path = self.base_path + "/{timestamp}_backup/plugin/btwaf".format(timestamp=timestamp)
plugin_path = "/www/server/btwaf"
if os.path.exists(restore_path) and os.path.exists(plugin_path):
public.ExecShell(
"\cp -rpa {restore_path}/config.json /www/server/btwaf/config.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/site.json /www/server/btwaf/site.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/rule/* /www/server/btwaf/rule".format(
restore_path=restore_path
)
)
def restore_monitor_data(self, timestamp):
restore_path = self.base_path + "/{timestamp}_backup/plugin/monitor".format(timestamp=timestamp)
plugin_path = "/www/server/panel/plugin/monitor/"
if os.path.exists(restore_path) and os.path.exists(plugin_path):
public.ExecShell(
"\cp -rpa {restore_path}/site_robots.json /www/server/panel/plugin/monitor/site_robots.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/site_sitemap_info.json /www/server/panel/plugin/monitor/site_sitemap_info.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/spider_api.config /www/server/panel/plugin/monitor/spider_api.config".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/baidu_user.config /www/server/panel/plugin/monitor/baidu_user.config".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/360_user.config /www/server/panel/plugin/monitor/360_user.config".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/config/* /www/server/monitor/config".format(
restore_path=restore_path
)
)
def restore_tamper_core_data(self, timestamp):
restore_path = self.base_path + "/{timestamp}_backup/plugin/tamper_core".format(timestamp=timestamp)
plugin_path = "/www/server/panel/plugin/tamper_core/"
if os.path.exists(restore_path) and os.path.exists(plugin_path):
public.ExecShell(
"\cp -rpa {restore_path}/tamper_push_template.json /www/server/panel/plugin/tamper_core/tamper_push_template.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/rule.json /www/server/panel/plugin/tamper_core/rule.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/config_ps.json /www/server/tamper/config_ps.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/tamper.conf /www/server/tamper/tamper.conf".format(
restore_path=restore_path
)
)
public.ExecShell("/etc/init.d/bt-tamper stop")
def restore_syssafe_data(self, timestamp):
restore_path = self.base_path + "/{timestamp}_backup/plugin/syssafe".format(timestamp=timestamp)
plugin_path = "/www/server/panel/plugin/syssafe/"
if os.path.exists(restore_path) and os.path.exists(plugin_path):
public.ExecShell(
"\cp -rpa {restore_path}/config.json /www/server/panel/plugin/syssafe/config.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/sys_process.json /www/server/panel/plugin/syssafe/sys_process.json".format(
restore_path=restore_path
)
)
public.ExecShell(
"\cp -rpa {restore_path}/config/* /www/server/panel/plugin/syssafe/config".format(
restore_path=restore_path
)
)
public.ExecShell("/etc/init.d/bt_syssafe stop")
def before_install_plugin(self, plugin_name: str):
try:
if plugin_name == "btwaf":
cmd = "ps aux | grep 'BT-WAF' | grep -v grep | awk '{print $2}'"
cmd_output = subprocess.check_output(cmd, shell=True, text=True)
pids1 = cmd_output.strip()
if pids1:
subprocess.run(["kill", "-9", str(pids1)], check=True)
xss_path = "/www/server/panel/plugin/btwaf/nginx_btwaf_xss"
lsof_output = subprocess.check_output(f"lsof -t {xss_path}", shell=True, text=True)
pids2 = lsof_output.strip().split()
for p2 in pids2:
subprocess.run(["kill", "-9", str(p2)], check=True)
time.sleep(1)
elif plugin_name == "syssafe":
public.ExecShell("/etc/init.d/bt_syssafe stop")
time.sleep(1)
elif plugin_name == "monitor":
pass
elif plugin_name == "tamper_core":
pass
time.sleep(1)
except:
pass
def install_plugin(self, sName, plugin_version):
with app.app_context():
try:
plugin = get_plugin_object()
version_parts = plugin_version.split(".", 1)
sVersion = version_parts[0]
sMin_version = version_parts[1] if len(version_parts) > 1 else ""
get = public.dict_obj()
get.sName = sName
get.version = sVersion
get.min_version = sMin_version
info = plugin.install_plugin(get)["message"]
args = public.dict_obj()
args.tmp_path = info.get("tmp_path")
args.plugin_name = sName
args.install_opt = info.get("install_opt")
info = plugin.input_package(args)
return info
except Exception:
import traceback
print(traceback.format_exc())
return {
'status': False,
'msg': public.lang('Installation failed')
}
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 2:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2]
plugin_manager = PluginModule() # 实例化对象
if hasattr(plugin_manager, method_name): # 检查方法是否存在
method = getattr(plugin_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: Method '{method_name}' not found")

View File

@@ -0,0 +1,911 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import json
import os
import shutil
import sys
import time
import warnings
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.hook_import import hook_import
hook_import()
from wp_toolkit import wpbackup
import db
from YakPanel import app
import panel_site_v2 as panelSite
from mod.project.backup_restore.data_manager import DataManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
class SiteModule(DataManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.site_dir_auth_path = "/www/server/panel/data/site_dir_auth.json"
self.redirect_conf_path = "/www/server/panel/data/redirect.conf"
self.proxy_conf_path = "/www/server/panel/data/proxyfile.json"
@staticmethod
def copy_directory(src: str, dst: str, overwrite: bool = False) -> None:
"""
site 复制文件夹
src: 源路径
dst: 目标路径
overwrite: 是否覆盖
"""
if not src or not dst:
return
if src == dst:
return
if isinstance(overwrite, int):
overwrite = False if overwrite == 0 else True
def _copy2(src_file: str, dst_file: str):
if overwrite or not os.path.exists(dst_file):
try:
shutil.copy2(src_file, dst_file)
except:
try:
public.ExecShell("chattr -i {}".format(dst_file))
shutil.copy2(src_file, dst_file)
except:
pass
# 如果源路径不存在,直接返回
if not os.path.exists(src):
return
# 确保目标目录存在
if not os.path.exists(dst):
os.makedirs(dst, 0o755, exist_ok=True)
# 复制源目录下的所有内容到目标目录
for item in os.listdir(src):
src_item = os.path.join(src, item)
dst_item = os.path.join(dst, item)
if os.path.isdir(src_item):
# 递归调用自身来复制子目录内容
SiteModule.copy_directory(src_item, dst_item, overwrite)
else:
# 复制文件
_copy2(src_item, dst_item)
@staticmethod
def _db_cp(source_db: str, target_db: str, tables: list = None) -> None:
"""
指定表复制到新db中
source_db: 源db路径
target_db: 目标db路径
tables: 要复制的表列表, None时复制所有
"""
if not source_db:
return
source_db = f"'{source_db}'"
target_db = f"'{target_db}'"
if not tables:
public.ExecShell(f"sqlite3 {source_db} .dump | sqlite3 {target_db}")
return
for table in tables:
public.ExecShell(f"sqlite3 {target_db} 'DROP TABLE IF EXISTS {table};'")
tables_for_dump = " ".join(tables)
tables_cmd = f"sqlite3 {source_db} '.dump {tables_for_dump}'"
tables_for_sql_in = ", ".join([f"'{t}'" for t in tables])
triggers_cmd = f"""sqlite3 {source_db} \"
SELECT sql || ';' FROM sqlite_master WHERE type='trigger' AND tbl_name IN ({tables_for_sql_in}) AND sql IS NOT NULL;\"
"""
res_cmd = f"({tables_cmd}; {triggers_cmd}) | sqlite3 {target_db}"
public.ExecShell(res_cmd)
public.ExecShell(f"sqlite3 {target_db} '.dump' | sqlite3 {target_db}")
@staticmethod
def chmod_dir_file(path: str, dir_mode: int = 0o755, file_mode: int = 0o644):
if not path:
return
for root, dirs, files in os.walk(path):
for d in dirs:
try:
os.chmod(os.path.join(root, d), dir_mode)
except:
continue
for f in files:
try:
os.chmod(os.path.join(root, f), file_mode)
except:
continue
if os.path.isdir(path):
try:
os.chmod(path, dir_mode)
except:
pass
elif os.path.isfile(path):
try:
os.chmod(path, file_mode)
except:
pass
def get_site_backup_conf(self, timestamp=None):
# todo node, 待优化
site_data = public.M('sites').where("project_type != ?", "Node").field('name,path,project_type,id,ps').select()
domian_data = public.M('domain').field('name,id,pid,id,port').select()
wp_onekey = public.M('wordpress_onekey').field('s_id,prefix,user,pass').select()
filtered_sites = [site for site in site_data]
filtered_domain = [name for name in domian_data]
pid_map = {}
for domain in filtered_domain:
pid = domain["pid"]
if pid not in pid_map:
pid_map[pid] = []
pid_map[pid].append(
{
"name": domain["name"],
"port": domain["port"],
}
)
for site in filtered_sites:
# domain
site_id = site["id"]
if site_id in pid_map:
site["domains"] = pid_map[site_id]
# wp prefix
hit = False
for p in wp_onekey:
try: # wp may be not exist
if p["s_id"] == site["id"] and p.get('prefix'):
site["wp_onekey"] = {
"prefix": p['prefix'],
"user": p.get('user', ''),
"pass": p.get('pass', ''),
}
hit = True
break
except:
pass
if not hit:
site["wp_onekey"] = {}
site["data_type"] = "backup"
site["status"] = 0
site["msg"] = None
return filtered_sites
def backup_site_data(self, timestamp):
data_list = self.get_backup_data_list(timestamp)
if not data_list:
return None
data_backup_path = data_list['backup_path']
site_backup_path = data_backup_path + '/site/'
if not os.path.exists(site_backup_path):
public.ExecShell('mkdir -p {}'.format(site_backup_path))
self.print_log("====================================================", 'backup')
self.print_log(public.lang("Start backing up site data"), 'backup')
self.backup_site_config(site_backup_path)
site_sql = db.Sql()
site_sql.table('sites')
domain_sql = db.Sql()
domain_sql.table('domain')
for site in data_list['data_list']['site']:
# 备份db数据库数据
site_id = site['id']
site_db_record = site_sql.where('id=?', (site_id,)).find()
site['site_db_record'] = site_db_record
# 备份网站数据
site['path'] = str(site['path']).rstrip('/')
last_path = os.path.basename(site['path'])
site["last_path"] = last_path
site_path = site_backup_path + last_path
if site["project_type"] == "PHP":
try:
site["php_ver"] = panelSite.panelSite().GetSitePHPVersion(
public.to_dict_obj({'siteName': site['name']})
)['phpversion']
except:
site["php_ver"] = None
site['status'] = 1
log_str = public.lang("Backing up {} project: {}").format(site['project_type'], site['name'])
self.print_log(log_str, "backup")
self.update_backup_data_list(timestamp, data_list)
# 备份网站项目
public.ExecShell("cp -rpa {} {}".format(site['path'], site_path))
site_zip = site_backup_path + last_path + ".zip"
public.ExecShell("cd {} && zip -r {}.zip {}".format(site_backup_path, last_path, last_path))
if os.path.exists(site_zip):
site_zip_size = public.ExecShell("du -sb {}".format(site_zip))[0].split("\t")[0]
site['data_file_name'] = site_zip
site['size'] = site_zip_size
site['zip_sha256'] = self.get_file_sha256(site_zip)
# 创建配置文件备份目录
webserver_conf_path = ["apache", "cert", "config", "nginx", "open_basedir",
"openlitespeed", "other_php", "rewrite", "ssl",
"ssl_saved", "template", "tomcat"]
conf_backup_path = site_backup_path + site['name'] + "_conf/"
public.ExecShell(f"mkdir -p '{conf_backup_path}'")
# 创建子目录
for wpath in webserver_conf_path:
web_conf_backup_path = conf_backup_path + wpath
public.ExecShell(f"mkdir -p '{web_conf_backup_path}'")
# 备份网站配置文件
self.backup_web_conf(site['name'], conf_backup_path)
# 打包网站配置文件
site_name = site['name']
site_conf_zip = site_backup_path + site_name + "_conf.zip"
public.ExecShell("cd {} && zip -r {}_conf.zip {}_conf".format(site_backup_path, site_name, site_name))
if os.path.exists(site_conf_zip):
site['conf_file_name'] = site_conf_zip
site['zip_sha256'] = self.get_file_sha256(site_conf_zip)
site['conf_sha256'] = self.get_file_sha256(site_conf_zip)
site['status'] = 2
format_backup_file_size = self.format_size(int(site['size']))
new_log_str = public.lang("{} project {} ✓ ({})").format(
site['project_type'], site['name'], format_backup_file_size
)
self.replace_log(log_str, new_log_str, 'backup')
self.update_backup_data_list(timestamp, data_list)
self.print_log(public.lang("Site data backup completed"), 'backup')
def backup_site_config(self, site_backup_path):
public.ExecShell(
"\cp -rpa /www/server/panel/data/default.db {site_backup_path}default.db".format(
site_backup_path=site_backup_path
)
)
# 备份加密访问配置文件
if os.path.exists("/www/server/panel/data/site_dir_auth.json"):
public.ExecShell(
"\cp -rpa /www/server/panel/data/site_dir_auth.json {site_backup_path}site_dir_auth.json".format(
site_backup_path=site_backup_path
)
)
# 备份加密密码
if os.path.exists("/www/server/pass/"):
public.ExecShell(
"\cp -rpa /www/server/pass/ {site_backup_path}pass/".format(
site_backup_path=site_backup_path
)
)
# 备份反代配置
if os.path.exists("/www/server/proxy_project/sites"):
public.ExecShell("mkdir -p {site_backup_path}proxy_project/".format(site_backup_path=site_backup_path))
public.ExecShell(
"\cp -rpa /www/server/proxy_project/sites {site_backup_path}proxy_project/sites/".format(
site_backup_path=site_backup_path
)
)
# 备份重定向配置
if os.path.exists("/www/server/panel/data/redirect.conf"):
public.ExecShell(
"\cp -rpa /www/server/panel/data/redirect.conf {site_backup_path}redirect.conf".format(
site_backup_path=site_backup_path
)
)
if os.path.exists("/www/server/panel/data/proxyfile.json"):
public.ExecShell(
"\cp -rpa /www/server/panel/data/proxyfile.json {site_backup_path}proxyfile.json".format(
site_backup_path=site_backup_path
)
)
# 备份wp加速配置文件
if os.path.exists("/www/server/nginx/conf/"):
nginx_conf_list = os.listdir("/www/server/nginx/conf/")
for nginx_conf_name in nginx_conf_list:
if "wpfastcgi" in nginx_conf_name:
public.ExecShell(
"\cp -rpa /www/server/nginx/conf/{nginx_conf_name} {site_backup_path}{nginx_conf_name}".format(
nginx_conf_name=nginx_conf_name, site_backup_path=site_backup_path
)
)
# 备份well-known文件
if os.path.exists("/www/server/panel/vhost/nginx/well-known"):
public.ExecShell(
"\cp -rpa /www/server/panel/vhost/nginx/well-known {site_backup_path}/well-known".format(
site_backup_path=site_backup_path
)
)
public.ExecShell("mkdir -p {site_backup_path}/monitor_conf/".format(site_backup_path=site_backup_path))
public.ExecShell(
"\cp -rpa /www/server/panel/vhost/nginx/0.monitor*.conf {site_backup_path}/monitor_conf/".format(
site_backup_path=site_backup_path
)
)
def restore_site_config(self, backup_path):
default_db_file = backup_path + "default.db"
dir_auth_file = backup_path + "site_dir_auth.json"
pass_path = backup_path + "pass"
proxy_project_path = backup_path + "proxy_project"
redirect_file = backup_path + "redirect.conf"
proxyfile_file = backup_path + "proxyfile.json"
if os.path.exists(default_db_file) and self.overwrite:
panel_current = public.S("users").find()
public.ExecShell(f"\cp -rpa {default_db_file} /www/server/panel/data/default.db")
os.chmod("/www/server/panel/data/default.db", 0o600)
if "id" in panel_current:
del panel_current["id"]
public.S("users").where("id=?", (1,)).update(panel_current)
if os.path.exists(pass_path):
self.copy_directory(
src=pass_path,
dst="/www/server/pass",
overwrite=self.overwrite,
)
self.chmod_dir_file("/www/server/pass", file_mode=0o644)
if os.path.exists(proxy_project_path):
target = "/www/server/proxy_project"
self.copy_directory(
src=proxy_project_path,
dst=target,
overwrite=self.overwrite,
)
self.chmod_dir_file(target, file_mode=0o644)
if os.path.exists(dir_auth_file):
target = "/www/server/panel/data/site_dir_auth.json"
if not os.path.exists(target) or self.overwrite:
public.ExecShell(f"\cp -rpa {dir_auth_file} /www/server/panel/data/site_dir_auth.json")
self.chmod_dir_file(target, file_mode=0o600)
if os.path.exists(redirect_file):
target = "/www/server/panel/data/redirect.conf"
if not os.path.exists(target) or self.overwrite:
public.ExecShell(f"\cp -rpa {redirect_file} /www/server/panel/data/redirect.conf")
self.chmod_dir_file(target, file_mode=0o600)
if os.path.exists(proxyfile_file):
target = "/www/server/panel/data/proxyfile.json"
if not os.path.exists(target) or self.overwrite:
public.ExecShell(f"\cp -rpa {proxyfile_file} /www/server/panel/data/proxyfile.json")
self.chmod_dir_file(target, file_mode=0o600)
public.ExecShell(f"\cp -rpa {backup_path}/*wpfastcgi.conf /www/server/nginx/conf/")
self.chmod_dir_file("/www/server/nginx/conf", file_mode=0o644)
if os.path.exists(backup_path + "well-known"):
target = "/www/server/panel/vhost/nginx/well-known"
if not os.path.exists(target):
public.ExecShell(f"mkdir -p {target}")
public.ExecShell(f"\cp -rpa {backup_path}well-known/* /www/server/panel/vhost/nginx/well-known/")
self.chmod_dir_file(target, dir_mode=0o600, file_mode=0o600)
public.ExecShell(f"\cp -rpa {backup_path}monitor_conf/* /www/server/panel/vhost/nginx/")
self.chmod_dir_file("/www/server/panel/vhost/nginx", dir_mode=0o600, file_mode=0o600)
def restore_site_python_env(self, timestamp):
self.print_log("================================================", "restore")
self.print_log(public.lang("Starting to restore site Python dependencies..."), 'restore')
restore_data = self.get_restore_data_list(timestamp)
site_data = restore_data['data_list']['site']
for site in site_data:
if site['project_type'] == 'Python':
python_site_config = site['site_db_record']['project_config']
requirement_path = json.loads(python_site_config)['requirement_path']
vpath = json.loads(python_site_config)['vpath']
if requirement_path:
pip3_path = vpath + "/bin/pip3"
pip2_path = vpath + "/bin/pip2"
pip_install_cmd = None
if os.path.exists(pip3_path):
pip_install_cmd = "{} install -r {}".format(pip3_path, requirement_path)
elif os.path.exists(pip2_path):
pip_install_cmd = "{} install -r {}".format(pip2_path, requirement_path)
if pip_install_cmd:
public.ExecShell(pip_install_cmd)
self.print_log(public.lang("Site Python dependencies restoration completed"), 'restore')
def backup_web_conf(self, site_name: str, conf_backup_path: str) -> None:
"""备份网站配置文件
Args:
site_name: 网站名称
conf_backup_path: 配置文件备份路径
"""
# 定义需要备份的配置文件和路径映射
conf_paths = {
'cert': "/www/server/panel/vhost/cert/{site_name}".format(site_name=site_name),
'rewrite': "/www/server/panel/vhost/rewrite/{site_name}.conf".format(site_name=site_name),
'nginx': {
'main': "/www/server/panel/vhost/nginx/{site_name}.conf".format(site_name=site_name),
'redirect': "/www/server/panel/vhost/nginx/redirect/{site_name}".format(site_name=site_name),
'proxy': "/www/server/panel/vhost/nginx/proxy/{site_name}".format(site_name=site_name),
'dir_auth': "/www/server/panel/vhost/nginx/dir_auth/{site_name}".format(site_name=site_name)
},
'apache': {
'main': "/www/server/panel/vhost/apache/{site_name}.conf".format(site_name=site_name),
'redirect': "/www/server/panel/vhost/apache/redirect/{site_name}".format(site_name=site_name),
'proxy': "/www/server/panel/vhost/apache/proxy/{site_name}".format(site_name=site_name),
'dir_auth': "/www/server/panel/vhost/apache/dir_auth/{site_name}".format(site_name=site_name)
},
'openlitespeed': {
'main': '/www/server/panel/vhost/openlitespeed',
'detail': '/www/server/panel/vhost/openlitespeed/detail',
'listen': '/www/server/panel/vhost/openlitespeed/listen',
'ssl': '/www/server/panel/vhost/openlitespeed/detail/ssl',
},
}
# 备份证书
if os.path.exists(conf_paths['cert']):
public.ExecShell(f"mkdir -p {conf_backup_path}cert/")
public.ExecShell(f"\cp -rpa {conf_paths['cert']} {conf_backup_path}cert/")
# 备份伪静态
if os.path.exists(conf_paths['rewrite']):
public.ExecShell(f"\cp -rpa {conf_paths['rewrite']} {conf_backup_path}rewrite")
rewrite_file_list = os.listdir("/www/server/panel/vhost/rewrite/")
for rewrite_file in rewrite_file_list:
if rewrite_file.endswith(".conf"):
if site_name in rewrite_file:
public.ExecShell(
f"\cp -rpa /www/server/panel/vhost/rewrite/{rewrite_file} {conf_backup_path}rewrite"
)
# 备份nginx配置
nginx_paths = conf_paths['nginx']
if os.path.exists(nginx_paths['main']):
public.ExecShell(f"\cp -rpa {nginx_paths['main']} {conf_backup_path}nginx/")
if not os.path.exists(nginx_paths['main']):
web_conf_list = os.listdir("/www/server/panel/vhost/nginx/")
for web_conf_name in web_conf_list:
if web_conf_name.endswith(".conf"):
if site_name in web_conf_name:
public.ExecShell(
f"\cp -rpa /www/server/panel/vhost/nginx/{web_conf_name} {conf_backup_path}nginx/"
)
if os.path.exists(nginx_paths['redirect']):
public.ExecShell(f"mkdir -p {conf_backup_path}nginx/redirect/{site_name}/")
public.ExecShell(f"\cp -rpa {nginx_paths['redirect']}/* {conf_backup_path}nginx/redirect/{site_name}/")
if os.path.exists(nginx_paths['proxy']):
public.ExecShell(f"mkdir -p {conf_backup_path}nginx/proxy/{site_name}/")
public.ExecShell(f"\cp -rpa {nginx_paths['proxy']}/* {conf_backup_path}nginx/proxy/{site_name}/")
if os.path.exists(nginx_paths['dir_auth']):
public.ExecShell(f"mkdir -p {conf_backup_path}nginx/dir_auth/{site_name}/")
public.ExecShell(f"\cp -rpa {nginx_paths['dir_auth']}/* {conf_backup_path}nginx/dir_auth/{site_name}/")
# 备份apache配置
apache_paths = conf_paths['apache']
if os.path.exists(apache_paths['main']):
public.ExecShell(f"\cp -rpa {apache_paths['main']} {conf_backup_path}apache/")
if not os.path.exists(apache_paths['main']):
web_conf_list = os.listdir("/www/server/panel/vhost/apache/")
for web_conf_name in web_conf_list:
if web_conf_name.endswith(".conf"):
if site_name in web_conf_name:
public.ExecShell(
f"\cp -rpa /www/server/panel/vhost/apache/{web_conf_name} {conf_backup_path}apache/"
)
if os.path.exists(apache_paths['redirect']):
public.ExecShell(f"mkdir -p {conf_backup_path}apache/redirect/{site_name}/")
public.ExecShell(f"\cp -rpa {apache_paths['redirect']}/* {conf_backup_path}apache/redirect/{site_name}/")
if os.path.exists(apache_paths['proxy']):
public.ExecShell(f"mkdir -p {conf_backup_path}apache/proxy/{site_name}/")
public.ExecShell(f"\cp -rpa {apache_paths['proxy']}/* {conf_backup_path}apache/proxy/{site_name}/")
if os.path.exists(apache_paths['dir_auth']):
public.ExecShell(f"mkdir -p {conf_backup_path}apache/dir_auth/{site_name}/")
public.ExecShell(f"\cp -rpa {apache_paths['dir_auth']}/* {conf_backup_path}apache/dir_auth/{site_name}/")
# 备份openlitespeed配置
ols_paths = conf_paths['openlitespeed']
if os.path.exists(ols_paths['main']):
for web_conf_name in os.listdir(ols_paths['main']):
if site_name in web_conf_name:
public.ExecShell(
f"\cp -rpa /www/server/panel/vhost/openlitespeed/{web_conf_name} {conf_backup_path}openlitespeed/"
)
if os.path.exists(conf_paths['openlitespeed']['detail']):
public.ExecShell(f"mkdir -p {conf_backup_path}openlitespeed/detail")
for detail in os.listdir(conf_paths['openlitespeed']['detail']):
if site_name in detail:
public.ExecShell(
f"\cp -rpa {ols_paths['main']}/detail/{detail} {conf_backup_path}openlitespeed/detail/"
)
if os.path.exists(conf_paths['openlitespeed']['listen']):
public.ExecShell(
f"cp -rpa {conf_paths['openlitespeed']['listen']} {conf_backup_path}openlitespeed/listen"
)
if os.path.exists(conf_paths['openlitespeed']['ssl']):
public.ExecShell(f"mkdir -p {conf_backup_path}openlitespeed/detail/ssl")
for ssl in os.listdir(conf_paths['openlitespeed']['ssl']):
if site_name in ssl:
public.ExecShell(
f"cp -rpa {conf_paths['openlitespeed']['ssl']}/{ssl} {conf_backup_path}openlitespeed/detail/ssl/{ssl}"
)
def restore_web_conf(self, site_name: str, conf_backup_path: str) -> None:
"""还原网站配置文件
Args:
site_name: 网站名称
conf_backup_path: 配置文件备份路径
"""
# 定义需要还原的配置文件和路径映射
conf_paths = {
'cert': "/www/server/panel/vhost/cert/{site_name}".format(site_name=site_name),
'rewrite': "/www/server/panel/vhost/rewrite/{site_name}.conf".format(site_name=site_name),
'nginx': {
'main': "/www/server/panel/vhost/nginx/{site_name}.conf".format(site_name=site_name),
'redirect': "/www/server/panel/vhost/nginx/redirect/{site_name}".format(site_name=site_name),
'proxy': "/www/server/panel/vhost/nginx/proxy/{site_name}".format(site_name=site_name)
},
'apache': {
'main': "/www/server/panel/vhost/apache/{site_name}.conf".format(site_name=site_name),
'redirect': "/www/server/panel/vhost/apache/redirect/{site_name}".format(site_name=site_name),
'proxy': "/www/server/panel/vhost/apache/proxy/{site_name}".format(site_name=site_name)
},
'openlitespeed': {
'main': '/www/server/panel/vhost/openlitespeed',
'detail': '/www/server/panel/vhost/openlitespeed/detail',
'listen': '/www/server/panel/vhost/openlitespeed/listen',
'ssl': '/www/server/panel/vhost/openlitespeed/detail/ssl',
},
}
# 还原证书
if os.path.exists(f"{conf_backup_path}cert"):
public.ExecShell(f"\cp -rpa {conf_backup_path}cert {conf_paths['cert']}")
# 还原伪静态
if os.path.exists(f"{conf_backup_path}rewrite"):
public.ExecShell(f"\cp -rpa {conf_backup_path}rewrite {conf_paths['rewrite']}")
# 还原nginx配置
if os.path.exists(f"{conf_backup_path}nginx"):
public.ExecShell(f"\cp -rpa {conf_backup_path}nginx {conf_paths['nginx']['main']}")
if os.path.exists(f"{conf_backup_path}nginx/redirect"):
public.ExecShell(f"\cp -rpa {conf_backup_path}nginx/redirect {conf_paths['nginx']['redirect']}")
if os.path.exists(f"{conf_backup_path}nginx/proxy"):
public.ExecShell(f"\cp -rpa {conf_backup_path}nginx/proxy {conf_paths['nginx']['proxy']}")
# 还原apache配置
if os.path.exists(f"{conf_backup_path}apache"):
public.ExecShell(f"\cp -rpa {conf_backup_path}apache {conf_paths['apache']['main']}")
if os.path.exists(f"{conf_backup_path}apache/redirect"):
public.ExecShell(f"\cp -rpa {conf_backup_path}apache/redirect {conf_paths['apache']['redirect']}")
if os.path.exists(f"{conf_backup_path}apache/proxy"):
public.ExecShell(f"\cp -rpa {conf_backup_path}apache/proxy {conf_paths['apache']['proxy']}")
# 还原openlitespeed配置
if os.path.exists(f"{conf_backup_path}openlitespeed"):
for web_cf_name in os.listdir(f"{conf_backup_path}openlitespeed"):
if site_name in web_cf_name:
public.ExecShell(
f"\cp -rpa {conf_backup_path}openlitespeed/{web_cf_name} {conf_paths['openlitespeed']['main']}/{web_cf_name}"
)
detail_path = f"{conf_backup_path}openlitespeed/detail"
if os.path.exists(detail_path):
if not os.path.exists(conf_paths['openlitespeed']['detail']):
public.ExecShell(f"mkdir -p {conf_paths['openlitespeed']['detail']}")
for detail in os.listdir(detail_path):
if site_name in detail:
public.ExecShell(
f"\cp -rpa {detail_path}/{detail} {conf_paths['openlitespeed']['detail']}/{detail}"
)
listen_path = f"{conf_backup_path}openlitespeed/listen"
if os.path.exists(listen_path):
public.ExecShell(
f"\cp -rpa {listen_path} {conf_paths['openlitespeed']['listen']}/listen"
)
ssl_path = f"{conf_backup_path}openlitespeed/detail/ssl"
if os.path.exists(ssl_path):
if not os.path.exists(conf_paths['openlitespeed']['ssl']):
public.ExecShell(f"mkdir -p {conf_paths['openlitespeed']['ssl']}")
for ssl in os.listdir(ssl_path):
if site_name in ssl:
public.ExecShell(
f"\cp -rpa {ssl_path}/{ssl} {conf_paths['openlitespeed']['ssl']}/{ssl}"
)
def _restore_site_db_data(self, site_db_record: dict) -> int:
sql = db.Sql()
sql.table('sites')
if 'id' in site_db_record:
del site_db_record['id']
# SQL关键字字段
if 'index' in site_db_record:
del site_db_record['index']
if_exist = sql.where(
'name=? AND project_type=?',
(site_db_record['name'], site_db_record['project_type'])
).find()
if if_exist:
return if_exist['id']
# insert db record
try:
new_id = sql.insert(site_db_record)
return new_id
except Exception as e:
raise public.lang("Site database insert failed: {}").format(str(e))
def _restore_site_domian_db_data(self, pid: int, domains: list) -> None:
domain_sql = db.Sql()
domain_sql.table('domain')
for domain in domains:
try:
if not domain_sql.where('name=?', (domain['name'],)).count():
domain_sql.add(
'pid, name, port, addtime',
(pid, domain['name'], int(domain['port']), public.getDate())
)
except Exception as e:
public.print_log("Domain database insert failed: {}".format(str(e)))
continue
def _backup_site(self, site: dict, backupPath: str) -> None:
try:
if site.get("project_type", "").lower() == "php":
find = public.M('sites').where("id=?", (site['id'],)).field('name,path,id').find()
fileName = find['name'] + '_' + time.strftime(
'%Y%m%d_%H%M%S', time.localtime()
) + '.zip'
zipName = backupPath + '/' + fileName
if not (os.path.exists(backupPath)):
os.makedirs(backupPath)
tmps = '/tmp/panelExec.log'
execStr = f"cd '{find['path']}' && zip '{zipName}' . -x .user.ini > {tmps} 2>&1"
public.ExecShell(execStr)
public.M('backup').add(
'type,name,pid,filename,size,addtime',
(0, fileName, find['id'], zipName, 0, public.getDate())
)
elif "wp" in site.get("project_type", "").lower():
bak_obj = wpbackup(int(site['id']))
bak_obj.backup_full()
except:
pass
def restore_site_data(self, timestamp: str) -> None:
"""还原站点数据
Args:
timestamp: 备份时间戳
"""
restore_data = self.get_restore_data_list(timestamp)
site_backup_path = self.base_path + "/{timestamp}_backup/site/".format(timestamp=timestamp)
# 还原site环境配置, 全局配置
self.restore_site_config(site_backup_path)
if not os.path.exists(site_backup_path):
self.print_log(public.lang("Site backup directory does not exist: {}").format(site_backup_path), 'restore')
return
self.print_log("====================================================", "restore")
self.print_log(public.lang("Start restoring site data"), 'restore')
backupPath = public.M('config').where('id=?', (1,)).getField('backup_path')
backupPath = backupPath + '/site/' if backupPath else "/www/backup/site/"
with app.app_context():
for site in restore_data['data_list']['site']:
log_str = public.lang("Restoring {} project: {}").format(site.get("project_type"), site.get("name"))
try:
site_name = site['name']
site['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
self.print_log(log_str, 'restore')
if self.overwrite:
# site backup if overwrite
self._backup_site(site, backupPath)
# data
if not self.overwrite and 'site_db_record' in site:
site_id = self._restore_site_db_data(site['site_db_record'])
if site_id and 'domains' in site:
# 还原域名记录
self._restore_site_domian_db_data(site_id, site['domains'])
# site file
site_path = str(site['path']).rstrip('/')
last_path: str = os.path.basename(site_path) if site['last_path'] == '' else site['last_path'] # site name
# site abs path
site_zip = site_backup_path + last_path + ".zip"
if os.path.exists(site_zip):
public.ExecShell(f"cd {site_backup_path} && unzip -o {last_path}.zip")
site_data_path = site_backup_path + last_path # site unzip file
if os.path.exists(site_data_path):
site_parent_path = os.path.dirname(site_path) # /www/wwwroot
if not os.path.exists(site_parent_path):
public.ExecShell("mkdir -p {}".format(site_parent_path))
public.ExecShell("chown -R www:www {}".format(site_parent_path))
public.ExecShell("chmod -R 755 {}".format(site_parent_path))
src_site = os.path.join(site_backup_path, last_path)
dst_site = os.path.join(site_parent_path, last_path)
public.print_log('copying site directory from {} to {}'.format(src_site, dst_site))
self.copy_directory(
src=src_site,
dst=dst_site,
overwrite=self.overwrite,
)
try:
shutil.rmtree(src_site)
except Exception as e:
public.print_log(public.lang("Failed to delete source site directory: {}").format(str(e)))
# makesure
public.ExecShell(f"chown -R www:www {dst_site}")
user_ini = dst_site + "/.user.ini"
if not os.path.exists(user_ini) or self.overwrite:
public.writeFile(user_ini, f"open_basedir={site_parent_path}/:/tmp/")
public.ExecShell("chmod 644 " + user_ini)
public.ExecShell("chown root:root " + user_ini)
public.ExecShell("chattr +i " + user_ini)
# site config
site_conf_zip = site_backup_path + site_name + "_conf.zip"
if os.path.exists(site_conf_zip):
public.ExecShell(
"cd {site_backup_path} && unzip -o {site_name}_conf.zip".format(
site_backup_path=site_backup_path, site_name=site_name
)
)
public.ExecShell(
"cd {site_backup_path} && \cp -rpa {site_name}_conf/* /www/server/panel/vhost".format(
site_backup_path=site_backup_path, site_name=site_name
)
)
new_log_str = public.lang("{} project: {}").format(site['project_type'], site['name'])
self.replace_log(log_str, new_log_str, 'restore')
site['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
except Exception as e:
site['restore_status'] = 3
self.update_restore_data_list(timestamp, restore_data)
new_log_str = public.lang(f"{site['project_type']} project: {site['name']} Reason: {str(e)}")
self.replace_log(log_str, new_log_str, 'restore')
continue
self.print_log(public.lang("Site data restoration completed"), 'restore')
# 还原site 所有Python环境
# self.restore_site_python_env(timestamp)
def backup_site_dir_auth(self, site_name: str):
if os.path.exists(self.site_dir_auth_path):
site_dir_auth_data = json.loads(public.ReadFile(self.site_dir_auth_path))
if site_name in site_dir_auth_data:
result = {site_name: site_dir_auth_data[site_name]}
return result
return False
def restore_site_dir_auth(self, site_name: str, backup_data_path: str):
if os.path.exists(backup_data_path):
dir_auth_backup_data = json.loads(public.ReadFile(backup_data_path))
if os.path.exists(self.site_dir_auth_path):
site_dir_auth_data = json.loads(public.ReadFile(self.site_dir_auth_path))
site_dir_auth_data[site_name] = dir_auth_backup_data[site_name]
public.WriteFile(self.site_dir_auth_path, json.dumps(site_dir_auth_data))
def backup_dir_pass(self, site_name: str, backup_data_path: str):
if os.path.exists(self.site_dir_auth_path):
site_dir_auth_data = json.loads(public.ReadFile(self.site_dir_auth_path))
if site_name in site_dir_auth_data:
result = {site_name: site_dir_auth_data[site_name]}
return result
return {}
def backup_redirect_conf(self, site_name: str):
if os.path.exists(self.redirect_conf_path):
redirect_conf_data = json.loads(public.ReadFile(self.redirect_conf_path))
for item in redirect_conf_data:
if site_name in item['sitename']:
return item
return False
def restore_redirect_conf(self, site_name: str, backup_data_path: str):
if os.path.exists(backup_data_path):
redirect_conf_data = json.loads(public.ReadFile(backup_data_path))
local_redirect_conf_data = []
if os.path.exists(self.redirect_conf_path):
local_redirect_conf_data = json.loads(public.ReadFile(self.redirect_conf_path))
data_exists = None
for item in local_redirect_conf_data:
if item['sitename'] == redirect_conf_data['sitename']:
data_exists = True
if not data_exists:
local_redirect_conf_data.append(redirect_conf_data)
public.WriteFile(self.redirect_conf_path, json.dumps(local_redirect_conf_data))
return False
def backup_proxy_conf(self, site_name: str):
if os.path.exists(self.proxy_conf_path):
proxy_conf_data = json.loads(public.ReadFile(self.proxy_conf_path))
for item in proxy_conf_data:
if site_name in item['sitename']:
return item
return False
def restore_proxy_conf(self, site_name: str, backup_data_path: str):
if os.path.exists(backup_data_path):
proxy_conf_data = json.loads(public.ReadFile(backup_data_path))
local_proxy_conf_data = []
if os.path.exists(self.proxy_conf_path):
local_proxy_conf_data = json.loads(public.ReadFile(self.proxy_conf_path))
data_exists = None
for item in local_proxy_conf_data:
if item['sitename'] == proxy_conf_data['sitename']:
data_exists = True
if not data_exists:
local_proxy_conf_data.append(proxy_conf_data)
public.WriteFile(self.proxy_conf_path, json.dumps(local_proxy_conf_data))
return False
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 3:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2] # IP地址
site_module = SiteModule() # 实例化对象
if hasattr(site_module, method_name): # 检查方法是否存在
method = getattr(site_module, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: Method '{method_name}' 'does not exist'")

View File

@@ -0,0 +1,829 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import json
import os
import re
import sys
import warnings
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 YakPanel import app
from mod.project.backup_restore.base_util import BaseUtil
from mod.project.backup_restore.config_manager import ConfigManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
OFFICIAL_URL = public.OfficialDownloadBase()
class SoftModule(BaseUtil, ConfigManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.packet = False
def get_install_type(self):
if os.path.exists("/usr/bin/yum") or os.path.exists("/usr/bin/dnf") or os.path.exists("/usr/sbin/yum"):
return 1
elif os.path.exists(
"/usr/bin/apt"
) or os.path.exists(
"/usr/sbin/apt-get"
) or os.path.exists(
"/usr/bin/apt-get"
):
return 4
else:
return 0
def get_web_server(self):
if os.path.exists("/www/server/nginx/sbin/nginx"):
nginx_version = public.ExecShell("nginx -v 2>&1")[0].replace("\n", "")
version_match = re.search(r'nginx/(\d+\.\d+)', nginx_version)
if version_match:
nginx_version = version_match.group(1)
result = {
"name": "nginx",
"version": nginx_version,
"size": BaseUtil().get_file_size("/www/server/nginx/"),
"status": 2,
}
self.print_log("nginx {}".format(nginx_version), 'backup')
return result
if os.path.exists("/www/server/apache/bin/httpd"):
apache_version = public.ExecShell("httpd -v 2>&1")[0].replace("\n", "")
version_match = re.search(r'Apache/(\d+\.\d+)', apache_version)
if version_match:
apache_version = version_match.group(1)
result = {
"name": "apache",
"version": apache_version,
"size": BaseUtil().get_file_size("/www/server/apache/"),
"status": 2,
}
self.print_log("apache {}".format(apache_version), 'backup')
return result
if os.path.exists("/usr/local/lsws/bin/openlitespeed"):
openlitespeed_version = public.ExecShell(
"/usr/local/lsws/bin/openlitespeed -v 2>&1")[0].replace("\n", "")
version_match = re.search(r'LiteSpeed/(\d+\.\d+\.\d+) Open', openlitespeed_version)
if version_match:
openlitespeed_version = version_match.group(1)
result = {
"name": "openlitespeed",
"version": openlitespeed_version,
"size": BaseUtil().get_file_size("/usr/local/lsws/"),
"status": 2,
}
self.print_log("openlitespeed {}".format(openlitespeed_version), 'backup')
return result
def get_php_server(self):
php_dir = "/www/server/php"
if os.path.exists(php_dir):
phplist = []
for dir_name in os.listdir(php_dir):
dir_path = 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')):
phplist.append(int(dir_name))
result = []
for php_ver in phplist:
php_ext = public.ExecShell("/www/server/php/{}/bin/php -m".format(php_ver))[0].split("\n")
filtered_data = [item for item in php_ext if item not in ('[PHP Modules]', '[Zend Modules]', '')]
php_result = {
"name": "php",
"version": php_ver,
"php_ext": filtered_data,
"size": BaseUtil().get_file_size("/www/server/php/{}".format(php_ver)),
"status": 2,
}
# 将PHP版本号转换为带小数点的格式
if isinstance(php_ver, (int, str)) and len(str(php_ver)) == 2:
# 例如54 -> 5.4, 70 -> 7.0
php_result['version'] = f"{str(php_ver)[0]}.{str(php_ver)[1]}"
elif isinstance(php_ver, (int, str)) and len(str(php_ver)) == 3:
# 例如82 -> 8.2
php_result['version'] = f"{str(php_ver)[0]}.{str(php_ver)[1:]}"
result.append(php_result)
self.print_log("php {}".format(php_result['version']), 'backup')
return result
return None
def get_mysql_server(self):
if os.path.exists("/www/server/mysql/bin/mysql"):
mysql_version = None
if os.path.exists("/www/server/mysql/version.pl"):
mysql_version = public.ReadFile("/www/server/mysql/version.pl").replace("\n", "")
elif os.path.exists("/www/server/mysql/version_check.pl"):
mysql_version = public.ExecShell("/www/server/mysql/version_check.pl")[0].replace("\n", "")
match = re.search(r'10\.\d+', mysql_version)
if match:
version = match.group()
type = "mariadb"
mysql_version = version
else:
type = "mysql"
mysql_version = mysql_version[0:3]
result = {
"type": type,
"version": mysql_version,
"size": BaseUtil().get_file_size("/www/server/mysql/"),
"status": 2,
}
self.print_log("mysql {}".format(mysql_version), 'backup')
return result
else:
return False
def get_ftp_server(self, get=None):
if os.path.exists("/www/server/pure-ftpd/bin/pure-pw"):
size = BaseUtil().get_file_size("/www/server/pure-ftpd/")
try:
pure_ftp_port = \
public.ExecShell("cat /www/server/pure-ftpd/etc/pure-ftpd.conf | grep Bind|awk '{print $2}'")[
0].replace("\n", "").replace("0.0.0.0,", "")
pure_ftp_port = int(pure_ftp_port)
except:
pure_ftp_port = 21
self.print_log("pure-ftpd {}".format(pure_ftp_port), 'backup')
return {
"name": "pure-ftpd",
"version": "1.0.49",
"size": size,
"port": int(pure_ftp_port),
"status": 2,
}
else:
return None
def get_node_list(self, timestamp):
node_dir = "/www/server/nodejs"
if not os.path.exists(node_dir):
return None
node_list = []
result = []
for dir_name in os.listdir(node_dir):
if re.match(r"^v[1-9]\d*(\.\d+)*$", dir_name):
node_list.append(dir_name)
for node_ver in node_list:
node_ver_path = os.path.join(node_dir, node_ver)
node_mod_path = os.path.join(node_ver_path, "lib", "node_modules")
if os.path.isdir(node_mod_path):
mod_list = os.listdir(node_mod_path)
else:
mod_list = []
node_result = {
"name": "node",
"version": node_ver,
"mod_list": mod_list,
"size": BaseUtil().get_file_size("/www/server/nodejs/{}".format(node_ver)),
"status": 2,
}
result.append(node_result)
self.print_log("node {}".format(node_ver), 'backup')
if result and self.packet:
backup_path = os.path.join(self.base_path, f"{timestamp}_backup/plugin")
public.ExecShell(f"\cp -rpa /www/server/nodejs/* {backup_path}/nodejs/*")
return result
def get_redis_server(self):
if os.path.exists("/www/server/redis/src/redis-server") and os.path.exists("/www/server/redis/version.pl"):
redis_version = public.ReadFile("/www/server/redis/version.pl")
size = BaseUtil().get_file_size("/www/server/redis/")
self.print_log("redis {}".format(redis_version[0:3]), 'backup')
return {
"name": "redis",
"version": redis_version[0:3],
"size": size,
"status": 2,
}
else:
return None
def get_memcached_server(self):
if os.path.exists("/usr/local/memcached/bin/memcached"):
size = BaseUtil().get_file_size("/usr/local/memcached/")
self.print_log("memcached {}".format("1.6.12"), 'backup')
return {
"name": "memcached",
"version": "1.6.12",
"size": size,
"status": 2,
}
else:
return None
def get_mongodb_server(self):
if os.path.exists("/www/server/mongodb/version.pl"):
mongod = "/www/server/mongodb/bin/mongod"
mongo = "/www/server/mongodb/bin/mongo"
if os.path.exists(mongod) or os.path.exists(mongo):
mongodb_version = public.ReadFile("/www/server/mongodb/version.pl")
size = BaseUtil().get_file_size("/www/server/mongodb/")
self.print_log("mongodb {}".format(mongodb_version[0:3]), 'backup')
return {
"name": "mongodb",
"version": mongodb_version[0:3],
"size": size,
"status": 2,
}
else:
return None
def get_pgsql_server(self):
if os.path.exists("/www/server/pgsql/bin/pg_config"):
pgsql_version = \
public.ExecShell("/www/server/pgsql/bin/pg_config --version")[0].replace("\n", "").split(" ")[1]
size = BaseUtil().get_file_size("/www/server/pgsql/")
self.print_log("pgsql {}".format(pgsql_version), 'backup')
return {
"name": "pgsql",
"version": pgsql_version,
"size": size,
"status": 2,
}
else:
return None
def get_phpmyadmin_version(self):
if os.path.exists("/www/server/phpmyadmin/version.pl"):
phpmyadmin_version = public.ReadFile("/www/server/phpmyadmin/version.pl").replace("\n", "")
size = BaseUtil().get_file_size("/www/server/phpmyadmin/")
self.print_log("phpmyadmin {}".format(phpmyadmin_version), 'backup')
return {
"name": "phpmyadmin",
"version": phpmyadmin_version,
"size": size,
"status": 2,
}
else:
return None
def get_soft_data(self, timestamp=None, packet: bool = False):
self.print_log("====================================================", "backup")
self.print_log(public.lang("Start backing up software information"), "backup")
self.packet = packet
result = {
"web_server": self.get_web_server(),
"php_server": self.get_php_server(),
"mysql_server": self.get_mysql_server(),
"ftp_server": self.get_ftp_server(),
# "node_list": self.get_node_list(timestamp),
"redis_server": self.get_redis_server(),
"memcached_server": self.get_memcached_server(),
"mongodb_server": self.get_mongodb_server(),
"pgsql_server": self.get_pgsql_server(),
"phpmyadmin_version": self.get_phpmyadmin_version(),
}
public.WriteFile("/root/soft.json", json.dumps(result))
self.print_log(public.lang("Software information backup completed"), 'backup')
return result
# ======================== install software ========================
def install_web_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
web_server = restore_data['data_list']['soft']['web_server']
install_type = self.get_install_type()
log_str = public.lang("Start installing nginx-{}").format(web_server.get('version', 'latest'))
try:
result = None
if web_server['name'] == 'nginx':
self.print_log(log_str, "restore")
web_server['restore_status'] = 1
web_server['msg'] = None
self.update_restore_data_list(timestamp, restore_data)
result = public.ExecShell(
"cd /www/server/panel/install && wget -O nginx.sh {}/install/{}/nginx.sh && bash nginx.sh install {}".format(
OFFICIAL_URL, install_type, web_server['version']
)
)
elif web_server['name'] == 'apache':
self.print_log(public.lang("Start installing apache service"), "restore")
web_server['restore_status'] = 1
web_server['msg'] = None
self.update_restore_data_list(timestamp, restore_data)
result = public.ExecShell(
"cd /www/server/panel/install && wget -O apache.sh {}/install/{}/apache.sh && bash apache.sh install {}".format(
OFFICIAL_URL, install_type, web_server['version']
)
)
if web_server['name'] == 'nginx' and os.path.exists("/www/server/nginx/sbin/nginx"):
new_log_str = "{}-{}".format(web_server['name'], web_server['version'])
self.replace_log(log_str, new_log_str, "restore")
web_server['restore_status'] = 2
web_server['msg'] = None
self.update_restore_data_list(timestamp, restore_data)
elif web_server['name'] == 'apache' and os.path.exists("/www/server/apache/bin/httpd"):
new_log_str = "{}-{}".format(web_server['name'], web_server['version'])
self.replace_log(log_str, new_log_str, "restore")
web_server['restore_status'] = 2
web_server['msg'] = None
self.update_restore_data_list(timestamp, restore_data)
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"{}-{} ✗ Installation failed Reason: {} \n Please try to reinstall the web server in the software store after the restore task ends").format(
web_server['name'], web_server['version'], err_msg
)
self.replace_log(log_str, new_log_str, "restore")
web_server['restore_status'] = 3
web_server['msg'] = new_log_str
self.update_restore_data_list(timestamp, restore_data)
except Exception as e:
err_msg = public.lang(
"{}-{} ✗ Installation failed Reason: {} \n Please try to reinstall the web server in the software store after the restore task ends").format(
web_server['name'], web_server['version'], str(e)
)
web_server['restore_status'] = 3
web_server['msg'] = err_msg
self.update_restore_data_list(timestamp, restore_data)
self.print_log(public.lang("Web server installation completed"), "restore")
def install_php_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
php_server = restore_data['data_list']['soft']['php_server']
install_type = self.get_install_type()
for php in php_server:
php_ver = php['version']
self.update_restore_data_list(timestamp, restore_data)
log_str = public.lang("Start installing php-{}").format(php_ver)
self.print_log(log_str, "restore")
path_ver = php_ver.replace('.', '')
if os.path.exists("/www/server/php/{}".format(path_ver)):
new_log_str = "php-{}".format(php_ver)
self.replace_log(log_str, new_log_str, "restore")
continue
result = public.ExecShell(
"cd /www/server/panel/install && wget -O php.sh {}/install/{}/php.sh && bash php.sh install {}".format(
OFFICIAL_URL, install_type, php_ver
)
)
if not os.path.exists("/www/server/php/{}".format(path_ver)):
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"php-{} ✗ Installation failed Reason: {} \n Please try to reinstall php in the software store after the restore task ends").format(
php_ver,
err_msg)
php["restore_status"] = 3
php["msg"] = err_msg
self.replace_log(log_str, new_log_str, "restore")
else:
php["restore_status"] = 2
php["msg"] = "success"
new_log_str = "php-{}".format(php_ver)
self.replace_log(log_str, new_log_str, "restore")
self.update_restore_data_list(timestamp, restore_data)
def install_node(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
node_list = restore_data['data_list']['soft']['node_list']
for node_data in node_list:
node_ver = node_data['version']
log_str = public.lang("Start installing node-{}").format(node_ver)
self.print_log(log_str, "restore")
if os.path.exists("/www/server/nodejs/{}".format(node_ver)):
new_log_str = "node-{}".format(node_ver)
self.replace_log(log_str, new_log_str, "restore")
continue
result = public.ExecShell(
"cd /www/server/panel/install && wget -O node_plugin_install.sh {}/install/0/node_plugin_install.sh && bash node_plugin_install.sh {}".format(
OFFICIAL_URL, node_ver
)
)
for mod_list in node_data['mod_list']:
mod_name = mod_list
mod_shell = '''
export PATH
export HOME=/root
export NODE_PATH="/www/server/nodejs/{node_ver}/etc/node_modules"
/www/server/nodejs/{node_ver}//bin/npm config set registry https://registry.npmmirror.com/
/www/server/nodejs/{node_ver}//bin/npm config set prefix /www/server/nodejs/{node_ver}/
/www/server/nodejs/{node_ver}//bin/npm config set cache /www/server/nodejs/{node_ver}//cache
/www/server/nodejs/{node_ver}//bin/npm config set strict-ssl false
/www/server/nodejs/{node_ver}//bin/yarn config set registry https://registry.npmmirror.com/
/www/server/nodejs/{node_ver}/bin/npm install {mod_name} -g &> /www/server/panel/plugin/nodejs/exec.log
'''.format(node_ver=node_ver, mod_name=mod_name)
result = public.ExecShell(mod_shell)
if os.path.exists("/www/server/nodejs/{}".format(node_ver)):
new_log_str = "node-{}".format(node_ver)
self.replace_log(log_str, new_log_str, "restore")
node_data["restore_status"] = 2
node_data["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"node-{} ✗ Installation failed Reason: {} \n Please try to reinstall node in the software store after the restore task ends").format(
node_ver, err_msg)
self.replace_log(log_str, new_log_str, "restore")
node_data["restore_status"] = 3
node_data["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_mysql_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
mysql_server = restore_data['data_list']['soft']['mysql_server']
install_type = self.get_install_type()
if mysql_server['type'] == 'mariadb':
log_str = public.lang("Start installing mariadb-{}").format(mysql_server['version'])
self.print_log(log_str, "restore")
if os.path.exists("/www/server/mysql/bin/mysql"):
new_log_str = "mariadb-{}".format(mysql_server['version'])
self.replace_log(log_str, new_log_str, "restore")
return
result = public.ExecShell(
"cd /www/server/panel/install && wget -O mysql.sh {}/install/{}/mysql.sh && bash mysql.sh install {}".format(
OFFICIAL_URL, install_type, f"mariadb_{mysql_server['version']}"
)
)
if os.path.exists("/www/server/mysql/bin/mysql"):
new_log_str = "mariadb-{}".format(mysql_server['version'])
self.replace_log(log_str, new_log_str, "restore")
mysql_server["restore_status"] = 2
mysql_server["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"mariadb-{} ✗ Installation failed Reason: {} \n Please try to reinstall mariadb in the software store after the restore task ends").format(
mysql_server['version'], err_msg
)
self.replace_log(log_str, new_log_str, "restore")
mysql_server["restore_status"] = 3
mysql_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
return
if mysql_server['type'] == 'mysql':
log_str = public.lang("Start installing mysql-{}").format(mysql_server['version'])
self.print_log(log_str, "restore")
if os.path.exists("/www/server/mysql/bin/mysql"):
new_log_str = "mysql-{}".format(mysql_server['version'])
self.replace_log(log_str, new_log_str, "restore")
return
result = public.ExecShell(
"cd /www/server/panel/install && wget -O mysql.sh {}/install/{}/mysql.sh && bash mysql.sh install {}".format(
OFFICIAL_URL, install_type, mysql_server['version']
)
)
if os.path.exists("/www/server/mysql/bin/mysql"):
new_log_str = "mysql-{}".format(mysql_server['version'])
self.replace_log(log_str, new_log_str, "restore")
mysql_server["restore_status"] = 2
mysql_server["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"mysql-{} ✗ Installation failed Reason: {} \n Please try to reinstall mysql in the software store after the restore task ends").format(
mysql_server['version'], err_msg
)
self.replace_log(log_str, new_log_str, "restore")
mysql_server["restore_status"] = 3
mysql_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_mongodb_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
mongodb_server = restore_data['data_list']['soft']['mongodb_server']
# install_type = self.get_install_type()
log_str = public.lang("Start installing mongodb-{}").format(mongodb_server['version'])
self.print_log(log_str, "restore")
mongo = "/www/server/mongodb/bin/mongo"
mongod = "/www/server/mongodb/bin/mongod"
if (os.path.exists(mongo) or os.path.exists(mongod)) and os.path.exists("/www/server/mongodb/version.pl"):
new_log_str = "mongodb-{}".format(mongodb_server['version'])
self.replace_log(log_str, new_log_str, "restore")
return
result = public.ExecShell(
"cd /www/server/panel/install && wget -O mongodb.sh {}/install/0/mongodb.sh && bash mongodb.sh install {}".format(
OFFICIAL_URL, mongodb_server['version']
)
)
if os.path.exists(mongo) or os.path.exists(mongod):
new_log_str = "mongodb-{}".format(mongodb_server['version'])
self.replace_log(log_str, new_log_str, "restore")
mongodb_server["restore_status"] = 2
mongodb_server["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"mongodb-{} ✗ Installation failed Reason: {} \n Please try to reinstall mongodb in the software store after the restore task ends").format(
mongodb_server['version'], err_msg)
self.replace_log(log_str, new_log_str, "restore")
mongodb_server["restore_status"] = 3
mongodb_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_memcached_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
memcached_server = restore_data['data_list']['soft']['memcached_server']
# install_type = self.get_install_type()
log_str = public.lang("Start installing memcached-{}").format(memcached_server['version'])
self.print_log(log_str, "restore")
if os.path.exists("/usr/local/memcached/bin/memcached"):
new_log_str = "memcached-{}".format(memcached_server['version'])
self.replace_log(log_str, new_log_str, "restore")
return
result = public.ExecShell(
f"cd /www/server/panel/install && wget -O memcached.sh {OFFICIAL_URL}/install/0/memcached.sh && bash memcached.sh install"
)
if os.path.exists("/usr/local/memcached/bin/memcached"):
new_log_str = "memcached-{}".format(memcached_server['version'])
self.replace_log(log_str, new_log_str, "restore")
memcached_server["restore_status"] = 2
memcached_server["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"memcached-{} ✗ Installation failed Reason: {} \n Please try to reinstall memcached in the software store after the restore task ends").format(
memcached_server['version'], err_msg)
self.replace_log(log_str, new_log_str, "restore")
memcached_server["restore_status"] = 3
memcached_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_redis_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
redis_server = restore_data['data_list']['soft']['redis_server']
# install_type = self.get_install_type()
log_str = public.lang("Start installing redis-{}").format(redis_server['version'])
self.print_log(log_str, "restore")
if os.path.exists("/www/server/redis/src/redis-server") and os.path.exists("/www/server/redis/version.pl"):
new_log_str = "redis-{}".format(redis_server['version'])
self.replace_log(log_str, new_log_str, "restore")
return
result = public.ExecShell(
"cd /www/server/panel/install && wget -O redis.sh {}/install/0/redis.sh && bash redis.sh install {}".format(
OFFICIAL_URL, redis_server['version']
)
)
if os.path.exists("/www/server/redis/src/redis-cli"):
new_log_str = "redis-{}".format(redis_server['version'])
self.replace_log(log_str, new_log_str, "restore")
redis_server["restore_status"] = 2
redis_server["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = "redis-{}{}".format(redis_server['version'], err_msg)
self.replace_log(log_str, new_log_str, "restore")
redis_server["restore_status"] = 3
redis_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_pgsql_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
pgsql_server = restore_data['data_list']['soft']['pgsql_server']
log_str = public.lang("Start installing pgsql-{}").format(pgsql_server['version'])
self.print_log(log_str, "restore")
if os.path.exists("/www/server/pgsql/bin/pg_config"):
new_log_str = "pgsql-{}".format(pgsql_server['version'])
self.replace_log(log_str, new_log_str, "restore")
return
self.update_restore_data_list(timestamp, restore_data)
down_file = "postgresql-{pgsql_version}.tar.gz".format(pgsql_version=pgsql_server['version'])
down_url = "{}/src/postgresql-{}.tar.gz".format(
OFFICIAL_URL, pgsql_server['version']
)
result = public.ExecShell(
"cd /www/server/panel/install && wget -O pgsql_install.sh {}/install/0/pgsql_install.sh && bash pgsql_install.sh {} {}".format(
OFFICIAL_URL, down_file, down_url
)
)
if os.path.exists("/www/server/pgsql/bin/psql"):
new_log_str = "pgsql-{}".format(pgsql_server['version'])
self.replace_log(log_str, new_log_str, "restore")
pgsql_server["restore_status"] = 2
pgsql_server["msg"] = None
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = "pgsql-{}{}".format(pgsql_server['version'], err_msg)
self.replace_log(log_str, new_log_str, "restore")
pgsql_server["restore_status"] = 3
pgsql_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_phpmyadmin(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
phpmyadmin_server = restore_data['data_list']['soft']['phpmyadmin_version']
log_str = public.lang("Start installing phpmyadmin-{}").format(phpmyadmin_server['version'])
self.print_log(log_str, "restore")
result = public.ExecShell(
"cd /www/server/panel/install && wget -O phpmyadmin.sh {}/install/0/phpmyadmin.sh && bash phpmyadmin.sh install {}".format(
OFFICIAL_URL, phpmyadmin_server['version']
)
)
if os.path.exists("/www/server/phpmyadmin/version.pl"):
phpmyadmin_server["restore_status"] = 2
phpmyadmin_server["msg"] = None
new_log_str = "phpmyadmin-{}".format(phpmyadmin_server['version'])
self.replace_log(log_str, new_log_str, "restore")
else:
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"phpmyadmin-{} ✗ Installation failed Reason: {} \n Please try to reinstall phpmyadmin in the software store after the restore task ends").format(
phpmyadmin_server['version'], err_msg
)
self.replace_log(log_str, new_log_str, "restore")
phpmyadmin_server["restore_status"] = 3
phpmyadmin_server["msg"] = err_msg
self.update_restore_data_list(timestamp, restore_data)
def install_ftp_server(self, timestamp):
restore_data = self.get_restore_data_list(timestamp)
ftp_server = restore_data['data_list']['soft']['ftp_server']
log_str = public.lang("Start installing ftp-{}").format(ftp_server['version'])
self.print_log(log_str, "restore")
if os.path.exists("/www/server/pure-ftpd/bin/pure-pw"):
new_log_str = "ftp-{}".format(ftp_server['version'])
self.replace_log(log_str, new_log_str, "restore")
ftp_server["restore_status"] = 2
ftp_server["msg"] = None
self.update_restore_data_list(timestamp, restore_data)
return
result = public.ExecShell(
f"cd /www/server/panel/install && wget -O pureftpd.sh {OFFICIAL_URL}/install/0/pureftpd.sh && bash pureftpd.sh install"
)
public.ExecShell("rm -f /www/server/pure-ftpd/etc/pureftpd.passwd")
public.ExecShell("rm -f /www/server/pure-ftpd/etc/pureftpd.pdb")
if not os.path.exists("/www/server/pure-ftpd/bin/pure-pw"):
combined_output = (result[0] + result[1]).splitlines()
err_msg = '\n'.join(combined_output[-10:])
new_log_str = public.lang(
"ftp-{} ✗ Installation failed Reason: {} \nPlease try to reinstall ftp in the software store after the restore task ends").format(
ftp_server['version'], err_msg)
self.replace_log(log_str, new_log_str, "restore")
ftp_server["restore_status"] = 3
ftp_server["msg"] = err_msg
else:
new_log_str = "ftp-{}".format(ftp_server['version'])
self.replace_log(log_str, new_log_str, "restore")
ftp_server["restore_status"] = 2
ftp_server["msg"] = None
self.update_restore_data_list(timestamp, restore_data)
import ftp
if ftp_server['port'] != 21:
with app.app_context():
args = public.dict_obj()
args.port = str(ftp_server['port'])
ftp.ftp().setPort(args)
def restore_env(self, timestamp):
self.print_log("==================================", "restore")
self.print_log(public.lang("Start restoring panel running environment"), "restore")
self.print_log(public.lang("Will skip installation if the same environment exists"), "restore")
restore_data = self.get_restore_data_list(timestamp)
self.update_restore_data_list(timestamp, restore_data)
soft_json_data = restore_data['data_list']['soft']
try:
if soft_json_data['web_server']:
self.install_web_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing web server: {}".format(str(e)))
pass
try:
if soft_json_data['php_server']:
self.install_php_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing PHP server: {}".format(str(e)))
pass
try:
if soft_json_data['mysql_server']:
self.install_mysql_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing MySQL server: {}".format(str(e)))
pass
try:
if soft_json_data['ftp_server']:
self.install_ftp_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing FTP server: {}".format(str(e)))
pass
# try:
# if soft_json_data['node_list']:
# self.install_node(timestamp)
# except Exception as e:
# import traceback
# public.print_log(traceback.format_exc())
# public.print_log("Error installing Node.js: {}".format(str(e)))
# pass
try:
if soft_json_data['redis_server']:
self.install_redis_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing Redis server: {}".format(str(e)))
pass
try:
if soft_json_data['memcached_server']:
self.install_memcached_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing Memcached server: {}".format(str(e)))
pass
try:
if soft_json_data['mongodb_server']:
self.install_mongodb_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing MongoDB server: {}".format(str(e)))
pass
try:
if soft_json_data['pgsql_server']:
self.install_pgsql_server(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing PostgreSQL server: {}".format(str(e)))
pass
try:
if soft_json_data['phpmyadmin_version']:
self.install_phpmyadmin(timestamp)
except Exception as e:
import traceback
public.print_log(traceback.format_exc())
public.print_log("Error installing phpMyAdmin: {}".format(str(e)))
pass
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 2:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名 p
timestamp = sys.argv[2]
soft_manager = SoftModule() # 实例化对象
if hasattr(soft_manager, method_name): # 检查方法是否存在
method = getattr(soft_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: {public.lang('Method')} '{method_name}' {public.lang('does not exist')}")

View File

@@ -0,0 +1,92 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <miku@yakpanel.com>
# -------------------------------------------------------------------
import os
import sys
import warnings
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 mod.project.backup_restore.base_util import BaseUtil
from mod.project.backup_restore.config_manager import ConfigManager
warnings.filterwarnings("ignore", category=SyntaxWarning)
class SshModule(BaseUtil, ConfigManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.ssh_path = "/www/server/panel/config/ssh_info"
def backup_ssh_data(self, timestamp):
self.print_log("====================================================", "backup")
self.print_log(public.lang("Start backing up terminal data"), "backup")
ssh_backup_path = self.base_path + "/{timestamp}_backup/ssh".format(timestamp=timestamp)
if not os.path.exists(ssh_backup_path):
public.ExecShell('mkdir -p {}'.format(ssh_backup_path))
print(self.ssh_path)
public.ExecShell("\cp -rpa {} {}".format(self.ssh_path, ssh_backup_path))
ssh_info = {
'status': 2,
'msg': None,
'ssh_info_path': ssh_backup_path,
}
backup_size = self.format_size(self.get_file_size(ssh_backup_path))
self.print_log(public.lang("Terminal data backup completed. Data size: {}").format(backup_size), 'backup')
data_list = self.get_backup_data_list(timestamp)
data_list['data_list']['ssh'] = ssh_info
self.update_backup_data_list(timestamp, data_list)
def restore_ssh_data(self, timestamp):
self.print_log("==================================", "restore")
self.print_log(public.lang("Start restoring terminal data"), "restore")
restore_data = self.get_restore_data_list(timestamp)
ssh_data = restore_data['data_list']['ssh']
ssh_info_path = ssh_data['ssh_info_path'] + "/ssh_info"
restore_data['data_list']['ssh']['restore_status'] = 1
self.update_restore_data_list(timestamp, restore_data)
if not os.path.exists(ssh_info_path):
self.print_log(public.lang("Restore failed, file does not exist"), "restore")
return
if not os.path.exists(self.ssh_path):
public.ExecShell("mkdir -p {}".format(self.ssh_path))
public.ExecShell("\cp -rpa {}/* {}".format(ssh_info_path, self.ssh_path))
self.print_log(public.lang("Terminal data restoration completed"), "restore")
restore_data['data_list']['ssh']['restore_status'] = 2
self.update_restore_data_list(timestamp, restore_data)
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 2:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2]
ssh_manager = SshModule() # 实例化对象
if hasattr(ssh_manager, method_name): # 检查方法是否存在
method = getattr(ssh_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: Method '{method_name}' does not exist")

View File

@@ -0,0 +1,343 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
import os
import sys
import time
from pathlib import Path
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.hook_import import hook_import
hook_import()
from YakPanel import app
from ssl_domainModelV2.api import DomainObject
from ssl_domainModelV2.service import CertHandler
from ssl_domainModelV2.config import UserFor
from ssl_domainModelV2.model import DnsDomainSSL, DnsDomainProvider
from mod.project.backup_restore.data_manager import DataManager
class SSLModel(DataManager):
def __init__(self):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
def get_ssl_backup_conf(self, timestamp: int = None) -> dict:
"""
Get SSL certificate and DNS API provider backup configuration
"""
ssl_list = [
{
**ssl.as_dict(),
"data_type": "backup",
"status": 0,
"msg": None,
} for ssl in DnsDomainSSL.objects.filter(is_order=0) # 过滤商业证书
]
provider_list = [
{
**x.as_dict(),
# 与备份的 status 字段冲突, 使用 account status
"account_status": x.status,
"data_type": "backup",
"status": 0,
"msg": None,
} for x in DnsDomainProvider.objects.all()
]
res = {
"ssl_list": ssl_list,
"provider_list": provider_list,
}
return res
def backup_ssl_data(self, timestamp) -> None:
"""
Backup domain management center
"""
# 总配置
data_list = self.get_backup_data_list(timestamp)
if not data_list:
return None
data_backup_path = data_list.get("backup_path")
ssl_backup_path = Path(data_backup_path) / "ssl"
ssl_backup_path.mkdir(parents=True, exist_ok=True)
self.print_log("==================================", "backup")
self.print_log(public.lang("Start backing up SSL certificate information"), "backup")
for ssl in data_list['data_list']['ssl'].get("ssl_list", []): # SSL in the general configuration
try:
if not ssl.get("path") or not os.path.exists(ssl.get("path")):
err = public.lang("{} {} Certificate file does not exist ✗").format(
ssl['info'].get("issuer_O", ""), ssl['dns']
)
self.print_log(err, "backup")
ssl["status"] = 3
ssl["msg"] = err
continue
if ssl.get("not_after_ts") < time.time() * 1000:
err = public.lang("{} [{}] Certificate has expired ✗").format(
ssl['info'].get("issuer_O", ""), str(ssl['dns'])
)
self.print_log(err, "backup")
ssl["status"] = 3
ssl["msg"] = err
continue
domian_path = ssl_backup_path / ssl.get("hash")
CertHandler.make_last_info(domian_path, force=True)
public.ExecShell(f"\cp -rpa {ssl['path']} {domian_path}")
ssl["status"] = 2
self.print_log(public.lang("{} {}").format(
ssl['info'].get("issuer_O", ""), ssl['dns']
), "backup")
except Exception as e:
err = public.lang("{} {} Backup failed: {}").format(
ssl['info'].get('issuer_O', ''), ssl['dns'], str(e)
)
ssl["status"] = 3
ssl["msg"] = err
self.print_log(err, "backup")
continue
new_provider_info = [
{**x, "status": 2} for x in data_list['data_list']['ssl'].get("provider_list", [])
]
data_list['data_list']['ssl']['provider_list'] = new_provider_info
self.print_log(public.lang("DNS API provider information backup completed"), "backup")
self.update_backup_data_list(timestamp, data_list)
self.print_log(public.lang("SSL certificate information backup completed"), "backup")
def _rebuild_deploy(self, ssl_obj: DnsDomainSSL, backup_ssl: dict) -> None:
try:
def r_log(log_str: str, new_log: str):
self.replace_log(log_str, new_log, "restore")
used = backup_ssl.get("user_for", {})
if not ssl_obj or not used:
return
# pre clear
for other_ssl in DnsDomainSSL.objects.filter(hash__ne=ssl_obj.hash):
is_change = False
for site_name in used.get(UserFor.sites, []):
if site_name in other_ssl.sites_uf:
other_ssl.sites_uf.remove(site_name)
is_change = True
for mail_name in used.get(UserFor.mails, []):
if mail_name in other_ssl.mails_uf:
other_ssl.mails_uf.remove(mail_name)
is_change = True
for panel_name in used.get(UserFor.panel, []):
if panel_name in other_ssl.panel_uf:
other_ssl.panel_uf = []
is_change = True
if is_change:
other_ssl.save()
if used.get(UserFor.sites):
log_str = public.lang("Restoring deployment sites for certificate {}...").format(backup_ssl['subject'])
self.print_log(log_str, "restore")
build_sites = ssl_obj.deploy_sites(
site_names=used[UserFor.sites], replace=True
)
r_log(log_str, public.lang("Restored deployment sites for certificate {}: {}").format(
backup_ssl['subject'], build_sites.get('msg')
))
if used.get(UserFor.mails):
log_str = public.lang("Restoring deployment mailboxes for certificate {}...").format(
backup_ssl['subject'])
self.print_log(log_str, "restore")
build_mails = ssl_obj.deploy_mails(
mail_names=used[UserFor.mails]
)
r_log(log_str, public.lang("Restored deployment mailboxes for certificate {}: {}").format(
backup_ssl['subject'], build_mails.get('msg')
))
if used.get(UserFor.panel):
log_str = public.lang("Restoring deployment panel for certificate {}...").format(backup_ssl['subject'])
self.print_log(log_str, "restore")
build_panel = ssl_obj.deploy_panel(
recover=0
)
r_log(log_str, public.lang("Restored deployment panel for certificate {}: {}").format(
backup_ssl['subject'], build_panel.get('msg')
))
except Exception as e:
public.print_log("rebuild deploy error: {}".format(str(e)))
def _restore_ssl(self, backup_ssl: dict, pem: str, key: str) -> None:
exist_obj = DnsDomainSSL.objects.filter(
hash=CertHandler.get_hash(cert_pem=pem)
).first()
if exist_obj:
if not self.overwrite:
return
# overwrite
# exist_obj.provider_id = backup_ssl["provider_id"]
# exist_obj.not_after = backup_ssl["not_after"]
# exist_obj.not_after_ts = backup_ssl["not_after_ts"]
exist_obj.user_for = backup_ssl["user_for"]
exist_obj.info = backup_ssl["info"]
exist_obj.alarm = backup_ssl["alarm"]
exist_obj.auto_renew = backup_ssl["auto_renew"]
exist_obj.auth_info = backup_ssl["auth_info"]
exist_obj.log = backup_ssl["log"]
exist_obj.save()
# ssl_obj = exist_obj
else:
try:
insert = CertHandler().save_by_data(
cert_pem=pem,
private_key=key,
new_auth_info=backup_ssl["auth_info"],
)
if not insert:
raise Exception(public.lang("Certificate insertion failed, please check the log"))
except Exception as e:
raise Exception(public.lang(f"Certificate Restore Failed: {str(e)}"))
# ssl_obj = DnsDomainSSL.objects.filter(hash=insert["hash"]).first()
# if ssl_obj:
# # it will update the ssl field 'user_for'
# self._rebuild_deploy(
# ssl_obj=ssl_obj,
# backup_ssl=backup_ssl,
# )
def _restore_provider(self, provider: dict):
if_exist = DnsDomainProvider.objects.filter(
name=provider["name"],
api_user=provider.get("api_user", ""),
api_key=provider["api_key"],
).first()
if if_exist:
if self.overwrite:
return
return
res = DomainObject().create_dns_api(
public.to_dict_obj({
"name": provider["name"],
"api_user": provider.get("api_user", ""),
"api_key": provider["api_key"],
"status": provider.get("account_status", 1),
"permission": provider.get("permission", "-"),
"alias": provider["alias"],
"ps": provider["ps"],
})
)
if res.get("status", 0) != 0:
raise Exception(public.lang(
f"Restore DNS API provider failed: {res.get('message', 'create dns api error')}"
))
def restore_ssl_data(self, timestamp: int) -> None:
""" Restore domain management center """
self.print_log("====================================================", "restore")
self.print_log(public.lang("Start restoring domain SSL certificate configuration"), "restore")
restore_data = self.get_restore_data_list(timestamp)
if not restore_data:
self.print_log(public.lang("No restore data found"), "restore")
return
ssl_cert_path = Path(restore_data.get("backup_path")) / "ssl"
if not ssl_cert_path.exists():
self.print_log(
public.lang("Backup directory {} does not exist, unable to restore SSL certificate information").format(
ssl_cert_path
), "restore")
return
ssl_info = restore_data["data_list"].get("ssl", {})
with app.app_context():
# ======================= ssl =============================
for ssl in ssl_info.get("ssl_list", []):
log_str = public.lang("Restoring {} Subject: {}").format(
ssl['info'].get('issuer_O'), ssl['subject']
)
try:
self.print_log(log_str, "restore")
ssl["restore_status"] = 1
ssl_path = ssl_cert_path / ssl["hash"]
if not ssl_path.exists():
raise Exception(public.lang("Certificate file does not exist"))
pem = ssl_path / "fullchain.pem"
key = ssl_path / "privkey.pem"
if not pem.exists() or not key.exists():
raise Exception(public.lang("Missing certificate or private key file"))
self._restore_ssl(
backup_ssl=ssl,
pem=public.readFile(str(pem)),
key=public.readFile(str(key)),
)
ssl["restore_status"] = 2
self.replace_log(
log_str,
public.lang(f"Restored {ssl['info'].get('issuer_O')} Subject: {ssl['subject']}"),
"restore"
)
except Exception as e:
err_msg = public.lang(
f"Restoring {ssl['info'].get('issuer_O', '')} Subject: {ssl['subject']} failed: {str(e)}"
)
ssl["restore_status"] = 3
ssl["msg"] = str(e)
self.replace_log(log_str, err_msg, "restore")
self.update_restore_data_list(timestamp, restore_data)
# ======================= dns provider =============================
for provider in ssl_info.get("provider_list", []):
log_str = public.lang(f"Restoring DNS API {provider['name']}: {provider['alias']}...")
try:
self.print_log(log_str, "restore")
self._restore_provider(provider)
time.sleep(1)
provider["restore_status"] = 2
self.replace_log(
log_str,
public.lang(f"Restored DNS API {provider['name']}: {provider['alias']}"),
"restore"
)
except Exception as e:
err_msg = public.lang(f"Restoring DNS API provider: {provider['name']} failed: {str(e)}")
provider["restore_status"] = 3
provider["msg"] = str(e)
self.replace_log(log_str, err_msg, "restore")
self.update_restore_data_list(timestamp, restore_data)
self.update_restore_data_list(timestamp, restore_data)
self.print_log(public.lang("SSL certificate information restoration completed"), "restore")
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Usage: btpython backup_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1]
timestamp = sys.argv[2]
database_module = SSLModel()
if hasattr(database_module, method_name):
method = getattr(database_module, method_name)
method(timestamp)
else:
print(f"Error: method '{method_name}' not found")

View File

@@ -0,0 +1,504 @@
# coding: utf-8
# -------------------------------------------------------------------
# yakpanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: miku <wzz@yakpanel.com>
# -------------------------------------------------------------------
import datetime
import json
import os
import sys
import time
import warnings
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 mod.project.backup_restore.modules.site_module import SiteModule
from mod.project.backup_restore.modules.database_module import DatabaseModule
from mod.project.backup_restore.modules.ftp_module import FtpModule
from mod.project.backup_restore.modules.crontab_module import CrontabModule
from mod.project.backup_restore.modules.ssh_module import SshModule
from mod.project.backup_restore.modules.firewall_module import FirewallModule
from mod.project.backup_restore.modules.mail_module import MailModule
from mod.project.backup_restore.modules.ssl_model import SSLModel
from mod.project.backup_restore.modules.plugin_module import PluginModule
warnings.filterwarnings("ignore", category=SyntaxWarning)
class RestoreManager(SiteModule, DatabaseModule, FtpModule, SSLModel, CrontabModule, SshModule,
FirewallModule, MailModule, PluginModule):
def __init__(self, overwrite: int = 0):
super().__init__()
self.base_path = '/www/backup/backup_restore'
self.bakcup_task_json = self.base_path + '/backup_task.json'
self.backup_log_file = self.base_path + '/backup.log'
self.backup_pl_file = self.base_path + '/backup.pl'
self.backup_success_file = self.base_path + '/success.pl'
self.backup_save_config = self.base_path + '/backup_save_config.json'
self.history_log_path = '/www/backup/backup_restore/history/log'
self.history_info_path = '/www/backup/backup_restore/history/info'
self.restore_log_file = self.base_path + '/restore.log'
self.restore_pl_file = self.base_path + '/restore.pl'
self.restore_success_file = self.base_path + '/restore_success.pl'
self.migrate_backup_info_path = '/www/backup/backup_restore/migrate_backup_info.json'
self.overwrite = overwrite # 强制还原标志
def restore_data(self, timestamp):
"""
还原数据
"""
if os.path.exists(self.restore_log_file):
public.ExecShell("rm -f {}".format(self.restore_log_file))
if os.path.exists(self.restore_pl_file):
return public.returnMsg(False, public.lang("A restore process is already running!"))
try:
public.WriteFile(self.restore_pl_file, timestamp)
backup_file = str(timestamp) + "_backup.tar.gz"
file_names = os.listdir(self.base_path)
for file in file_names:
if backup_file in file:
backup_file = file
if os.path.exists(self.migrate_backup_info_path):
backup_list = []
if os.path.exists(self.bakcup_task_json):
backup_list = json.loads(public.ReadFile(self.bakcup_task_json))
migrate_backup_info = json.loads(public.ReadFile(self.migrate_backup_info_path))
backup_list.append(migrate_backup_info)
public.ExecShell("rm -f {}".format(self.migrate_backup_info_path))
public.WriteFile(self.bakcup_task_json, json.dumps(backup_list))
time.sleep(1)
backup_conf = self.get_backup_conf(timestamp)
backup_conf['restore_status'] = 1
self.save_backup_conf(timestamp, backup_conf)
self.print_log("==================================", "restore")
self.print_log(public.lang("Start decompressing the data package"), "restore")
try:
public.ExecShell("rm -rf {}/{}_backup".format(self.base_path, timestamp))
except:
pass
if not os.path.exists(self.base_path + "/{timestamp}_backup".format(timestamp=timestamp)):
public.ExecShell("cd {}/ && tar -xvf {}".format(self.base_path, backup_file))
restore_data_path = self.base_path + "/{timestamp}_backup".format(timestamp=timestamp)
public.ExecShell("\cp -rpa {}/backup.json {}/restore.json".format(restore_data_path, restore_data_path))
restore_info = self.get_restore_data_list(timestamp)
restore_info["force_restore"] = self.overwrite # 覆盖标志
restore_info['restore_status'] = 1 # 更新状态
self.update_restore_data_list(timestamp, restore_info)
start_time = int(time.time())
# ============================= START ==================================
self.print_log(public.lang("Start restoring data"), "restore")
# ============================= env ====================================
try:
self.restore_env(timestamp)
except Exception as e:
public.print_log(f"restore env error: {str(e)}")
# ============================= site ====================================
try:
self.restore_site_data(timestamp)
except Exception as e:
public.print_log("restore site error: {}".format(str(e)))
finally:
self.chmod_dir_file("/www/wwwroot", dir_mode=0o755, file_mode=0o644)
# ============================= ftp =====================================
try:
self.restore_ftp_data(timestamp)
except Exception as e:
public.print_log("restore ftp error: {}".format(str(e)))
# ============================= database =================================
try:
self.restore_database_data(timestamp)
except Exception as e:
public.print_log("restore database error: {}".format(str(e)))
finally:
if not self.overwrite:
try: # 补全关系
self.fix_wp_onekey(timestamp)
except Exception as e:
public.print_log("fix forign key error: {}".format(str(e)))
# ============================= ssl ======================================
try:
self.restore_ssl_data(timestamp)
except Exception as e:
public.print_log("restore ssl error: {}".format(str(e)))
# ============================= cron task ================================
try:
self.restore_crontab_data(timestamp)
except Exception as e:
public.print_log("restore cron task error: {}".format(str(e)))
finally:
self.reload_crontab()
# ============================== ssh ======================================
# TDDO: 存在问题,下个版本修复
# try:
# SshModule().restore_ssh_data(timestamp)
# except Exception as e:
# public.print_log("restore ssh error: {}".format(str(e)))
# ============================= firewall ==================================
try:
self.restore_firewall_data(timestamp)
except Exception as e:
public.print_log("restore firewall error: {}".format(str(e)))
# ============================= mail ======================================
try:
self.restore_vmail_data(timestamp)
except Exception as e:
public.print_log("restore mail error: {}".format(str(e)))
# ============================= plugin ====================================
try:
self.restore_plugin_data(timestamp)
except Exception as e:
public.print_log("restore plugin error: {}".format(str(e)))
# ============================= END =======================================
end_time = int(time.time())
done_time = datetime.datetime.fromtimestamp(int(end_time)).strftime('%Y-%m-%d %H:%M:%S')
total_time = end_time - start_time
backup_conf = self.get_backup_conf(timestamp)
backup_conf['restore_status'] = 2
backup_conf['restore_done_time'] = done_time
backup_conf['restore_total_time'] = total_time
self.save_backup_conf(timestamp, backup_conf)
restore_info['restore_status'] = 2
restore_info['restore_done_time'] = done_time
restore_info['restore_total_time'] = total_time
self.update_restore_data_list(timestamp, restore_info)
self.print_log("==================================", "restore")
self.print_log(public.lang("Data restoration completed"), "restore")
public.WriteFile(self.restore_success_file, timestamp)
public.ExecShell("rm -f {}".format(self.restore_pl_file))
if not os.path.exists(self.history_log_path):
public.ExecShell("mkdir -p {}".format(self.history_log_path))
if not os.path.exists(self.history_info_path):
public.ExecShell("mkdir -p {}".format(self.history_info_path))
hitory_log_file = self.history_log_path + '/' + str(timestamp) + '_restore.log'
history_info_file = self.history_info_path + '/' + str(timestamp) + '_restore.info'
public.WriteFile(
hitory_log_file,
public.ReadFile("/www/backup/backup_restore/restore.log".format(timestamp))
)
public.WriteFile(
history_info_file,
public.ReadFile("/www/backup/backup_restore/{}_backup/restore.json".format(timestamp))
)
if os.path.exists("/www/server/panel/data/migration.pl"):
public.ExecShell("rm -f /www/server/panel/data/migration.pl")
# 重启服务
public.ServiceReload()
time.sleep(2)
public.ExecShell("/etc/init.d/bt restart")
except Exception as e:
return public.returnMsg(False, public.lang("Data restoration failed: {}").format(str(e)))
finally:
if os.path.exists(self.restore_pl_file):
public.ExecShell("rm -f {}".format(self.restore_pl_file))
def get_restore_log(self, timestamp):
restore_log_file = self.base_path + '/restore.log'
history_log_file = self.history_log_path + '/' + str(timestamp) + '_restore.log'
if os.path.exists(self.restore_pl_file):
restore_timestamp = int(public.ReadFile(self.restore_pl_file))
if int(restore_timestamp) == int(timestamp):
return public.ReadFile(restore_log_file)
if os.path.exists(history_log_file):
return public.ReadFile(history_log_file)
else:
return ""
def get_restore_details(self, timestamp):
history_info_file = self.history_info_path + '/' + str(timestamp) + '_restore.info'
if not os.path.exists(history_info_file):
return public.fail_v2(public.lang("File does not exist"))
restore_info = json.loads(public.ReadFile(history_info_file))
restore_info = self.process_detail(restore_info)
restore_task_info = self.get_backup_conf(timestamp)
restore_info['backup_file_size'] = restore_task_info['backup_file_size']
restore_info['backup_file_sha256'] = restore_task_info['backup_file_sha256']
restore_info['create_time'] = restore_task_info['create_time']
restore_info['backup_time'] = restore_task_info['backup_time']
restore_info['backup_file'] = restore_task_info['backup_file']
restore_info['backup_path'] = restore_task_info['backup_path']
restore_info['restore_done_time'] = restore_task_info['restore_done_time']
restore_info['restore_total_time'] = restore_task_info['restore_total_time']
restore_info['type'] = "restore"
return public.success_v2(restore_info)
# todo 弃用
def get_restore_progress(self, get=None):
"""
获取还原进度信息
@param get: object 包含请求参数
@return: dict 还原进度信息
"""
# 设置相关文件路径
restore_pl_file = self.base_path + '/restore.pl'
restore_log_file = self.base_path + '/restore.log'
restore_success_file = self.base_path + '/restore_success.pl'
# 创建处理已完成备份的函数,减少代码重复
def create_completed_result(restore_timestamp):
if not restore_timestamp:
return public.ReturnMsg(False, public.lang("Restore completed but unable to get restore timestamp"))
if not os.path.exists(self.bakcup_task_json):
return public.ReturnMsg(False, public.lang("Restore configuration file does not exist"))
restore_configs = json.loads(public.ReadFile(self.bakcup_task_json))
success_data = next(
(item for item in restore_configs if str(item.get('timestamp')) == str(restore_timestamp)), {})
return {
"task_type": "restore",
"task_status": 2,
"backup_data": None,
"backup_name": None,
"data_backup_status": 2,
"progress": 100,
"msg": None,
'exec_log': public.ReadFile(restore_log_file) if os.path.exists(restore_log_file) else "",
'timestamp': restore_timestamp,
'backup_file_info': success_data
}
# 检查备份是否已完成
if os.path.exists(restore_success_file):
success_time = int(os.path.getctime(restore_success_file))
local_time = int(time.time())
# 如果success文件创建时间在10秒内说明备份刚刚完成
if success_time + 10 > local_time:
try:
restore_timestamp = public.ReadFile(restore_success_file).strip()
return public.ReturnMsg(True, create_completed_result(restore_timestamp))
except Exception as e:
public.ExecShell("rm -f {}".format(restore_success_file))
return public.ReturnMsg(False,
public.lang("Error getting restore completion information: {}").format(
str(e)))
else:
# 超过10秒删除success文件
public.ExecShell("rm -f {}".format(restore_success_file))
# 检查是否有备份进程运行
try:
# 检查备份进程锁文件
if os.path.exists(restore_pl_file):
timestamp = public.ReadFile(restore_pl_file).strip()
if not timestamp:
return public.ReturnMsg(False, public.lang(
"Restore process is running, but unable to get restore timestamp"))
else:
# 等待2秒可能是还原刚刚完成
time.sleep(2)
if os.path.exists(restore_success_file):
success_time = int(os.path.getctime(restore_success_file))
local_time = int(time.time())
if success_time + 10 > local_time:
restore_timestamp = public.ReadFile(restore_success_file).strip()
return public.ReturnMsg(True, create_completed_result(restore_timestamp))
# 再次检查是否有备份进程
if os.path.exists(restore_success_file):
timestamp = public.ReadFile(restore_success_file).strip()
if not timestamp:
return public.ReturnMsg(False, public.lang(
"Restore process is running, but unable to get restore timestamp"))
else:
return public.ReturnMsg(False, public.lang(
"No restore task found, please check the restore list to see if the restore is complete"))
# 读取备份配置文件
restore_json_path = f"{self.base_path}/{timestamp}_backup/restore.json"
count = 0
while 1:
if count >= 3:
return public.ReturnMsg(False, public.lang("Restore configuration file does not exist: {}").format(
restore_json_path))
count += 1
if not os.path.exists(restore_json_path):
time.sleep(1)
else:
break
conf_data = json.loads(public.ReadFile(restore_json_path))
except Exception as e:
return public.ReturnMsg(False, public.lang("Error getting restore progress information: {}").format(str(e)))
# 读取备份日志
restore_log_data = public.ReadFile(restore_log_file) if os.path.exists(restore_log_file) else ""
# 定义备份类型及其处理逻辑
restore_types = [
{
'type': 'site',
'data_key': 'site',
'display_name': 'site',
'progress': 30
},
{
'type': 'database',
'data_key': 'database',
'display_name': 'database',
'progress': 60
},
{
'type': 'ftp',
'data_key': 'ftp',
'display_name': 'ftp',
'progress': 70
},
{
'type': 'ssh',
'data_key': 'ssh',
'display_name': 'ssh',
'progress': 75
},
{
'type': 'firewall',
'data_key': 'firewall',
'display_name': 'firewall',
'progress': 90
}
]
# 检查软件环境状态
if "data_list" in conf_data and "soft" in conf_data["data_list"]:
soft_data = conf_data["data_list"]["soft"]
if "status" in soft_data and soft_data["status"].get("status") == 1:
name = soft_data["status"].get("name", "unknown")
# version = soft_data["status"].get("version", "unknown")
return public.ReturnMsg(True, {
"task_type": "restore",
"task_status": 1,
"data_type": "soft",
"name": name,
"data_backup_status": 1,
"progress": 20,
"msg": soft_data["status"].get("err_msg"),
'exec_log': restore_log_data,
'timestamp': timestamp
})
# 检查各类型备份进度
for restore_type in restore_types:
items = conf_data.get("data_list", {}).get(restore_type['data_key'], [])
for item in items:
try:
if item.get("restore_status") == 2:
continue
return public.ReturnMsg(True, {
"task_type": "restore",
"task_status": 1,
"data_type": restore_type['type'],
"name": item.get("name", public.lang("Unknown {}").format(restore_type['display_name'])),
"data_backup_status": item.get("status", 0),
"progress": restore_type['progress'],
"msg": item.get("msg"),
'exec_log': restore_log_data,
'timestamp': timestamp
})
except:
return public.ReturnMsg(True, {
"task_type": "restore",
"task_status": 1,
"data_type": public.lang("Server Configuration"),
"name": public.lang("Server Configuration"),
"data_backup_status": 2,
"progress": 90,
"msg": None,
'exec_log': restore_log_data,
'timestamp': timestamp
})
# 检查数据打包进度
try:
restore_status = conf_data.get('restore_status')
if restore_status == 1:
return public.ReturnMsg(True, {
"task_type": "restore",
"task_status": 1,
"data_type": "tar",
"name": public.lang("Data Decompression"),
"data_backup_status": 1,
"progress": 10,
'exec_log': restore_log_data,
'timestamp': timestamp
})
except Exception:
# 可能没有backup_status字段继续处理
pass
# 如果没有发现进行中的任务,但有备份进程
if timestamp:
return {
"backup_data": "unknown",
"backup_name": public.lang("Unknown Task"),
"data_backup_status": 1,
"progress": 10,
'backup_msg': public.lang("Preparing to restore data"),
'backup_log': restore_log_data,
'timestamp': timestamp
}
return public.ReturnMsg(
False,
public.lang(
"No ongoing restore task found, please check the restore list to see if the restore is complete"
)
)
if __name__ == '__main__':
# 获取命令行参数
if len(sys.argv) < 3:
print("Usage: btpython restore_manager.py <method> <timestamp>")
sys.exit(1)
method_name = sys.argv[1] # 方法名
timestamp = sys.argv[2] # 时间戳
overwrite = sys.argv[3] if len(sys.argv) >= 3 else 0
try:
overwrite = int(overwrite)
except ValueError:
print(public.lang("Error: force parameter must be 0 or 1"))
sys.exit(1)
restore_manager = RestoreManager(overwrite) # 实例化对象
if hasattr(restore_manager, method_name): # 检查方法是否存在
method = getattr(restore_manager, method_name) # 获取方法
method(timestamp) # 调用方法
else:
print(f"Error: {public.lang('Method')} '{method_name}' {public.lang('does not exist')}")

File diff suppressed because it is too large Load Diff