HuggingFace0920 commited on
Commit
97622a8
·
verified ·
1 Parent(s): 93d2585

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +69 -0
  2. README.md +13 -10
  3. install_reader.sh +51 -0
  4. sync_data.sh +160 -0
Dockerfile ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 使用官方Ubuntu基础镜像
2
+ FROM ubuntu:22.04
3
+
4
+ # 设置构建参数
5
+ ARG TZ=Asia/Shanghai
6
+ ARG APP_USER=reader
7
+ ARG APP_HOME=/app
8
+ ARG JAVA_VERSION=17
9
+
10
+ # 设置环境变量
11
+ ENV TZ=${TZ} \
12
+ JAVA_HOME=/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64 \
13
+ APP_USER=${APP_USER} \
14
+ APP_HOME=${APP_HOME} \
15
+ VIRTUAL_ENV=${APP_HOME}/venv \
16
+ PATH="${APP_HOME}/venv/bin:${JAVA_HOME}/bin:${PATH}" \
17
+ LANG=C.UTF-8 \
18
+ LC_ALL=C.UTF-8
19
+
20
+ # 安装依赖并配置环境
21
+ RUN set -eux; \
22
+ # 设置时区
23
+ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime; \
24
+ echo $TZ > /etc/timezone; \
25
+ \
26
+ # 更新包索引并安装基本依赖
27
+ apt-get update; \
28
+ apt-get install -y --no-install-recommends \
29
+ openjdk-${JAVA_VERSION}-jdk \
30
+ ca-certificates \
31
+ curl \
32
+ unzip \
33
+ bash \
34
+ jq \
35
+ python3 \
36
+ python3-pip \
37
+ python3-venv; \
38
+ \
39
+ # 清理APT缓存以减小镜像体积
40
+ apt-get clean; \
41
+ rm -rf /var/lib/apt/lists/*; \
42
+ \
43
+ # 更新CA证书
44
+ update-ca-certificates; \
45
+ \
46
+ # 创建应用用户和目录
47
+ useradd -r -u 1000 -m -d $APP_HOME -s /bin/bash $APP_USER; \
48
+ chown -R $APP_USER:$APP_USER $APP_HOME; \
49
+ \
50
+ # 创建Python虚拟环境并安装依赖
51
+ python3 -m venv $VIRTUAL_ENV; \
52
+ pip install --no-cache-dir --upgrade pip setuptools wheel; \
53
+ pip install --no-cache-dir huggingface_hub
54
+
55
+ # 设置工作目录和用户
56
+ WORKDIR $APP_HOME
57
+ USER $APP_USER
58
+
59
+ # 复制并设置脚本权限
60
+ COPY --chown=${APP_USER}:${APP_USER} install_reader.sh ./
61
+ COPY --chown=${APP_USER}:${APP_USER} sync_data.sh ./
62
+ RUN chmod +x install_reader.sh sync_data.sh
63
+
64
+ # 暴露服务端口
65
+ EXPOSE 8080
66
+
67
+ # 使用exec形式的ENTRYPOINT以确保信号正确传递
68
+ ENTRYPOINT ["/bin/bash", "-c"]
69
+ CMD ["./sync_data.sh"]
README.md CHANGED
@@ -1,10 +1,13 @@
1
- ---
2
- title: Reader
3
- emoji: 🐢
4
- colorFrom: indigo
5
- colorTo: gray
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
1
+ ---
2
+ title: Reader
3
+ emoji: 📖
4
+ short_description: Legado is a free and open source novel reader for Web
5
+ license: gpl-3.0
6
+ sdk: docker
7
+ colorFrom: blue
8
+ colorTo: blue
9
+ pinned: false
10
+ app_port: 8080
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
install_reader.sh ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ # 获取最新版本的重定向URL
6
+ echo "正在获取最新版本信息..."
7
+ redirect_url=$(curl -Ls -o /dev/null -w '%{url_effective}' 'https://github.com/hectorqin/reader/releases/latest')
8
+
9
+ # 提取版本标签
10
+ tag=$(basename "$redirect_url")
11
+ if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
12
+ echo "错误:无效的版本标签 '$tag'"
13
+ exit 1
14
+ fi
15
+ version="${tag#v}"
16
+ echo "检测到最新版本: $version"
17
+
18
+ # 构造下载链接
19
+ download_url="https://github.com/hectorqin/reader/releases/download/${tag}/reader-server-${version}.zip"
20
+ echo "开始下载: $download_url"
21
+
22
+ # 下载文件
23
+ if ! curl -LO "$download_url"; then
24
+ echo "错误:文件下载失败"
25
+ exit 1
26
+ fi
27
+
28
+ # 解压文件
29
+ zip_file="reader-server-${version}.zip"
30
+ echo "正在解压文件..."
31
+ unzip "$zip_file"
32
+
33
+ cd target
34
+ chmod +x reader-pro-${version}.jar
35
+
36
+ cd ../bin
37
+ # 执行启动脚本
38
+ if [ -f "./startup.sh" ]; then
39
+ echo "正在启动服务..."
40
+ chmod +x "./startup.sh"
41
+
42
+ # 添加自定义系统属性
43
+ export JAVA_OPT_EXT="--reader.app.defaultUserEnableBookSource=false --reader.app.minUserPasswordLength=6"
44
+
45
+ ./startup.sh -m multi -i "${INVITE_CODE:-}" -k "${SECURE_KEY:-}"
46
+ echo "服务已启动!"
47
+ tail -f /app/logs/start.out
48
+ else
49
+ echo "错误:启动脚本不存在"
50
+ exit 1
51
+ fi
sync_data.sh ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+
3
+ # 检查必须的环境变量是否已设置
4
+ if [ -z "$HF_TOKEN" ] || [ -z "$DATASET_ID" ]; then
5
+ echo "HF_TOKEN 和 DATASET_ID 环境变量未设置"
6
+ exit 1
7
+ fi
8
+
9
+ # 激活 Python 虚拟环境
10
+ . /app/venv/bin/activate
11
+
12
+ # 生成 HuggingFace 同步脚本(Python 实现核心同步逻辑)
13
+ cat > hf_sync.py << 'EOL'
14
+ from huggingface_hub import HfApi
15
+ import sys
16
+ import os
17
+ import tarfile
18
+ import tempfile
19
+
20
+ # 管理备份文件,超过最大数量则自动删除最旧的备份
21
+ def manage_backups(api, repo_id, max_files=50):
22
+ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset")
23
+ backup_files = [f for f in files if f.startswith('backup_') and f.endswith('.tar.gz')]
24
+ backup_files.sort() # 从旧到新
25
+ if len(backup_files) >= max_files:
26
+ files_to_delete = backup_files[:(len(backup_files) - max_files + 1)]
27
+ for file_to_delete in files_to_delete:
28
+ try:
29
+ api.delete_file(path_in_repo=file_to_delete, repo_id=repo_id, repo_type="dataset")
30
+ print(f'已删除旧备份: {file_to_delete}')
31
+ except Exception as e:
32
+ print(f'删除 {file_to_delete} 时出错: {str(e)}')
33
+
34
+ # 上传备份文件到 HuggingFace
35
+ def upload_backup(file_path, file_name, token, repo_id):
36
+ api = HfApi(token=token)
37
+ try:
38
+ api.upload_file(
39
+ path_or_fileobj=file_path,
40
+ path_in_repo=file_name,
41
+ repo_id=repo_id,
42
+ repo_type="dataset"
43
+ )
44
+ print(f"成功上传备份: {file_name}")
45
+ manage_backups(api, repo_id)
46
+ except Exception as e:
47
+ print(f"上传文件出错: {str(e)}")
48
+
49
+ # 下载最新的备份并解压到指定目录
50
+ def download_latest_backup(token, repo_id, extract_path):
51
+ try:
52
+ api = HfApi(token=token)
53
+ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset")
54
+ backup_files = [f for f in files if f.startswith('backup_') and f.endswith('.tar.gz')]
55
+ if not backup_files:
56
+ print("未找到任何备份文件")
57
+ return
58
+ latest_backup = sorted(backup_files)[-1]
59
+ with tempfile.TemporaryDirectory() as temp_dir:
60
+ filepath = api.hf_hub_download(
61
+ repo_id=repo_id,
62
+ filename=latest_backup,
63
+ repo_type="dataset",
64
+ local_dir=temp_dir
65
+ )
66
+ if filepath and os.path.exists(filepath):
67
+ with tarfile.open(filepath, 'r:gz') as tar:
68
+ tar.extractall(extract_path)
69
+ print(f"成功恢复备份: {latest_backup}")
70
+ except Exception as e:
71
+ print(f"下载备份出错: {str(e)}")
72
+
73
+ # 清理所有 LFS(大文件存储)备份文件,只保留最新的 keep_latest 个
74
+ def clear_lfs_files(token, repo_id, keep_latest=3):
75
+ try:
76
+ api = HfApi(token=token)
77
+ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset")
78
+ lfs_files = [f for f in files if f.endswith('.tar.gz')]
79
+ lfs_files.sort() # 从旧到新
80
+ files_to_delete = lfs_files[:-keep_latest] if len(lfs_files) > keep_latest else []
81
+ if not files_to_delete:
82
+ print("没有需要删除的旧 LFS 文件")
83
+ return
84
+ for lfs_file in files_to_delete:
85
+ try:
86
+ api.delete_file(path_in_repo=lfs_file, repo_id=repo_id, repo_type="dataset")
87
+ print(f"已删除旧 LFS 文件: {lfs_file}")
88
+ except Exception as e:
89
+ print(f"删除 LFS 文件出错: {str(e)}")
90
+ except Exception as e:
91
+ print(f"清理 LFS 文件出错: {str(e)}")
92
+
93
+ # 合并所有历史提交为一个(Super Squash),可选保留最新的几个提交
94
+ def super_squash_history(token, repo_id, keep_latest=1):
95
+ try:
96
+ api = HfApi(token=token)
97
+ api.super_squash_history(repo_id=repo_id, repo_type="dataset", keep_last_commits=keep_latest)
98
+ print(f"成功执行 super_squash_history,将历史合并,仅保留最新的 {keep_latest} 个提交。")
99
+ except Exception as e:
100
+ print(f"super_squash_history 执行出错: {str(e)}")
101
+
102
+ # 主函数,根据命令行参数执行不同操作
103
+ if __name__ == "__main__":
104
+ if len(sys.argv) < 2:
105
+ print("用法: python hf_sync.py <操作> [参数]")
106
+ sys.exit(1)
107
+ action = sys.argv[1]
108
+ token = os.getenv('HF_TOKEN')
109
+ repo_id = os.getenv('DATASET_ID')
110
+ if action == "upload":
111
+ if len(sys.argv) < 4:
112
+ print("用法: python hf_sync.py upload <file_path> <file_name>")
113
+ sys.exit(1)
114
+ file_path = sys.argv[2]
115
+ file_name = sys.argv[3]
116
+ upload_backup(file_path, file_name, token, repo_id)
117
+ elif action == "download":
118
+ extract_path = sys.argv[2] if len(sys.argv) > 2 else '.'
119
+ download_latest_backup(token, repo_id, extract_path)
120
+ elif action == "clear_lfs":
121
+ keep_latest = int(sys.argv[2]) if len(sys.argv) > 2 else 3
122
+ clear_lfs_files(token, repo_id, keep_latest)
123
+ elif action == "super_squash":
124
+ keep_latest = int(sys.argv[2]) if len(sys.argv) > 2 else 1
125
+ super_squash_history(token, repo_id, keep_latest)
126
+ else:
127
+ print("无效操作。可用操作: upload, download, clear_lfs, super_squash")
128
+ sys.exit(1)
129
+ EOL
130
+
131
+ # 首次启动时自动下载最新备份
132
+ echo "正在从 HuggingFace 下载最新备份..."
133
+ python hf_sync.py download "./"
134
+
135
+ # 定时同步函数
136
+ sync_data() {
137
+ while true; do
138
+ echo "开始同步流程,当前时间: $(date)"
139
+ STORAGE_PATH="./storage"
140
+ if [ -d "${STORAGE_PATH}" ]; then
141
+ BACKUP_FILE="backup_$(date +%Y%m%d_%H%M%S).tar.gz"
142
+ tar -czf "${BACKUP_FILE}" -C "${STORAGE_PATH}" .
143
+ echo "已创建备份: ${BACKUP_FILE}"
144
+ python hf_sync.py upload "${BACKUP_FILE}" "${BACKUP_FILE}"
145
+ rm -f "${BACKUP_FILE}"
146
+ python hf_sync.py clear_lfs 3
147
+ else
148
+ echo "数据目录不存在: ${STORAGE_PATH}"
149
+ fi
150
+ SYNC_INTERVAL=${SYNC_INTERVAL:-7200}
151
+ echo "等待 ${SYNC_INTERVAL} 秒后进行下一次同步..."
152
+ sleep $SYNC_INTERVAL
153
+ done
154
+ }
155
+
156
+ # 后台启动同步进程
157
+ sync_data &
158
+
159
+ # 启动主应用(请根据实际路径调整)
160
+ exec bash install_reader.sh