备份时写入当前中控登录用户名和密码,恢复后可直接使用。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+67
-5
@@ -101,20 +101,35 @@ def prune_backups(*, keep_days: int | None = None) -> int:
|
||||
return removed
|
||||
|
||||
|
||||
def create_backup(*, root_dir: str, env_path: str, db_path: str, label: str = "manual") -> dict[str, Any]:
|
||||
def create_backup(
|
||||
*,
|
||||
root_dir: str,
|
||||
env_path: str,
|
||||
db_path: str,
|
||||
label: str = "manual",
|
||||
username: str | None = None,
|
||||
password: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
with _LOCK:
|
||||
now = beijing_now()
|
||||
name = f"crypto_okx_hub_backup_{now.strftime('%Y%m%d_%H%M%S')}.tar.gz"
|
||||
out_path = os.path.join(backup_dir(), name)
|
||||
u = (username if username is not None else os.getenv("HUB_USERNAME") or "admin").strip() or "admin"
|
||||
p = password if password is not None else (os.getenv("HUB_PASSWORD") or "admin123")
|
||||
if p is None:
|
||||
p = "admin123"
|
||||
manifest = {
|
||||
"kind": "crypto_okx_hub",
|
||||
"created_bj": now.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"label": (label or "manual").strip() or "manual",
|
||||
"includes": [".env", "database"],
|
||||
"includes": [".env", "database", "hub_login"],
|
||||
"hub_username": u,
|
||||
}
|
||||
tmp_fd, tmp_path = tempfile.mkstemp(suffix=".tar.gz", prefix="hub_bak_")
|
||||
os.close(tmp_fd)
|
||||
env_snap = ""
|
||||
try:
|
||||
env_snap = _snapshot_env_with_login(env_path, u, str(p))
|
||||
with tarfile.open(tmp_path, "w:gz") as tar:
|
||||
manifest_path = tmp_path + ".manifest.json"
|
||||
with open(manifest_path, "w", encoding="utf-8") as f:
|
||||
@@ -126,8 +141,8 @@ def create_backup(*, root_dir: str, env_path: str, db_path: str, label: str = "m
|
||||
os.remove(manifest_path)
|
||||
except OSError:
|
||||
pass
|
||||
if env_path and os.path.isfile(env_path):
|
||||
tar.add(env_path, arcname=".env")
|
||||
if env_snap and os.path.isfile(env_snap):
|
||||
tar.add(env_snap, arcname=".env")
|
||||
if db_path and os.path.isfile(db_path):
|
||||
tar.add(db_path, arcname="database/" + os.path.basename(db_path))
|
||||
shutil.move(tmp_path, out_path)
|
||||
@@ -137,6 +152,12 @@ def create_backup(*, root_dir: str, env_path: str, db_path: str, label: str = "m
|
||||
except OSError:
|
||||
pass
|
||||
raise
|
||||
finally:
|
||||
if env_snap:
|
||||
try:
|
||||
os.remove(env_snap)
|
||||
except OSError:
|
||||
pass
|
||||
prune_backups()
|
||||
st = os.stat(out_path)
|
||||
return {
|
||||
@@ -149,6 +170,21 @@ def create_backup(*, root_dir: str, env_path: str, db_path: str, label: str = "m
|
||||
}
|
||||
|
||||
|
||||
def _snapshot_env_with_login(env_path: str, username: str, password: str) -> str:
|
||||
"""复制 .env 并写入当前中控登录账号,避免包内缺 HUB_USERNAME/PASSWORD."""
|
||||
fd, snap = tempfile.mkstemp(suffix=".env", prefix="hub_env_")
|
||||
os.close(fd)
|
||||
if env_path and os.path.isfile(env_path):
|
||||
shutil.copy2(env_path, snap)
|
||||
else:
|
||||
with open(snap, "w", encoding="utf-8") as f:
|
||||
f.write("")
|
||||
from lib.env_lib import apply_env_updates
|
||||
|
||||
apply_env_updates(snap, {"HUB_USERNAME": username, "HUB_PASSWORD": password})
|
||||
return snap
|
||||
|
||||
|
||||
def restore_backup(name: str, *, root_dir: str, env_path: str, db_path: str) -> dict[str, Any]:
|
||||
path = backup_path(name)
|
||||
if not path or not os.path.isfile(path):
|
||||
@@ -168,10 +204,23 @@ def restore_backup(name: str, *, root_dir: str, env_path: str, db_path: str) ->
|
||||
os.makedirs(os.path.dirname(os.path.abspath(db_path)) or ".", exist_ok=True)
|
||||
shutil.copy2(src, db_path)
|
||||
break
|
||||
restored_user = ""
|
||||
restored_password = ""
|
||||
if env_path and os.path.isfile(env_path):
|
||||
from lib.env_lib import env_get
|
||||
|
||||
restored_user = env_get(env_path, "HUB_USERNAME", "")
|
||||
restored_password = env_get(env_path, "HUB_PASSWORD", "")
|
||||
if restored_user:
|
||||
os.environ["HUB_USERNAME"] = restored_user
|
||||
if restored_password:
|
||||
os.environ["HUB_PASSWORD"] = restored_password
|
||||
return {
|
||||
"ok": True,
|
||||
"msg": "已恢复 .env / 数据库,建议重启中控进程使配置完全生效",
|
||||
"msg": "已恢复 .env / 数据库 / 登录账号,建议重启中控进程使配置完全生效",
|
||||
"name": os.path.basename(path),
|
||||
"hub_username": restored_user,
|
||||
"hub_password": restored_password,
|
||||
}
|
||||
|
||||
|
||||
@@ -237,6 +286,8 @@ def maybe_run_auto_backup(
|
||||
env_path: str,
|
||||
db_path: str,
|
||||
hour: int = 3,
|
||||
username: str | None = None,
|
||||
password: str | None = None,
|
||||
) -> dict[str, Any] | None:
|
||||
global _LAST_AUTO_DAY
|
||||
now = beijing_now()
|
||||
@@ -255,6 +306,8 @@ def maybe_run_auto_backup(
|
||||
env_path=env_path,
|
||||
db_path=db_path,
|
||||
label="auto",
|
||||
username=username,
|
||||
password=password,
|
||||
)
|
||||
_LAST_AUTO_DAY = day
|
||||
return result
|
||||
@@ -266,15 +319,24 @@ def start_backup_scheduler(
|
||||
env_path: str,
|
||||
db_path_fn: Callable[[], str],
|
||||
hour: int = 3,
|
||||
get_credentials: Callable[[], tuple[str, str]] | None = None,
|
||||
) -> None:
|
||||
def loop() -> None:
|
||||
while True:
|
||||
try:
|
||||
u, p = ("", "")
|
||||
if get_credentials:
|
||||
try:
|
||||
u, p = get_credentials()
|
||||
except Exception:
|
||||
u, p = "", ""
|
||||
maybe_run_auto_backup(
|
||||
root_dir=root_dir,
|
||||
env_path=env_path,
|
||||
db_path=db_path_fn(),
|
||||
hour=hour,
|
||||
username=u or None,
|
||||
password=p if p != "" else None,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
<div class="backup-block">
|
||||
<div class="backup-block-head">
|
||||
<h3>中控备份</h3>
|
||||
<p class="muted field-hint">目录 <code id="hub-backup-dir">/root/crypto_okx_hub_backups</code> · 含 <code>.env</code> 与 <code>hub.db</code>。换机时先上传家里下载的包,再点恢复。</p>
|
||||
<p class="muted field-hint">目录 <code id="hub-backup-dir">/root/crypto_okx_hub_backups</code> · 含 <code>.env</code>、<code>hub.db</code>、当前登录用户名和密码。换机时先上传家里下载的包,再点恢复。</p>
|
||||
<div class="settings-actions">
|
||||
<button type="button" class="settings-primary" id="hub-backup-create">立即备份</button>
|
||||
<button type="button" class="btn-secondary" id="hub-backup-refresh">刷新</button>
|
||||
@@ -188,7 +188,7 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='settings_backup.js') }}?v=3"></script>
|
||||
<script src="{{ url_for('static', filename='settings_backup.js') }}?v=4"></script>
|
||||
<script src="{{ url_for('static', filename='settings_update.js') }}?v=5"></script>
|
||||
<script>
|
||||
HubSettingsBackup.boot();
|
||||
|
||||
Reference in New Issue
Block a user