Files
Jack a24f2e23c6 Update to 7.25.0
1. Added Mail Marketing Automation trigger tasks
2. Added Mail Marketing Groups Import, Export, Merge
3. Added Mail Marketing Subscribers to support paste import
4. Added Mail Marketing Template Import Export Duplicate
5. Added Mail server add timed automatic reply
6. Added HTTPS Protection function for Website (disables automatic HTTP to HTTPS redirection when enabled)
7. Optimize Website Interface Button Integration
8. Optimize the response speed of WP Toolkit interface
2025-03-13 17:52:26 +08:00

136 lines
5.5 KiB
Python

import os
import time
from hashlib import md5
from typing import Optional, List, Union, Dict, Any
from .util import DB, ExecShell, write_file, write_log
from .versions_tool import VersionTool
class BackupTool:
def __init__(self):
self._backup_path: Optional[str] = None
self._sub_dir_name: str = ""
self.exec_log_file = "/tmp/mod_backup_exec.log"
@staticmethod
def _hash_src_name(name: Union[str, bytes]) -> str:
if isinstance(name, str):
name = name.encode('utf-8')
md5_obj = md5()
md5_obj.update(name)
return md5_obj.hexdigest()
@property
def backup_path(self) -> str:
if self._backup_path is None:
config_data = DB("config").where("id=?", (1,)).select()
if isinstance(config_data, dict):
path = config_data["backup_path"]
else: # 查询出错
path = "/www/backup"
self._backup_path = path
return self._backup_path
# sub_dir 可以设置为多级子目录, 如 "site/aaa", 或使用列表传递如:["site", "aaa"]
def set_sub_dir(self, sub_dir: Union[str, List[str]]) -> Optional[str]:
if isinstance(sub_dir, str):
self._sub_dir_name = sub_dir.strip("./")
elif isinstance(sub_dir, list):
self._sub_dir_name = "/".join(filter(None, [i.strip("./") for i in sub_dir]))
else:
return "Unsupported type settings"
def backup(self,
src: str, # 源文件位置
backup_path: Optional[str] = None, # 备份位置
sub_dir: Union[str, List[str]] = None, # 备份目录的子目录
site_info: Dict[str, Any] = None, # 关联的站点信息, 必须包含 id 和 name
sync=False # 是否同步执行, 默认异步由单独的线程放入后台执行
) -> Optional[str]: # 返回执行错误的信息
if not os.path.exists(src):
return "The source path does not exist"
if backup_path is None:
backup_path = self.backup_path
if not os.path.exists(backup_path):
return "The backup directory does not exist"
if sub_dir is not None:
set_res = self.set_sub_dir(sub_dir)
if set_res is not None:
return set_res
target_path = os.path.join(backup_path, self._sub_dir_name)
if not os.path.isdir(target_path):
os.makedirs(target_path)
zip_name = "{}_{}.tar.gz".format(os.path.basename(src), time.strftime('%Y%m%d_%H%M%S', time.localtime()))
if sync:
return self._sync_backup(src, target_path, zip_name, site_info)
else:
return self._async_backup(src, target_path, zip_name, site_info)
def _sync_backup(self, src: str, target_path: str, zip_name: str, site_info: dict):
try:
write_file(self.exec_log_file, "")
execStr = ("cd {} && "
"tar -zcvf '{}' --exclude=.user.ini ./ 2>&1 > {} \n"
"echo '---Backup execution completed---' >> {}"
).format(src, os.path.join(target_path, zip_name), self.exec_log_file, self.exec_log_file)
ExecShell(execStr)
if site_info is not None and "id" in site_info and "name" in site_info:
DB('backup').add(
'type,name,pid,filename,size,addtime',
(0, zip_name, site_info["id"], os.path.join(target_path, zip_name), 0, self.get_date())
)
write_log('TYPE_SITE', 'SITE_BACKUP_SUCCESS', (site_info["name"],))
except:
return "The backup execution failed"
def _async_backup(self, src: str, target_path: str, zip_name: str, site_info: dict):
import threading
hash_name = self._hash_src_name(src)
backup_tip_path = "/tmp/mod_backup_tip"
if os.path.exists(backup_tip_path):
os.makedirs(backup_tip_path)
tip_file = os.path.join(backup_tip_path, hash_name)
if os.path.isfile(tip_file):
mtime = os.stat(tip_file).st_mtime
if time.time() - mtime > 60 * 20: # 20 分钟未执行,认为出现在不可抗力,导致备份失败,允许再次备份
os.remove(tip_file)
else:
return "The backup is in progress, do not proceed"
write_file(tip_file, "")
def _back_p():
try:
write_file(self.exec_log_file, "")
execStr = ("cd {} && "
"tar -zcvf '{}' --exclude=.user.ini ./ 2>&1 > {} \n"
"echo '---Backup execution completed---' >> {}"
).format(src, os.path.join(target_path, zip_name), self.exec_log_file, self.exec_log_file)
ExecShell(execStr)
if site_info is not None and "id" in site_info and "name" in site_info:
DB('backup').add(
'type,name,pid,filename,size,addtime',
(0, zip_name, site_info["id"], os.path.join(target_path, zip_name), 0, self.get_date())
)
write_log('TYPE_SITE', 'SITE_BACKUP_SUCCESS', (site_info["name"],))
except:
pass
finally:
if os.path.exists(tip_file):
os.remove(tip_file)
t = threading.Thread(target=_back_p)
t.start()
@staticmethod
def get_date():
# 取格式时间
return time.strftime('%Y-%m-%d %X', time.localtime())