File size: 6,155 Bytes
5fd4643 e35a36c 7e3c809 6523d00 5fd4643 e35a36c cfafa7b ed69bdb 1df45a1 ed69bdb ee37362 ed69bdb 1aa22df ed69bdb 1df45a1 ed69bdb ee37362 1df45a1 ee37362 1df45a1 ee37362 cfafa7b ed69bdb 1df45a1 ed69bdb cfafa7b ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb cfafa7b ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb 1df45a1 ed69bdb cfafa7b ed69bdb 7e3c809 1df45a1 cfafa7b 1df45a1 ed69bdb cfafa7b 9642b8f ed69bdb 7e3c809 ed69bdb 1df45a1 ed69bdb 7e3c809 1aa22df ed69bdb e5f80c5 1aa22df | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | # 使用 lxmusic 官方镜像(Alpine 基础)
FROM xcq0607/lxserver:latest
# 安装必要工具
RUN apk add --no-cache python3 py3-pip curl socat tar && \
pip3 install --no-cache-dir --break-system-packages requests huggingface_hub && \
rm -rf /var/cache/apk/*
# ========= 备份恢复脚本 sync.py(修复 API 调用错误) =========
RUN cat > /usr/local/bin/sync.py << 'SYNC_EOF'
#!/usr/bin/env python3
import os, sys, tarfile, tempfile, time
from datetime import datetime, timedelta
from huggingface_hub import HfApi
from requests.exceptions import HTTPError
LX_DATA_BASE = "/server"
BACKUP_DIRS = ["data", "logs", "cache"]
CONFIG_FILES = ["config.json"]
BACKUP_PREFIX = "lxmusic_backup_"
KEEP_DAYS = 7
RETRY_COUNT = 3
RETRY_DELAY = 5
def log(msg): print(f"[SYNC] {msg}")
def get_env(var): return os.getenv(var, "").strip()
def validate_hf_access(repo_id, token):
api = HfApi()
try:
api.repo_info(repo_id=repo_id, repo_type="dataset", token=token)
except HTTPError as e:
if e.response and e.response.status_code == 404:
log(f"仓库 {repo_id} 不存在,尝试创建...")
api.create_repo(repo_id=repo_id, repo_type="dataset", token=token, private=True)
log("仓库创建成功")
else:
raise PermissionError(f"访问仓库失败: {e}")
# 测试写入权限(使用关键字参数)
test_file = tempfile.NamedTemporaryFile(mode="w", delete=False)
test_file.write("test")
test_file.close()
try:
api.upload_file(
path_or_fileobj=test_file.name,
path_in_repo=".write_test",
repo_id=repo_id,
repo_type="dataset",
token=token
)
api.delete_file(path_in_repo=".write_test", repo_id=repo_id, repo_type="dataset", token=token)
log("写入权限验证通过")
except Exception as e:
raise PermissionError(f"写入权限不足: {e}")
finally:
os.unlink(test_file.name)
def restore():
repo_id = get_env("HF_DATASET")
token = get_env("HF_TOKEN")
if not repo_id or not token:
log("未设置 HF_DATASET 或 HF_TOKEN,跳过恢复")
return False
try:
validate_hf_access(repo_id, token)
except Exception as e:
log(f"访问验证失败: {e}")
return False
api = HfApi()
try:
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
except Exception as e:
log(f"列出文件失败: {e}")
return False
candidates = []
for i in range(KEEP_DAYS):
day = (datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d")
name = f"{BACKUP_PREFIX}{day}.tar.gz"
if name in files:
candidates.append(name)
if not candidates:
log("未找到最近备份")
return False
latest = sorted(candidates, reverse=True)[0]
log(f"找到备份: {latest},开始下载")
for attempt in range(RETRY_COUNT):
try:
local = api.hf_hub_download(
repo_id=repo_id,
filename=latest,
repo_type="dataset",
token=token,
resume=True
)
break
except Exception as e:
log(f"下载失败 ({attempt+1}/{RETRY_COUNT}): {e}")
time.sleep(RETRY_DELAY)
else:
return False
try:
with tarfile.open(local, "r:gz") as tar:
tar.extractall(path=LX_DATA_BASE)
log("备份恢复成功")
return True
except Exception as e:
log(f"解压失败: {e}")
return False
def backup():
repo_id = get_env("HF_DATASET")
token = get_env("HF_TOKEN")
if not repo_id or not token:
log("未设置备份环境变量,跳过备份")
return
try:
validate_hf_access(repo_id, token)
except Exception as e:
log(f"备份验证失败: {e}")
return
day = datetime.now().strftime("%Y-%m-%d")
backup_name = f"{BACKUP_PREFIX}{day}.tar.gz"
with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete=False) as tmp:
tmp.close()
with tarfile.open(tmp.name, "w:gz") as tar:
for d in BACKUP_DIRS:
src = f"{LX_DATA_BASE}/{d}"
if os.path.exists(src):
tar.add(src, arcname=d)
for cfg in CONFIG_FILES:
src = f"{LX_DATA_BASE}/{cfg}"
if os.path.exists(src):
tar.add(src, arcname=cfg)
api = HfApi()
api.upload_file(
path_or_fileobj=tmp.name,
path_in_repo=backup_name,
repo_id=repo_id,
repo_type="dataset",
token=token
)
log(f"备份 {backup_name} 上传成功")
# 清理旧备份
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
cutoff = datetime.now() - timedelta(days=KEEP_DAYS)
for f in files:
if f.startswith(BACKUP_PREFIX) and f.endswith(".tar.gz"):
date_str = f[len(BACKUP_PREFIX):-7]
try:
if datetime.strptime(date_str, "%Y-%m-%d") < cutoff:
api.delete_file(path_in_repo=f, repo_id=repo_id, repo_type="dataset", token=token)
log(f"删除旧备份: {f}")
except: pass
os.unlink(tmp.name)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "backup":
backup()
else:
restore()
SYNC_EOF
RUN chmod +x /usr/local/bin/sync.py
# ========= 启动脚本 =========
RUN printf '#!/bin/sh\n\
set -e\n\
\n\
echo "===== LXMusic Server with Backup/Restore ====="\n\
\n\
mkdir -p /server/data /server/logs /server/cache\n\
\n\
python3 /usr/local/bin/sync.py restore\n\
\n\
(\n\
while true; do\n\
sleep 1800\n\
echo "--- 定时备份 ---"\n\
python3 /usr/local/bin/sync.py backup\n\
done\n\
) &\n\
echo "Background backup started (every 30 minutes)"\n\
\n\
cd /server\n\
exec node index.js\n\
' > /start.sh && chmod +x /start.sh
EXPOSE 9527
CMD ["/start.sh"] |